397 lines
16 KiB
Python
397 lines
16 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,
|
|||
|
|
QgsSymbolLayerUtils,
|
|||
|
|
QgsUnitTypes,
|
|||
|
|
QgsVectorLayer,
|
|||
|
|
)
|
|||
|
|
from sn_basis.functions.qt_wrapper import QSize
|
|||
|
|
|
|||
|
|
MM = QgsUnitTypes.LayoutMillimeters
|
|||
|
|
|
|||
|
|
LEGENDE_ID = "Legende Basemap"
|
|||
|
|
LEGENDE_TITEL = "Kartengrundlage (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"}
|
|||
|
|
_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)
|
|||
|
|
symbole[eintrag.schluessel] = basis
|
|||
|
|
return symbole
|
|||
|
|
|
|||
|
|
|
|||
|
|
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(True)
|
|||
|
|
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", True)
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
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
|