# Basemap-Legende und Basemap-Stilvorlagen im Druck
Stand 02.10.2026. Konzept freigegeben und umgesetzt (Branch `feature/88-basemap-legende`, Abschnitt 10), Ticket **Plugin_SN_Basis #88** („Legende für basemap").
Betroffen ist ausschließlich **`sn_basis`** (Druck); `sn_plan41` und `sn_verfahrensgebiet` nutzen den
Druck-Tab aus `sn_basis` und bekommen die Funktion ohne eigene Änderung.
## 1. Ziel
Der Vektorkachel-Dienst basemap.de
(`https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`) hat keine
Legende, die nur die tatsächlich dargestellten Objekte zeigt. Für die Drucklayouts (§ 38, § 41) wird eine solche
Legende gebraucht:
1. Beim Anlegen einer Druckvorlage wird geprüft, ob ein Basemap-Layer im Projekt ist.
2. Die Objekte der Kacheln **im Auswertebereich** (Standard: Verfahrensgebiet) werden ausgewertet.
3. Ein Auswahldialog zeigt die gefundenen Kategorien; der Nutzer wählt ab, was nicht in die Legende soll.
4. Die gewählten Kategorien erscheinen mit der Signatur **aus dem Projektlayer** (Standard „Basemap.de - Farbe")
in der Legende – nicht mit der Signatur aus dem Signaturenkatalog, weil die Darstellung im Projekt abweichen kann.
Dazu kommt ein einklappbarer Bereich **„Basemap"** im Druck-Tab (Auswertebereich, Legende aktualisieren,
Stilvorlagen laden/speichern).
## 2. Festlegungen (mit Daniel abgestimmt, 02.10.2026)
| # | Frage | Festlegung |
|---|---|---|
| 1 | Für welche Layouts? | Funktion layoutunabhängig bauen; jedes Layout kann sie per Kennzeichen nutzen. **Vorerst aktiv für § 38 und § 41**, nicht für „Anlage zur Baufreigabe". |
| 2 | Auswertebereich | **Standard Verfahrensgebiet.** Umschaltbar auf den Druckbereich (Kartenausschnitt der Vorlage) im Basemap-Bereich des Druck-Tabs. Ohne Verfahrensgebiet: Rückfall auf den Druckbereich mit Hinweis. |
| 3 | Feinheit der Einträge | Brücken-, Tunnel- und Fahrbahnvarianten gehen im Grundeintrag auf („Kreisstraße"). „Im Bau" und „Außer Betrieb" werden eigene Einträge. Beschriftungen (`Name_*`, `Nummer_*`), Hausnummern und `Hintergrund` kommen nie in die Legende. |
| 4 | Basemap-Layer in der Hauptlegende | Der Layer selbst (heute nur Name ohne Symbol) wird ausgeblendet und durch die Gruppe „Basemap" ersetzt. |
| 5 | Nachträglich aktualisieren | Knopf „Basemap-Legende aktualisieren" im Basemap-Bereich, für bestehende Vorlagen. |
| 6 | Stilvorlagen | Darstellung des Basemap-Layers als **lokale Stilvorlage** speichern und wieder laden, **mehrere Varianten** möglich, eine davon als Standard markierbar. |
## 3. Technische Grundlagen (recherchiert 02.10.2026)
- **QGIS bietet für Vektorkachel-Layer keine Legende.** `QgsVectorTileLayer` setzt keine `QgsMapLayerLegend`
(Quellcode geprüft für 3.40, 3.44, master). Die Legendeneinträge müssen wir selbst erzeugen.
- **Auswertung über `QgsVectorTileLayer.selectByGeometry()`** (seit QGIS 3.28): holt die Kacheln zur Zoomstufe
des übergebenen Maßstabs (`QgsSelectionContext.setScale`), dekodiert sie, schneidet sie mit der Geometrie (im
Layer-KBS EPSG:3857) und liefert über `selectedFeatures()` Features mit den Zusatzfeldern `tile_layer` und
`tile_zoom`. Mit übergebenem `QgsRenderContext` werden nur Features geliefert, die der Renderer auch zeichnet.
`getRawTile()` ist in Python nicht verfügbar (`SIP_SKIP`). Der Aufruf **verändert die Auswahl des Layers**
– vorher sichern, danach wiederherstellen.
- **Kacheln enden bei Zoomstufe 15** (`bm_web_de_3857.json`). Druckmaßstäbe 1:1.000 bis 1:5.000 werten Stufe 15
aus. Eine Kachel der Stufe 15 deckt in Sachsen rund 0,6 km² ab → ein Verfahrensgebiet von 50 km² braucht
etwa 100 Kacheln. Bereits von der Kartenansicht geladene Kacheln kommen aus dem Netzwerkcache.
- **Der Signaturenkatalog ist die Style-JSON** `https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/styles/bm_web_col.json`
(569 Style-Layer: 283 Linien, 210 Symbole, 66 Flächen, 7 Kreise, 3 Extrusionen; 29 Quell-Layer).
Style-IDs sind technische Namen (`Kontur_Kreisstr`, `Decker_Kreisstr`, `Bruecke_Kreisstr_getrennt_Decker` …),
**ein Legendeneintrag besteht aus mehreren Style-Layern** (Kreisstraße: 17). Lesbare Bezeichnungen liefern die
`klasse`-Werte der Filter (520 Werte über alle Quell-Layer).
- **Beim Import in QGIS wird jede Style-ID zum `styleName`** eines `QgsVectorTileBasicRendererStyle` (Layername,
Geometrietyp, Filterausdruck, Zoombereich, Symbol, aktiv/inaktiv). Beschriftungen landen in
`QgsVectorTileBasicLabeling`. Darüber verbinden wir Katalog und Projektlayer.
- **Zoomabhängige Strichbreiten** (Mapbox-`stops`) werden in QGIS zu datendefinierten Ausdrücken mit
`@vector_tile_zoom`. In der Legende gibt es diese Variable nicht → Werte für die Druck-Zoomstufe festschreiben
oder die Variable am Legendenelement setzen (Spike, Abschnitt 7).
Quellen: [Datenmodell](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_datenmodell.html),
[Signaturenkatalog](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_col_signaturenkatalog.html),
[PyQGIS QgsVectorTileLayer](https://qgis.org/pyqgis/3.44/core/QgsVectorTileLayer.html),
QGIS-Quellcode `src/core/vectortile/qgsvectortilelayer.cpp` (`final-3_40_0`, `final-3_44_0`).
## 4. Ablauf
```mermaid
flowchart TD
A[Vorlage erstellen
oder Basemap-Legende aktualisieren] --> B{Layout nutzt
Basemap-Legende?}
B -- nein --> Z[Layout wie bisher]
B -- ja --> C{Sichtbarer Basemap-Layer
im Projekt?}
C -- nein --> Z
C -- ja --> D[Auswertebereich bestimmen
Verfahrensgebiet / Druckbereich]
D --> E[Geometrie nach EPSG:3857,
kachelweise selectByGeometry
mit Fortschritt und Abbruch]
E --> F[Feature → aktive Renderer-Styles
bei Druck-Zoomstufe]
F --> G[Styles → Legendeneinträge
über Legendenkatalog]
G --> H[Auswahldialog mit Symbolvorschau
Vorbelegung aus Projektvariable]
H --> I[Gruppe Basemap in der Legende
zusammengesetzte Symbole]
I --> J[Auswahl und Einstellungen
am Layout und im Projekt merken]
```
- **Layer-Erkennung:** Layertyp Vektorkachel **und** Quell-URL enthält `gdz_basemapde_vektor`; Name nur als
Rückfall (Layer werden sonst über den Namen gesucht, siehe `fallstricke.md`). Berücksichtigt werden nur Layer,
die in der Karte der Vorlage sichtbar sind (Thema bzw. aktuelle Sichtbarkeit). Mehrere Basemap-Layer
(z. B. Farbe und Grau): jeder bekommt eine eigene Gruppe.
- **Deaktivierte Styles** im Projektlayer erzeugen keine Legendeneinträge. Wer z. B. Hausnummern oder andere
Objekte über eine Stilvorlage ausschaltet, sieht sie automatisch auch nicht in der Legende.
- **Unbekannte Style-IDs** (eigene Ergänzungen im Projektlayer, neue Basemap-Version) erscheinen mit der
Bezeichnung „Quell-Layer: klasse" bzw. dem `styleName` in einer Gruppe „Sonstiges" und sind im Dialog
abgewählt vorbelegt.
## 5. Bausteine
### 5.1 `sn_basis` – neue und geänderte Dateien
| Datei | Inhalt |
|---|---|
| `data/basemap_legende.json` (neu) | **Legendenkatalog:** je Eintrag Schlüssel, Bezeichnung, Gruppe, Reihenfolge, `treffer_stile` (alle Style-IDs, deren Vorkommen den Eintrag auslösen), `symbol_stile` (Style-IDs, die zusammen das Legendensymbol bilden, z. B. `Kontur_Kreisstr` + `Decker_Kreisstr`), dazu eine Liste `nie_in_legende`. Kopf mit Version der Style-JSON. |
| `modules/basemap_legende.py` (neu, < 600 Zeilen) | Layer finden, Auswertebereich bestimmen, Kacheln auswerten, Styles zuordnen (reine Logik, testbar), Symbole zusammensetzen, Gruppe in eine Legende schreiben bzw. ersetzen. Keine direkten QGIS-/Qt-Importe. |
| `modules/basemap_stilvorlagen.py` (neu) | Stilvorlagen auflisten, speichern, laden, löschen, Standard merken. |
| `ui/widgets/basemap_bereich.py` (neu) | Einklappbarer Bereich „Basemap" für den Druck-Tab (Abschnitt 5.2). Eigene Datei, weil `print_tab.py` schon 709 Zeilen hat. |
| `ui/tabs/print_tab.py` | Bereich einbinden (wenige Zeilen), `cleanup()` weiterreichen. |
| `modules/print_logic.py` | Nach der Vorlagenbezeichnung Basemap-Legende ermitteln, wenn das Layout sie nutzt; Ergebnis an das Layout übergeben. Menge der Layouts mit Basemap-Legende als Konstante (`{KARTENNAME_38, KARTENNAME_41}`). |
| `modules/print_layout.py` | Neuer optionaler Parameter für die Basemap-Einträge; Layout-Kennzeichen setzen; Basemap-Layer-Knoten der Hauptlegende ausblenden. |
| `functions/dialog_wrapper.py` | Neuer allgemeiner Dialog `ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)`: Liste mit Häkchen, Symbolvorschau, Gruppen, Suche, „Alle auswählen". Mock: gibt die Vorauswahl zurück. |
| `functions/qgiscore_wrapper.py`, `qgisui_wrapper.py`, `qt_wrapper.py` | Fehlende Symbole mit Mock-Fallback: `QgsVectorTileLayer`, `QgsSelectionContext`, `QgsRenderContext`, `QgsExpression`, `QgsExpressionContext(Scope)`, `QgsCategorizedSymbolRenderer`, `QgsRendererCategory`, `QgsLayerTreeGroup`/`QgsLayerTreeLayer`, `QgsSymbolLayerUtils`, `QgsApplication.qgisSettingsDirPath`, `QgsCollapsibleGroupBox`. |
| `changelog.txt` | Eintrag oben (nächstes Release). |
**Layoutunabhängigkeit:** Ein Layout nutzt die Basemap-Legende, wenn es die Layout-Eigenschaft
`sn_basemap_legende = 1` trägt und ein Legendenelement mit bekannter ID hat (Standard `Legende`). Am Layout werden
außerdem Auswertebereich, Maßstab und gewählte Einträge gespeichert, damit „Aktualisieren" mit denselben
Einstellungen neu rechnen kann. Ein neues Layout schaltet die Funktion ein, indem es das Kennzeichen setzt bzw.
in die Konstante aufgenommen wird – ohne Änderung am Basemap-Modul.
### 5.2 Basemap-Bereich im Druck-Tab
Einklappbar (standardmäßig eingeklappt), unterhalb der bisherigen Einstellungen, oberhalb von „Vorlage erstellen".
Ohne Basemap-Layer im Projekt ist der Bereich deaktiviert und zeigt „Kein Basemap-Layer im Projekt".
```
▸ Basemap (Basemap.de - Farbe)
Legende
Auswertebereich [Verfahrensgebiet ▾] (Verfahrensgebiet | Druckbereich)
[x] Basemap-Legende beim Erstellen der Vorlage erzeugen
Vorlage [§41 Wegeplan ▾] [Basemap-Legende aktualisieren]
Stilvorlage
Variante [Ohne Hausnummern ★ ▾]
[Laden] [Speichern unter …] [Als Standard] [Löschen]
```
- „Vorlage" listet die Layouts des Projekts mit Kennzeichen `sn_basemap_legende`.
- „Aktualisieren" rechnet mit dem aktuell eingestellten Auswertebereich neu, zeigt den Auswahldialog
(vorbelegt mit der letzten Auswahl dieses Layouts) und ersetzt die Gruppe „Basemap" in der Legende.
### 5.3 Stilvorlagen (lokal, mehrere Varianten)
- **Inhalt einer Variante:** Darstellung des Basemap-Layers als QML (Kategorien Symbologie und Beschriftung,
also auch abgeschaltete Styles wie Hausnummern) und eine kleine JSON-Begleitdatei mit Anzeigename,
Erstellungsdatum, Quell-Stil (`bm_web_col` v2) und – optional – den in der Legende standardmäßig abgewählten
Einträgen.
- **Ablage:** lokal im QGIS-Profil, `/sn_basis/basemap_stile/.qml` + `.json`.
Dateiname über `sanitize_bezeichner`. Der Standard wird in `QSettings` gemerkt (`sn_basis/basemap_standardstil`).
- **Laden:** Rückfrage vor dem Überschreiben der aktuellen Darstellung. Danach Prüfung, ob die `styleName`s der
Vorlage zum Layer passen (Farbe vs. Grau, neue Basemap-Version); bei Abweichung Hinweis mit Anzahl unbekannter
Styles. Laden geschieht **nur über den Knopf**, nicht automatisch beim Öffnen eines Projekts.
Seit #101 zusätzlich: Passt das `thema` der Vorlage (Farbe/Grau) nicht zum Thema des Layers, folgt eine
Rückfrage. Ohne Thema in Vorlage oder Layer (ältere oder als QML importierte Vorlage) gibt es keine Rückfrage.
- **Speichern unter …:** Name abfragen, vorhandene Variante nur nach Rückfrage überschreiben.
- **Exportieren … / Importieren … (#101):** Export schreibt die gewählte Variante als **eine ZIP-Datei**
(`.qml` + `.json`) zum Weitergeben per Mail oder Netzlaufwerk. Import nimmt eine solche ZIP-Datei
oder eine **reine QML-Datei** an (z. B. aus „Stil speichern“ in QGIS), schlägt den Anzeigenamen aus der
Begleitdatei bzw. dem Dateinamen vor und überschreibt eine gleichnamige Variante nur nach Rückfrage. Aus der
fremden Begleitdatei werden nur `erstellt`, `layer`, `stile`, `thema` und `legende_auswahl` übernommen, ergänzt
um `importiert` und `importiert_aus`; der Standard des anderen Rechners nicht. Geprüft wird, dass die QML eine
QGIS-Stildatei ist (`