Standard- und Mindestbreite der Haupt- und Basemap-Legende ist die Breite des Kartenschilds (180 mm, MIN_SPALTENBREITE_MM) – die zunächst genannten 210 mm sind der gesamte Kartenrand einschließlich Abständen. Die Legenden passen sich weiter ihrem Inhalt an, werden aber nie schmaler (setMinimumSize). QGIS speichert die Mindestgröße nicht im Projekt; nach dem Neuladen schrumpfte die Legende bei der nächsten Größenanpassung wieder (QGIS-Test: 43,7 mm). Deshalb vermerkt das Layout die Mindestbreite (sn_legende_mindestbreite), und ein Listener auf readProject setzt sie beim Laden wieder (setup/teardown_mindestbreiten_listener in main.py, wie der Verfahrensgebiet-Listener). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
472 lines
19 KiB
Python
472 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 MIN_SPALTENBREITE_MM, 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))
|
||
# Mindestens so breit wie das Kartenschild (Basis #96)
|
||
_aufruf(legende, "setMinimumSize", QgsLayoutSize(MIN_SPALTENBREITE_MM, 0.0, 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
|