diff --git a/docs/README.md b/docs/README.md index e50fd4d..bbf4bb7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,7 @@ | `tests/testkatalog.html` | generiert | dieselben Daten als lokal zu öffnendes Dashboard „Prüfstand“ (Baumdiagramm je Testdatei, Filter, Suche) – vom selben Lauf erzeugt | | `tests/*.md` | lebend | Testkonzepte je Fachablauf, mit Mermaid; Vorlage in `vorlagen/` | | `konzept/07_qgis_tests_plan.md` | Vorschlag | Stufenplan für Tests gegen echtes QGIS | +| `konzept/10_basemap_legende.md` | zur Freigabe | Basemap-Legende im Druck und Basemap-Stilvorlagen (Basis #88) | | `konzept/09_alkis7_umstellung.md` | Prüfliste | ALKIS 6 ↔ 7 (GID 6.0/7.1), was in `sn_verfahrensgebiet` anzupassen ist | | `konzept/08_repo_einrichten.md` | Prüfliste | Repo auf Gitea anlegen, `stable` und Branch-Schutz | | `konzept/02_zusammenarbeit_ki_agenten.md` | stabil | Begründung und Aufbau der Zusammenarbeit | diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md new file mode 100644 index 0000000..4da9f65 --- /dev/null +++ b/docs/konzept/10_basemap_legende.md @@ -0,0 +1,235 @@ +# Basemap-Legende und Basemap-Stilvorlagen im Druck + +Stand 02.10.2026. Konzept zur Freigabe, 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. +- **Speichern unter …:** Name abfragen, vorhandene Variante nur nach Rückfrage überschreiben. +- **Später möglich, nicht Teil dieses Tickets:** mitgelieferte Varianten im Plugin (`styles/basemap/`) und ein + gemeinsamer Ordner (Netzlaufwerk) für die ganze Behörde. + +### 5.4 Werkzeug im Projektwissen + +`scripts/basemap_katalog.py` (neu, nur Standardbibliothek): lädt `bm_web_col.json`, gruppiert Style-IDs nach +Quell-Layer und `klasse` (Präfixe/Suffixe wie `Kontur_`, `Decker_`, `Bruecke_`, `Tunnel_`, `_getrennt`, +`_Fahrbahnachse`, `_FSZ1` werden zusammengefasst), schlägt `symbol_stile` vor und schreibt einen Entwurf von +`basemap_legende.json`. Bestehende fachliche Nachbearbeitung (Bezeichnungen, Gruppen, Reihenfolge) bleibt bei +einem erneuten Lauf erhalten; neue oder entfallene Style-IDs werden gemeldet. + +### 5.5 Neue Schnittstellen (in `schnittstellen.md` nachtragen) + +| Art | Schlüssel | Bedeutung | +|---|---|---| +| Projektvariable | `basemap_auswertebereich` | `verfahrensgebiet` (Standard) oder `druckbereich` | +| Projektvariable | `basemap_legende_erzeugen` | `1`/`0`, Standard `1` | +| Projektvariable | `basemap_legende_ausgeblendet` | JSON-Liste der zuletzt abgewählten Eintragsschlüssel (Vorbelegung) | +| Layout-Eigenschaft | `sn_basemap_legende`, `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` | Kennzeichen und letzte Einstellungen je Layout | +| QSettings | `sn_basis/basemap_standardstil` | Name der Standard-Stilvorlage | +| Öffentliche Funktion | `dialog_wrapper.ask_mehrfachauswahl(...)` | neuer allgemeiner Dialog | + +## 6. Tests (Mock-Modus, ohne Netzwerk) + +Jede neue beobachtbare Funktion bekommt mindestens einen Test mit deutschem Docstring („Prüft, dass …"). + +| Bereich | Testfälle | +|---|---| +| Katalog | Laden und Schema; jede Style-ID höchstens einem Eintrag zugeordnet; `symbol_stile` ⊆ `treffer_stile`; Ausschlussliste greift | +| Zuordnung | Mehrere Styles → ein Eintrag; deaktivierte Styles ignoriert; Zoombereich berücksichtigt; Filterprüfung als übergebene Funktion; unbekannte Style-IDs → „Sonstiges", abgewählt vorbelegt; Reihenfolge nach Gruppe/Reihenfolge | +| Layer-Erkennung | Erkennung über Quell-URL; Name nur als Rückfall; unsichtbarer Layer ignoriert; kein Basemap-Layer → Druck unverändert | +| Auswertebereich | Verfahrensgebiet als Standard; Druckbereich bei Einstellung; ohne Verfahrensgebiet Rückfall mit Hinweis; Auswahl des Layers danach wiederhergestellt | +| Dialog | Mock gibt Vorauswahl zurück; Abbruch → keine Basemap-Gruppe, Vorlage trotzdem erzeugt | +| Layout | Kennzeichen nur bei § 38/§ 41; Gruppe „Basemap" wird beim Aktualisieren ersetzt, nicht verdoppelt; Basemap-Layer-Knoten ausgeblendet | +| Projektvariablen | Auswahl und Auswertebereich werden gespeichert und wieder gelesen | +| Stilvorlagen | Speichern, Auflisten, Laden, Löschen in temporärem Profilordner; Name über `sanitize_bezeichner`; Überschreiben nur nach Bestätigung; Standard in QSettings; fehlende Begleitdatei toleriert | +| Katalog-Skript | Gruppierung Kontur/Decker/Brücke zu einem Eintrag an einem Ausschnitt der Style-JSON (Testdaten im Repo, kein Download) | + +**Nur in QGIS prüfbar** (Ergebnis im PR dokumentieren): echte Kachelauswertung, Laufzeit, Aussehen der Symbole +in der Legende, QML-Laden/Speichern am Vektorkachel-Layer, Einklappen des Bereichs unter Qt5 und Qt6. + +## 7. Umsetzungsschritte + +Ein Feature-Branch `feature/88-basemap-legende` in `Plugin_SN_Basis` von `unstable`, Commits je Schritt. + +1. **Spike in QGIS** (Python-Konsole, echtes Verfahrensgebiet, QGIS 3.44 und 4.x). Ergebnis als ADR in + `docs/entscheidungen/`: + - Laufzeit und Kachelzahl von `selectByGeometry()` für ein großes Verfahrensgebiet; kachelweise Aufteilung. + - Auswertung der Style-Filter mit `@vector_tile_zoom`. + - **Darstellung in der Legende:** (a) leere Hilfslayer (Speicherlayer je Geometrietyp, kategorisiert, nicht im + Layerbaum) als Gruppe „Basemap" in der Hauptlegende – offen, ob `setLegendFilterByMapEnabled(True)` sie + wegfiltert; oder (b) eigenes Legendenelement „Basemap" ohne Kartenfilter direkt unter der Hauptlegende. + - Zusammengesetzte Symbole und festgeschriebene Strichbreiten in der Legende. + - `saveNamedStyle`/`loadNamedStyle` am Vektorkachel-Layer inklusive deaktivierter Styles und Beschriftung. + - `QgsCollapsibleGroupBox` unter Qt5/Qt6 verfügbar, sonst Muster des Atlas-Bereichs (Kontrollkästchen + blendet Inhalt ein). +2. **Legendenkatalog:** Skript, erster Entwurf, fachliche Durchsicht der Bezeichnungen und Gruppen. +3. **Auswertung und Zuordnung** (`basemap_legende.py`) mit Tests. +4. **Dialog** `ask_mehrfachauswahl` mit Test. +5. **Layout-Anbindung** in `print_logic`/`print_layout` für § 38/§ 41, Projektvariablen. +6. **Basemap-Bereich im Druck-Tab** mit Aktualisieren-Knopf. +7. **Stilvorlagen** (`basemap_stilvorlagen.py`, Teil des Bereichs). +8. `python scripts/run_tests.py sn_basis --baseline`, `python scripts/testkatalog.py`, + `python scripts/pruefe_umlaute.py Plugin_SN_Basis`, QGIS-Abnahme, `changelog.txt`, PR-Text, Übergabe: + `schnittstellen.md`, `modulkarte.md`, `fallstricke.md` (Katalog hängt an Stil-Version v2), `STATUS.md`. + +## 8. Abnahmekriterien + +- [ ] Beim Erstellen einer § 38- oder § 41-Vorlage mit sichtbarem Basemap-Layer erscheint der Auswahldialog mit + genau den Kategorien, die im Auswertebereich vorkommen und im Projektlayer aktiv sind. +- [ ] Die gewählten Kategorien stehen als Gruppe „Basemap" mit der Signatur des Projektlayers in der Legende; + zusammengesetzte Signaturen (Straßen) sind als ein Symbol dargestellt. +- [ ] Beschriftungen, Hausnummern und Hintergrund erscheinen nie. +- [ ] Ohne Basemap-Layer, bei Abbruch des Dialogs und bei „Anlage zur Baufreigabe" entsteht die Vorlage wie bisher. +- [ ] Auswertebereich ist im Basemap-Bereich auf Druckbereich umstellbar; die Einstellung bleibt im Projekt erhalten. +- [ ] „Basemap-Legende aktualisieren" ersetzt die Gruppe in einer bestehenden Vorlage, ohne sie zu verdoppeln. +- [ ] Stilvorlagen lassen sich unter eigenem Namen speichern, laden, löschen und als Standard markieren; mehrere + Varianten sind möglich; nach dem Laden einer Variante ohne Hausnummern fehlen diese in Karte und Legende. +- [ ] Neue Tests grün, Baseline nicht verschlechtert, Testkatalog aktualisiert, Ablauf in QGIS 3.44 und 4.x geprüft. + +## 9. Risiken + +- **Blockierender Netzwerkzugriff** im Hauptthread bei großen Gebieten → kachelweise mit Fortschrittsanzeige und + Abbruch; Hinweis bei mehr als z. B. 400 Kacheln. +- **Neue Basemap-Stilversion** (heute v2) → Katalog veraltet; Skript erneut laufen lassen, unbekannte IDs fallen + nach „Sonstiges". +- **Kachelränder:** Features sind an Kachelgrenzen beschnitten und können mehrfach vorkommen – für die Frage + „kommt vor" unerheblich. +- **Umfang:** Stilvorlagen sind fachlich eigenständig. Werden sie zu groß für einen PR, als eigenes Ticket + abtrennen (Schritte 1–6 bleiben #88).