Mit gleichen Spaltenbreiten bestimmte der längste Legendentext die Breite aller Spalten; die dreispaltige Basemap-Legende wurde 431 mm breit. Jetzt: ungleich breite Spalten, automatischer Textumbruch nach 60 mm (TEXTUMBRUCH_MM in print_kartenrand, auch für die Basemap-Legende) und zweizeiliger Titel „Kartengrundlage / (basemap.de)“, der sonst die Mindestbreite bestimmte. Im QGIS-Test: dreispaltig 226 statt 431 mm, einspaltig 89 statt 163 mm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
470 lines
19 KiB
Python
470 lines
19 KiB
Python
"""
|
||
sn_basis/modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout ausgeben
|
||
|
||
Baut aus den gewählten Legendeneinträgen (``basemap_legende``) die Legendensymbole aus den
|
||
Renderer-Styles des Projektlayers, legt dafür leere Hilfslayer an und schreibt sie in ein
|
||
eigenes Legendenelement „Legende Basemap“ unterhalb der Hauptlegende.
|
||
|
||
Warum ein eigenes Legendenelement: Die Hauptlegende filtert nach Karteninhalt
|
||
(``setLegendFilterByMapEnabled``) und würde die Hilfslayer ausblenden, weil sie nicht in der
|
||
Karte liegen (Spike 02.10.2026, QGIS 3.44). Das Basemap-Element ist ohne Kartenfilter und ohne
|
||
automatische Aktualisierung; die Hilfslayer stehen nicht im Layerbaum, bleiben aber mit dem
|
||
Projekt gespeichert. Leere Speicherlayer lösen beim Schließen des Projekts keine Rückfrage aus.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import re
|
||
from typing import Any
|
||
|
||
from sn_basis.functions.qgiscore_wrapper import (
|
||
LEGEND_STYLE_HIDDEN,
|
||
LEGEND_STYLE_SUBGROUP,
|
||
PROPERTY_TYPE_EXPRESSION,
|
||
QgsCategorizedSymbolRenderer,
|
||
QgsExpressionContext,
|
||
QgsExpressionContextScope,
|
||
QgsLayoutItem,
|
||
QgsLayoutItemLegend,
|
||
QgsLayoutItemMap,
|
||
QgsLayoutMeasurement,
|
||
QgsLayoutPoint,
|
||
QgsLayoutSize,
|
||
QgsLayoutUtils,
|
||
QgsLegendRenderer,
|
||
QgsProperty,
|
||
QgsRendererCategory,
|
||
QgsSimpleLineSymbolLayer,
|
||
QgsSymbolLayerUtils,
|
||
QgsUnitTypes,
|
||
QgsVectorLayer,
|
||
)
|
||
from sn_basis.functions.qt_wrapper import QSize
|
||
from sn_basis.modules.print_kartenrand import TEXTUMBRUCH_MM
|
||
|
||
MM = QgsUnitTypes.LayoutMillimeters
|
||
|
||
LEGENDE_ID = "Legende Basemap"
|
||
# Zweizeilig, damit der Titel nicht die Mindestbreite der Legende bestimmt (Basis #96)
|
||
LEGENDE_TITEL = "Kartengrundlage\n(basemap.de)"
|
||
HAUPTKARTE_ID = "Hauptkarte"
|
||
HAUPTLEGENDE_ID = "Legende"
|
||
QUELLENANGABE_ID = "Quellenangabe"
|
||
ABSTAND_MM = 4.0
|
||
# Mindestplatz, der über der Basemap-Legende für die Hauptlegende frei bleiben soll.
|
||
PLATZ_HAUPTLEGENDE_MM = 40.0
|
||
RAHMENSTAERKE_MM = 0.5
|
||
|
||
# Layout-Eigenschaften
|
||
LAYOUT_KENNZEICHEN = "sn_basemap_legende"
|
||
LAYOUT_BEREICH = "sn_basemap_bereich"
|
||
LAYOUT_MASSSTAB = "sn_basemap_massstab"
|
||
LAYOUT_EINTRAEGE = "sn_basemap_eintraege"
|
||
LAYOUT_HILFSLAYER = "sn_basemap_hilfslayer"
|
||
# Eigenschaft der Hilfslayer (Wert: Layoutname)
|
||
HILFSLAYER_KENNUNG = "sn_basemap_legende"
|
||
|
||
_GEOMETRIE_JE_SYMBOLTYP = {0: "Point", 1: "LineString", 2: "Polygon"}
|
||
|
||
# Legendenrand für weiße Flächen ohne Kontur (Basis #92)
|
||
LEGENDENRAND_BREITE_MM = 0.15
|
||
LEGENDENRAND_FARBE = "150,150,150,255"
|
||
# Ab diesem Wert je Farbkanal gilt eine Farbe auf weißem Legendenhintergrund als unsichtbar.
|
||
HELL_AB = 240
|
||
DURCHSICHTIG_BIS = 25
|
||
_ZOOM_VARIABLEN = {"vector_tile_zoom", "zoom_level"}
|
||
_VARIABLE = re.compile(r"@([A-Za-z_][A-Za-z0-9_]*)")
|
||
|
||
|
||
def _enum_int(wert: Any) -> int:
|
||
try:
|
||
return int(wert)
|
||
except (TypeError, ValueError):
|
||
return int(getattr(wert, "value", -1))
|
||
|
||
|
||
def _aufruf(objekt: Any, methode: str, *args: Any, standard: Any = None) -> Any:
|
||
funktion = getattr(objekt, methode, None)
|
||
if not callable(funktion):
|
||
return standard
|
||
try:
|
||
return funktion(*args)
|
||
except Exception:
|
||
return standard
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# Layout-Kennzeichen
|
||
# ---------------------------------------------------------
|
||
|
||
def markiere_layout(layout: Any) -> None:
|
||
"""Kennzeichnet ein Layout als Nutzer der Basemap-Legende."""
|
||
layout.setCustomProperty(LAYOUT_KENNZEICHEN, 1)
|
||
|
||
|
||
def nutzt_basemap_legende(layout: Any) -> bool:
|
||
return str(_aufruf(layout, "customProperty", LAYOUT_KENNZEICHEN, 0, standard=0)).lower() in ("1", "true")
|
||
|
||
|
||
def layouts_mit_basemap_legende(project: Any) -> list[str]:
|
||
"""Namen der Drucklayouts des Projekts, die die Basemap-Legende nutzen."""
|
||
verwaltung = _aufruf(project, "layoutManager")
|
||
layouts = _aufruf(verwaltung, "printLayouts", standard=[]) or []
|
||
return [l.name() for l in layouts if nutzt_basemap_legende(l)]
|
||
|
||
|
||
def hauptkarte(layout: Any) -> Any | None:
|
||
"""Die Karte, auf die sich die Legende bezieht (ID „Hauptkarte“, sonst die erste Karte)."""
|
||
karte = _aufruf(layout, "itemById", HAUPTKARTE_ID)
|
||
if karte is not None:
|
||
return karte
|
||
for element in _aufruf(layout, "items", standard=[]) or []:
|
||
if isinstance(element, QgsLayoutItemMap):
|
||
return element
|
||
return None
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# Symbole
|
||
# ---------------------------------------------------------
|
||
|
||
def friere_zoom_ein(symbol: Any, zoom_level: int, vector_tile_zoom: float) -> None:
|
||
"""Schreibt zoomabhängige Werte (Strichbreite, Deckkraft …) für die Druck-Zoomstufe fest.
|
||
|
||
Die Basemap-Styles nutzen ``@vector_tile_zoom``; diese Variable gibt es in der Legende nicht.
|
||
Ohne Festschreiben bleiben z. B. gestrichelte Linien in der Legende unsichtbar, weil ihr
|
||
Strichmuster zoomabhängig berechnet wird. ``@symbol_color`` wird mit der Farbe der jeweiligen
|
||
Symbolebene belegt; lokale Variablen wie ``@element`` (``array_foreach``) wertet QGIS selbst aus.
|
||
"""
|
||
for symbol_layer in symbol.symbolLayers():
|
||
kontext = QgsExpressionContext()
|
||
scope = QgsExpressionContextScope()
|
||
scope.setVariable("vector_tile_zoom", vector_tile_zoom)
|
||
scope.setVariable("zoom_level", zoom_level)
|
||
farbe = _aufruf(symbol_layer, "color")
|
||
if farbe is not None:
|
||
scope.setVariable("symbol_color", farbe)
|
||
kontext.appendScope(scope)
|
||
eigenschaften = symbol_layer.dataDefinedProperties()
|
||
for schluessel in eigenschaften.propertyKeys():
|
||
eigenschaft = eigenschaften.property(schluessel)
|
||
if eigenschaft.propertyType() != PROPERTY_TYPE_EXPRESSION:
|
||
continue
|
||
if not set(_VARIABLE.findall(eigenschaft.expressionString())) & _ZOOM_VARIABLEN:
|
||
continue
|
||
ergebnis = eigenschaft.value(kontext, None)
|
||
wert, ok = ergebnis if isinstance(ergebnis, tuple) else (ergebnis, ergebnis is not None)
|
||
if ok and wert is not None:
|
||
eigenschaften.setProperty(schluessel, QgsProperty.fromValue(wert))
|
||
unter = symbol_layer.subSymbol()
|
||
if unter is not None:
|
||
friere_zoom_ein(unter, zoom_level, vector_tile_zoom)
|
||
|
||
|
||
def waehle_symbolnamen(eintrag: Any, aktive: dict[str, int]) -> list[str]:
|
||
"""Style-Namen für das Legendensymbol, in Zeichenreihenfolge des Renderers.
|
||
|
||
Bevorzugt die ``symbol_stile`` des Katalogs, soweit sie bei der Zoomstufe aktiv sind; sonst
|
||
den ersten tatsächlich getroffenen Style.
|
||
"""
|
||
namen = [n for n in eintrag.symbol_stile if n in aktive]
|
||
if not namen:
|
||
namen = [n for n in eintrag.treffer if n in aktive][:1]
|
||
return sorted(dict.fromkeys(namen), key=lambda n: aktive[n])
|
||
|
||
|
||
def baue_symbole(layer: Any, eintraege: list, zoom_level: int, vector_tile_zoom: float) -> dict[str, Any]:
|
||
"""Setzt je Legendeneintrag ein Symbol aus den Renderer-Styles des Projektlayers zusammen."""
|
||
stile = layer.renderer().styles()
|
||
aktive = {s.styleName(): i for i, s in enumerate(stile) if s.isActive(zoom_level) and s.symbol() is not None}
|
||
je_name = {s.styleName(): s for s in stile}
|
||
symbole: dict[str, Any] = {}
|
||
for eintrag in eintraege:
|
||
basis = None
|
||
for name in waehle_symbolnamen(eintrag, aktive):
|
||
symbol = je_name[name].symbol().clone()
|
||
if basis is None:
|
||
basis = symbol
|
||
continue
|
||
for symbol_layer in symbol.symbolLayers():
|
||
# Unverträgliche Ebenen (z. B. Punktsymbol in einer Füllung) lehnt QGIS ab.
|
||
basis.appendSymbolLayer(symbol_layer.clone())
|
||
if basis is not None:
|
||
friere_zoom_ein(basis, zoom_level, vector_tile_zoom)
|
||
ergaenze_legendenrand(basis)
|
||
symbole[eintrag.schluessel] = basis
|
||
return symbole
|
||
|
||
|
||
def _ist_unsichtbar(farbe: tuple) -> bool:
|
||
"""Ob eine RGBA-Farbe auf weißem Legendenhintergrund praktisch nicht zu sehen ist."""
|
||
rot, gruen, blau, alpha = farbe
|
||
return alpha <= DURCHSICHTIG_BIS or min(rot, gruen, blau) >= HELL_AB
|
||
|
||
|
||
def braucht_legendenrand(ebenen: list[dict]) -> bool:
|
||
"""Ob ein Flächensymbol in der Legende einen Rand braucht.
|
||
|
||
``ebenen`` beschreibt die Symbolebenen: ``art`` (``fuellung``, ``linie``, ``sonst``), ``farbe``
|
||
(RGBA) und bei Füllungen ``rand`` (RGBA oder ``None`` ohne Kontur). Ein Rand ist nötig, wenn
|
||
alle Ebenen auf weißem Hintergrund unsichtbar sind – z. B. die weiße Fläche „Platz“.
|
||
"""
|
||
if not ebenen:
|
||
return False
|
||
for ebene in ebenen:
|
||
if ebene.get("art") not in ("fuellung", "linie") or not _ist_unsichtbar(ebene["farbe"]):
|
||
return False
|
||
if ebene.get("rand") is not None and not _ist_unsichtbar(ebene["rand"]):
|
||
return False
|
||
return True
|
||
|
||
|
||
def _rgba(farbe: Any) -> tuple:
|
||
return farbe.red(), farbe.green(), farbe.blue(), farbe.alpha()
|
||
|
||
|
||
def _ebenen_beschreiben(symbol: Any) -> list[dict]:
|
||
ebenen = []
|
||
for ebene in symbol.symbolLayers():
|
||
art = ebene.layerType()
|
||
if art == "SimpleFill":
|
||
ohne_kontur = _enum_int(ebene.strokeStyle()) == 0 # Qt.NoPen
|
||
ebenen.append({"art": "fuellung", "farbe": _rgba(ebene.color()),
|
||
"rand": None if ohne_kontur else _rgba(ebene.strokeColor())})
|
||
elif art == "SimpleLine":
|
||
ebenen.append({"art": "linie", "farbe": _rgba(ebene.color())})
|
||
else:
|
||
ebenen.append({"art": "sonst"})
|
||
return ebenen
|
||
|
||
|
||
def ergaenze_legendenrand(symbol: Any) -> bool:
|
||
"""Gibt einem praktisch weißen Flächensymbol ohne Kontur einen dünnen grauen Rand (nur Legende).
|
||
|
||
Returns
|
||
-------
|
||
bool
|
||
Ob ein Rand ergänzt wurde.
|
||
"""
|
||
if _enum_int(symbol.type()) != 2 or not braucht_legendenrand(_ebenen_beschreiben(symbol)):
|
||
return False
|
||
symbol.appendSymbolLayer(QgsSimpleLineSymbolLayer.create({
|
||
"line_color": LEGENDENRAND_FARBE,
|
||
"line_width": str(LEGENDENRAND_BREITE_MM),
|
||
"line_width_unit": "MM",
|
||
}))
|
||
return True
|
||
|
||
|
||
def vorschau_icon(symbol: Any) -> Any:
|
||
if symbol is None:
|
||
return None
|
||
return _aufruf(QgsSymbolLayerUtils, "symbolPreviewIcon", symbol, QSize(16, 16))
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# Hilfslayer
|
||
# ---------------------------------------------------------
|
||
|
||
def entferne_hilfslayer(project: Any, layout: Any) -> None:
|
||
"""Entfernt die Hilfslayer einer früheren Basemap-Legende dieses Layouts aus dem Projekt."""
|
||
roh = str(_aufruf(layout, "customProperty", LAYOUT_HILFSLAYER, "", standard="") or "")
|
||
for layer_id in [i for i in roh.split(",") if i]:
|
||
if _aufruf(project, "mapLayer", layer_id) is not None:
|
||
project.removeMapLayer(layer_id)
|
||
|
||
|
||
def erzeuge_hilfslayer(project: Any, layout_name: str, crs_authid: str, eintraege: list,
|
||
symbole: dict[str, Any]) -> list[tuple[str, list[Any]]]:
|
||
"""Legt je Gruppe und Geometrietyp einen leeren, kategorisierten Speicherlayer an.
|
||
|
||
Returns
|
||
-------
|
||
list[tuple[str, list]]
|
||
Gruppen in Legendenreihenfolge mit ihren Hilfslayern.
|
||
"""
|
||
gruppen: dict[str, dict[int, list]] = {}
|
||
for eintrag in eintraege:
|
||
symbol = symbole.get(eintrag.schluessel)
|
||
if symbol is None:
|
||
continue
|
||
typ = _enum_int(symbol.type())
|
||
if typ not in _GEOMETRIE_JE_SYMBOLTYP:
|
||
continue
|
||
gruppen.setdefault(eintrag.gruppe, {}).setdefault(typ, []).append(
|
||
QgsRendererCategory(eintrag.schluessel, symbol, eintrag.bezeichnung))
|
||
|
||
ergebnis: list[tuple[str, list[Any]]] = []
|
||
for gruppe, je_typ in gruppen.items():
|
||
layer_liste = []
|
||
for typ in sorted(je_typ, reverse=True): # Flächen, Linien, Punkte
|
||
layer = QgsVectorLayer(f"{_GEOMETRIE_JE_SYMBOLTYP[typ]}?crs={crs_authid}&field=eintrag:string",
|
||
f"Basemap-Legende {layout_name}: {gruppe}", "memory")
|
||
layer.setRenderer(QgsCategorizedSymbolRenderer("eintrag", je_typ[typ]))
|
||
layer.setCustomProperty("skipMemoryLayersCheck", 1)
|
||
layer.setCustomProperty(HILFSLAYER_KENNUNG, layout_name)
|
||
project.addMapLayer(layer, False)
|
||
layer_liste.append(layer)
|
||
ergebnis.append((gruppe, layer_liste))
|
||
return ergebnis
|
||
|
||
|
||
# ---------------------------------------------------------
|
||
# Legendenelement
|
||
# ---------------------------------------------------------
|
||
|
||
def _rechteck_mm(element: Any) -> tuple[float, float, float, float] | None:
|
||
"""Links, oben, Breite, Höhe eines Layoutelements in mm – unabhängig von seinem Bezugspunkt."""
|
||
rechteck = _aufruf(element, "sceneBoundingRect")
|
||
if rechteck is not None:
|
||
return rechteck.x(), rechteck.y(), rechteck.width(), rechteck.height()
|
||
position = _aufruf(element, "positionWithUnits")
|
||
groesse = _aufruf(element, "sizeWithUnits")
|
||
if position is None or groesse is None:
|
||
return None
|
||
return position.x(), position.y(), groesse.width(), groesse.height()
|
||
|
||
|
||
def _neues_legendenelement(layout: Any) -> Any:
|
||
legende = QgsLayoutItemLegend(layout)
|
||
legende.setId(LEGENDE_ID)
|
||
_aufruf(legende, "setObjectName", LEGENDE_ID)
|
||
_aufruf(legende, "setFrameEnabled", True)
|
||
_aufruf(legende, "setBackgroundEnabled", False)
|
||
_aufruf(legende, "setFrameStrokeWidth", _layout_messung(RAHMENSTAERKE_MM))
|
||
layout.addLayoutItem(legende)
|
||
return legende
|
||
|
||
|
||
def _layout_messung(wert_mm: float) -> Any:
|
||
return QgsLayoutMeasurement(wert_mm, MM)
|
||
|
||
|
||
def mindestgroesse(legende: Any, layout: Any, spalten: int) -> tuple[float, float] | None:
|
||
"""Platzbedarf der Legende in mm bei ``spalten`` Spalten, ohne sie zu zeichnen.
|
||
|
||
``adjustBoxSize`` wirkt in QGIS erst, nachdem das Element einmal gezeichnet wurde; für die
|
||
Platzierung beim Anlegen wird der Bedarf deshalb über ``QgsLegendRenderer`` berechnet.
|
||
"""
|
||
einstellungen = _aufruf(legende, "legendSettings")
|
||
kontext = _aufruf(QgsLayoutUtils, "createRenderContextForLayout", layout, None)
|
||
if einstellungen is None or kontext is None:
|
||
return None
|
||
einstellungen.setColumnCount(spalten)
|
||
einstellungen.setSplitLayer(True)
|
||
einstellungen.setEqualColumnWidth(False)
|
||
einstellungen.setAutoWrapLinesAfter(TEXTUMBRUCH_MM)
|
||
try:
|
||
groesse = QgsLegendRenderer(legende.model(), einstellungen).minimumSize(kontext)
|
||
except Exception:
|
||
return None
|
||
return groesse.width(), groesse.height()
|
||
|
||
|
||
def platziere(layout: Any, legende: Any) -> str:
|
||
"""Setzt die Basemap-Legende direkt über die Quellenangabe, in die Spalte der Hauptlegende.
|
||
|
||
Die Legende ist unten verankert und wächst nach oben. Unter der Hauptlegende zu verankern geht
|
||
nicht: deren Höhe steht erst nach dem Rendern fest, weil sie nach Karteninhalt filtert. Bis zur
|
||
Oberkante der Hauptlegende bleibt ``PLATZ_HAUPTLEGENDE_MM`` frei; reicht der Platz nicht, wird
|
||
die Legende zwei- oder dreispaltig, solange sie nicht über den Seitenrand hinausragt.
|
||
|
||
Returns
|
||
-------
|
||
str
|
||
Hinweis, wenn der Platz auch dreispaltig nicht reicht, sonst leer.
|
||
"""
|
||
haupt = _rechteck_mm(_aufruf(layout, "itemById", HAUPTLEGENDE_ID))
|
||
karte = _rechteck_mm(hauptkarte(layout))
|
||
quelle = _rechteck_mm(_aufruf(layout, "itemById", QUELLENANGABE_ID))
|
||
if haupt is not None:
|
||
x, oben = haupt[0], haupt[1]
|
||
elif karte is not None:
|
||
x, oben = karte[0] + karte[2] + 10.0, karte[1]
|
||
else:
|
||
x, oben = 10.0, 10.0
|
||
if quelle is not None:
|
||
unten = quelle[1] - ABSTAND_MM
|
||
elif karte is not None:
|
||
unten = karte[1] + karte[3]
|
||
else:
|
||
unten = oben + 200.0
|
||
verfuegbar = unten - oben - PLATZ_HAUPTLEGENDE_MM
|
||
seite = _aufruf(_aufruf(_aufruf(layout, "pageCollection"), "page", 0), "pageSize")
|
||
max_breite = (seite.width() - x - 10.0) if seite is not None else float("inf")
|
||
|
||
# Wenigste Spalten, mit denen die Höhe passt – aber nie breiter als bis zum Seitenrand.
|
||
spalten, groesse = 1, mindestgroesse(legende, layout, 1)
|
||
while groesse is not None and groesse[1] > verfuegbar and spalten < 3:
|
||
breiter = mindestgroesse(legende, layout, spalten + 1)
|
||
if breiter is None or breiter[0] > max_breite:
|
||
break
|
||
spalten, groesse = spalten + 1, breiter
|
||
_aufruf(legende, "setColumnCount", spalten)
|
||
_aufruf(legende, "setSplitLayer", True)
|
||
_aufruf(legende, "setEqualColumnWidth", False)
|
||
|
||
unten_links = getattr(getattr(QgsLayoutItem, "ReferencePoint", QgsLayoutItem), "LowerLeft", None)
|
||
if unten_links is not None:
|
||
_aufruf(legende, "setReferencePoint", unten_links)
|
||
breite, hoehe = groesse if groesse is not None else (180.0, 20.0)
|
||
legende.attemptMove(QgsLayoutPoint(x, unten, MM))
|
||
legende.attemptResize(QgsLayoutSize(breite, hoehe, MM))
|
||
if groesse is not None and hoehe > verfuegbar:
|
||
return ("Die Basemap-Legende braucht mehr Platz, als neben der Karte frei ist. Bitte im Layout "
|
||
"anpassen oder weniger Kategorien wählen.")
|
||
return ""
|
||
|
||
|
||
def schreibe_legende(layout: Any, map_item: Any, eintraege: list, symbole: dict[str, Any], project: Any,
|
||
*, bereich: str, massstab: float) -> str:
|
||
"""Schreibt bzw. ersetzt die Basemap-Legende des Layouts.
|
||
|
||
Ohne gewählte Einträge wird ein vorhandenes Basemap-Legendenelement entfernt.
|
||
|
||
Returns
|
||
-------
|
||
str
|
||
Hinweis für den Nutzer oder leer.
|
||
"""
|
||
entferne_hilfslayer(project, layout)
|
||
legende = _aufruf(layout, "itemById", LEGENDE_ID)
|
||
markiere_layout(layout)
|
||
layout.setCustomProperty(LAYOUT_BEREICH, bereich)
|
||
layout.setCustomProperty(LAYOUT_MASSSTAB, float(massstab))
|
||
layout.setCustomProperty(LAYOUT_EINTRAEGE, json.dumps([e.schluessel for e in eintraege]))
|
||
|
||
if not eintraege:
|
||
if legende is not None:
|
||
layout.removeLayoutItem(legende)
|
||
layout.setCustomProperty(LAYOUT_HILFSLAYER, "")
|
||
return ""
|
||
|
||
crs = _aufruf(map_item, "crs")
|
||
gruppen = erzeuge_hilfslayer(project, layout.name(), _aufruf(crs, "authid", standard="") or "EPSG:25833",
|
||
eintraege, symbole)
|
||
layout.setCustomProperty(LAYOUT_HILFSLAYER, ",".join(l.id() for _, ls in gruppen for l in ls))
|
||
|
||
neu = legende is None
|
||
if neu:
|
||
legende = _neues_legendenelement(layout)
|
||
_aufruf(legende, "setLocked", False)
|
||
legende.setLinkedMap(map_item)
|
||
legende.setAutoUpdateModel(False)
|
||
legende.setLegendFilterByMapEnabled(False)
|
||
legende.setTitle(LEGENDE_TITEL)
|
||
_aufruf(legende, "setAutoWrapLinesAfter", TEXTUMBRUCH_MM)
|
||
wurzel = legende.model().rootGroup()
|
||
wurzel.removeAllChildren()
|
||
for gruppe, layer_liste in gruppen:
|
||
for nummer, layer in enumerate(layer_liste):
|
||
knoten = wurzel.addLayer(layer)
|
||
_aufruf(knoten, "setName", gruppe)
|
||
QgsLegendRenderer.setNodeLegendStyle(knoten, LEGEND_STYLE_SUBGROUP if nummer == 0 else LEGEND_STYLE_HIDDEN)
|
||
_aufruf(legende, "refresh")
|
||
hinweis = platziere(layout, legende) if neu else ""
|
||
if not neu:
|
||
_aufruf(legende, "adjustBoxSize")
|
||
_aufruf(legende, "setLocked", True)
|
||
return hinweis
|