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/architektur/modulkarte.md b/docs/architektur/modulkarte.md index 203f972..e0ac01b 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -15,19 +15,20 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `main.py` | 64 | `BasisPlugin`: baut `Navigation`, startet Verfahrensgebiet-Listener, ruft `initGui()` aller `sn_*`-Plugins nach, räumt beim Unload auf | | `ui/navigation.py` | 127 | Menü und Toolbar „LNO Sachsen“; `add_action(text, callback, tooltip, priority)`, `finalize_menu_and_toolbar()`, `set_active_plugin()`. Enthält noch Debug-`print` und eine `TEST ACTION` | | `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an | -| `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück | +| `ui/base_dockwidget.py` | 172 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, jeder Tab in einem Scroll-Bereich (`setWidget`, #104), ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück | | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) | -| `ui/tabs/print_tab.py` ⚠ | 709 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas; nutzt `print_logic` | +| `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` | +| `ui/widgets/basemap_bereich.py` | 503 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen inkl. Export/Import (#101) | ### functions/ – Wrapper-Schicht (Qt/QGIS/OS mit Mock-Fallback) | Datei | Zeilen | Zweck | |---|---:|---| | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) | -| `functions/qt_wrapper.py` ⚠ | 796 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | -| `functions/qgiscore_wrapper.py` ⚠ | 1 122 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | -| `functions/qgisui_wrapper.py` | 258 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner; Mock-`iface` | -| `functions/dialog_wrapper.py` | 393 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog` | +| `functions/qt_wrapper.py` ⚠ | 873 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | +| `functions/qgiscore_wrapper.py` ⚠ | 1 474 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | +| `functions/qgisui_wrapper.py` | 292 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, Zoom auf Layer (`zoom_auf_layer`), aktiver Layer (`get_active_layer`); Mock-`iface` | +| `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) | | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` | | `functions/variable_wrapper.py` | 120 | `get_variable(key, scope)`, `set_variable(...)` mit Präfix `sn_`; Mock-Speicher | | `functions/settings_logic.py` | 50 | `SettingsLogic.VARIABLEN`: Liste der gespeicherten Projektvariablen, `load()/save()` | @@ -55,9 +56,17 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `modules/Datenabruf.py` ⚠ | 636 | `Datenabruf.datenabruf(...)`: WFS, ArcGIS REST, OGR laden; Raumfilter Verfahrensgebiet/Puffer/ohne | | `modules/LayerLoader.py` | 422 | `create_layer(provider, link, thema)`, `apply_style`, `filter_by_extent`, `filter_by_layer`, `add_to_project` | | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll | -| `modules/print_logic.py` ⚠ | 1 321 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | -| `modules/print_layout.py` ⚠ | 1 078 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | -| `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) | +| `modules/print_logic.py` ⚠ | 1 419 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | +| `modules/print_layout.py` ⚠ | 1 137 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | +| `modules/print_kartenrand.py` | 284 | Kartenrand (Legenden, Quellenangabe, Kartenschild) überlappungsfrei anordnen: 1–3 Legendenspalten, Rand breiter, Seite ggf. höher (#93); Legenden-Mindestbreite 180 mm samt Listener für das Neuladen (#96) | +| `modules/print_layout_baufreigabe.py` | 419 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) | +| `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg | +| `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer | +| `modules/basemap_legende_layout.py` | 471 | Legendensymbole aus Projektlayer-Styles zusammensetzen (weiße Flächen mit Legendenrand, #92), leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren | +| `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) | +| `modules/basemap_stilkorrektur.py` | 99 | Darstellungsfehler des Basemap-Layers korrigieren: Punktlinien mit Strichlänge 0 sichtbar machen (#90) | +| `modules/basemap_stilvorlagen.py` | 313 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil, mit Thema), Standard in `QSettings`; Export als ZIP, Import von ZIP oder QML, Themenprüfung beim Laden (#101) | +| `modules/basemap_themen.py` | 245 | Themen des Basemap-Layers (Farbe, Grau): Layer anlegen, an der Quell-URL erkennen, Thema wechseln, Prüfwert der Darstellung für ungespeicherte Änderungen (#100) | ### Sonstiges @@ -66,6 +75,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `tests/` | 32 Testdateien, `unittest`, Mock-Modus; `run_tests.py` (farbiger Runner), `test_qgis.bat` (Coverage in OSGeo4W) | | `styles/` | QML-Stile (Verfahrensgebiet, LSG, Denkmalschutz, Flurstücksbeschriftung) | | `data/landkreise.json` | Landkreise mit Kennzahl | +| `data/basemap_legende.json` | Legendenkatalog basemap.de: Style-ID → Legendeneintrag (190 Einträge), erzeugt mit `basemap_katalog.py`, fachlich nachbearbeitet | | `assets/plan-qgis340ExitCrashFix.prompt.md` | KI-Planungsnotiz zum Exit-Crash (historisch) | | `.gitea/workflows/release.yaml`, `.gitea/templates/metadata.template` | Release-Auslöser, Vorlage für `metadata.txt` | @@ -74,21 +84,28 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | Datei | Zeilen | Zweck | |---|---:|---| | `main.py` | 80 | `Verfahrensgebiet`: registriert Action (Priorität 10), erzeugt `DockWidget` mit `Pruefmanager` und `DataGrabber` | -| `ui/dockwidget.py` | 60 | Tabs `WorkingTab`, `PrintTab`, `SettingsTab`; injiziert Services per `set_services()` | -| `ui/tabs/working_tab.py` ⚠ | 873 | Tab „Bearbeitung“: Buttons und Handler für ALKIS/Shape laden, Gemarkungen, Flurstücke, Grenzpunkte, Beteiligung, Statistik; hält viel Ablauflogik | +| `ui/dockwidget.py` | 60 | Tabs `WorkingTab`, `EigentuemerTab`, `PrintTab`, `SettingsTab`; injiziert Services per `set_services()` | +| `ui/tabs/working_tab.py` ⚠ | 820 | Tab „Bearbeitung“: Buttons und Handler für ALKIS/Shape laden, Gemarkungen, Flurstücke, Grenzpunkte, Beteiligung, Statistik; hält viel Ablauflogik | +| `ui/tabs/eigentuemer_tab.py` | 440 | Tab „Eigentümer“ (#56): Flurstückssuche, Eigentümersuche, Anzeige für den Eigentümer (Sitzung, „Anzeige leeren“, Lage des Fensters merken) und Korrekturen mit Pfadanzeige | +| `ui/eigentuemer_treffer.py` | 83 | Nicht modale Trefferliste der Eigentümersuche mit Mehrfachauswahl und „Daten anzeigen“ (#56) | +| `ui/eigentuemer_anzeige.py` | 112 | Anzeigefenster für den Eigentümer (nur Name, Anschrift, E-Mail; maskiertes HTML), immer mit Rahmen, nie Vollbild (#56) | +| `ui/eigentuemer_korrektur_dialog.py` | 79 | Korrekturdialog für den Bearbeiter (#56) | | `functions/verfahrensgebiet_alkis.py` | 359 | WFS-Suche `BauRaumOderBodenordnungsrecht` per Verfahrensnummer/Landkreis, `LoadStatus` | | `functions/verfahrensgebiet_alkis_komplett.py` ⚠ | 661 | Vollständiger Ladeablauf aus ALKIS: BROR → Gemarkungen → Flurstücke → Flächenvergleich → Detailprüfung | | `functions/verfahrensgebiet_shape_komplett.py` ⚠ | 978 | Ladeablauf aus Shape/ZIP (Mehrfachauswahl, `FBSchl`-Klassifikation, Abgleich mit BROR) – siehe `docs/aus_shape_laden_technisch.md` im Repo | -| `functions/verfahrensgebiet_dialoge.py` | 300 | Dialoge: BROR-Kombinationen, Landkreis-Objekte, Landkreisauswahl | +| `functions/verfahrensgebiet_dialoge.py` | 344 | Dialoge: BROR-Kombinationen, Landkreis-Objekte, Landkreisauswahl; gemeinsame Auswahltabelle `baue_auswahltabelle` (auch für die Eigentümer-Trefferliste) | | `functions/verfahrensgebiet_edit_service.py` | 359 | Verfahrensgebiet aus Flurstücksauswahl bilden, ergänzen, entfernen, ersetzen | | `functions/gemarkung_alkis.py` | 229 | Gemarkungen aus vereinfachtem ALKIS-WFS | | `functions/flurstueck_alkis.py` | 259 | Flurstücke je Gemarkung aus ALKIS-SF-WFS (BBOX, Batches) | +| `functions/flurstueck_suche.py` | 175 | Flurstückssuche im Layer „Flurstücke“ über das `flurstueckskennzeichen`; Gemarkungsname über Layer „Gemarkungen“ | +| `functions/eigentuemer_korrektur.py` | 128 | Korrekturen ermitteln und an `_korrekturen.csv` anhängen; Pfad aus der Layerquelle; Qt-frei (#56) | +| `functions/eigentuemer_suche.py` | 225 | Eigentümersuche im CSV-Layer (Serienbriefexport): Layer erkennen, Name/Vorname suchen, gleiche Personen mit gleicher Anschrift zusammenfassen; Qt-frei | | `functions/grenzpunkt_alkis.py` ⚠ | 1 674 | Grenzpunkte/`PunktortTA` gemarkungsweise, GML-Parsing, URL-sichere Batches | | `functions/knickpunkt_pruefung.py` ⚠ | 810 | Knickpunktbasierte BROR-Prüfung, Ergebnislayer mit Kategorien | | `functions/umringspruefung.py` | 312 | Umringsprüfung (Variante 1) | | `functions/umringstatistik_grenzpunkte.py` | 374 | Statistik Grenzpunkte am Umring | | `functions/beteiligung_pruefung.py` | 453 | Abgleich BI-Server-CSV mit ALKIS-Flurstücken | -| `tests/` | 12 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) | +| `tests/` | 18 Dateien | Mock-Modus, aktuell 157/201 grün (Baseline) | | `docs/` | 3 Dateien | Ablaufdiagramme (Mermaid) für ALKIS- und Shape-Laden | | `styles/` | 6 QML | BROR, Gemarkungen, Beteiligung, Verfahrensgebiet | @@ -135,12 +152,12 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl | Datei | Zeilen | |---|---:| | `sn_verfahrensgebiet/functions/grenzpunkt_alkis.py` | 1 674 | -| `sn_basis/modules/print_logic.py` | 1 321 | +| `sn_basis/modules/print_logic.py` | 1 419 | | `sn_plan41/ui/tab_a_logic.py` | 1 239 | -| `sn_basis/functions/qgiscore_wrapper.py` | 1 122 | -| `sn_basis/modules/print_layout.py` | 1 078 | +| `sn_basis/functions/qgiscore_wrapper.py` | 1 474 | +| `sn_basis/modules/print_layout.py` | 1 137 | | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 | -| `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 | +| `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 820 | | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 | -| `sn_basis/functions/qt_wrapper.py` | 796 | -| `sn_basis/ui/tabs/print_tab.py` | 709 | +| `sn_basis/functions/qt_wrapper.py` | 873 | +| `sn_basis/ui/tabs/print_tab.py` | 730 | diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md index a3bffa4..a376256 100644 --- a/docs/architektur/schnittstellen.md +++ b/docs/architektur/schnittstellen.md @@ -40,6 +40,23 @@ Vertrag für ein Fachplugin (Muster: `Plugin_SN_Widmung/main.py`): Optional `set_services(pruefmanager=..., data_grabber=...)` (wird von Fachplugin-Docks aufgerufen) und `cleanup()` (wird beim Schließen des Docks aufgerufen – Signale trennen!). - Gemeinsame Tabs aus `sn_basis`: `ui.tabs.print_tab.PrintTab` (Druck), `ui.tabs.settings_tab.SettingsTab` (Projekteigenschaften). - `dock.action` = Referenz auf die Toolbar-Action; `BaseDockWidget.closeEvent` setzt sie zurück. +- **Scroll-Bereich je Tab (Basis #104):** `BaseDockWidget.setWidget(widget)` packt jeden Tab eines `QTabWidget` in einen + `QScrollArea` (`widgetResizable`, ohne Rahmen; Funktion `base_dockwidget.tabs_mit_scrollbereich`). Das gilt auch für + Fachplugins, die ihre Tabs in `_init_tabs_with_services()` selbst aufbauen und per `setWidget` einsetzen. Folge: + `dock.widget().widget(i)` liefert den Scroll-Bereich, die Tab-Instanz ist `….widget(i).widget()`; das Eltern-Widget + eines Tabs ist der Viewport des Scroll-Bereichs, nicht mehr das `QTabWidget`. Tabs vor `setWidget` vollständig hinzufügen. + +## 2a. Koordinatenbezugssysteme (Basis #99) + +- **Ausdehnungen stehen im KBS ihrer Quelle:** `layer.extent()` im Layer-KBS, `canvas.extent()` im KBS der + Kartenansicht (`canvas.mapSettings().destinationCrs()`), Layoutkarten in `map_item.crs()`. Wer eine Ausdehnung + weitergibt, rechnet sie mit `qgiscore_wrapper.transformiere_rechteck(rechteck, quell_crs, ziel_crs)` um. +- **Zoom auf einen Layer:** `qgisui_wrapper.zoom_auf_layer(layer, canvas=None)` – nie `canvas.setExtent(layer.extent())`. +- **Druck-KBS:** Die Hauptkarte der Layouts (§ 38/§ 41, Baufreigabe) steht im KBS des Verfahrensgebiet-Layers, ohne + diesen im Projekt-KBS (`PrintLogic._druck_crs`); `create_single_page_layout`/`create_baufreigabe_layout` nehmen es + als Parameter `crs` und setzen es vor der Ausdehnung. Alle Druckbereiche werden in dieses KBS umgerechnet. +- **Druckrechteck:** Der Layer „Druckrechteck“ hat das KBS des Verfahrensgebiets; das in der Kartenansicht + gezeichnete Rechteck wird vor dem Speichern umgerechnet. `druckrechteck_xmin/…` stehen im KBS dieses Layers. ## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt) @@ -55,6 +72,9 @@ Der Wrapper setzt das Präfix `sn_` selbst; Schlüssel **ohne** Präfix übergeb | `linkliste`, `tab_a_linkliste` | Plan41 Tab A | Plan41 | Pfad der Linkliste (XLSX) | | `tab_a_layer_id`, `Raumfilter` | Plan41 Tab A | Plan41 | Raumfilter-Einstellung | | `druckrechteck_xmin/xmax/ymin/ymax` | PrintTab/PrintLogic | Druck | gespeicherter Druckbereich | +| `basemap_auswertebereich` | Basemap-Bereich im Druck-Tab | `basemap_legende` | `verfahrensgebiet` (Standard) oder `druckbereich` (Basis #88) | +| `basemap_legende_erzeugen` | Basemap-Bereich | `print_logic` | `1`/`0`, Standard `1`: Basemap-Legende beim Erstellen einer §-38/§-41-Vorlage | +| `basemap_legende_auswahl` | `basemap_legende`, Stilvorlage laden | Auswahldialog | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` mit Eintragsschlüsseln des Legendenkatalogs | | `sn_abdeckungslayer` ⚠ | PrintTab/PrintLogic | Druck (Atlas) | Atlas-Abdeckungslayer. **Schlüssel enthält bereits `sn_` → tatsächliche Variable `sn_sn_abdeckungslayer`**; funktioniert, ist aber inkonsistent | | `verfahrensgebietslayer` ⚠ | `print_logic.py` | `print_logic.py` | **Zweite Schreibweise neben `verfahrensgebiet_layer`** – vermutlich Altlast, beim Refactoring zusammenführen | @@ -126,3 +146,56 @@ flowchart LR ## 10. Linkliste-Repository `linkliste.csv` (Semikolon, Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) → Gitea Action → `linkliste.xml/json/yaml` auf `main`. Die Plan41-Laufzeit nutzt stattdessen die mitgelieferte `assets/Linkliste.xlsx` mit abweichenden Spaltennamen. + +## 11. Basemap-Legende im Druck (Basis #88) + +- **Welche Layouts:** `print_logic.LAYOUTS_MIT_BASEMAP_LEGENDE` (heute §38, §41). Ein Layout nutzt die Funktion, + wenn es die Layout-Eigenschaft `sn_basemap_legende = 1` trägt (`basemap_legende_layout.markiere_layout`); + `PrintLayout.create_single_page_layout(..., basemap_legende=True)` setzt sie und erzeugt die Legende vor dem + Öffnen des Designers. Neue Layouts: in die Konstante aufnehmen bzw. das Kennzeichen setzen. +- **Layout-Elemente nach ID:** `Hauptkarte` (Bezugskarte), `Legende` (Hauptlegende, Spalte und Oberkante), + `Quellenangabe` (Unterkante der Basemap-Legende), `Legende Basemap` (das Basemap-Element selbst). + Umbenennen dieser IDs bricht die Platzierung. +- **Weitere Layout-Eigenschaften:** `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` (JSON), + `sn_basemap_hilfslayer` (IDs der Hilfslayer, werden beim Aktualisieren ersetzt). +- **Hilfslayer:** leere Speicherlayer, nicht im Layerbaum, Eigenschaften `sn_basemap_legende = ` und + `skipMemoryLayersCheck = 1`. Code, der über `QgsProject.mapLayers()` iteriert, sieht sie. +- **Basemap-Layer-Erkennung:** Vektorkachel-Layer, Quelle enthält `gdz_basemapde_vektor`; Name „basemap.de“ nur + als Rückfall. +- **Legendenkatalog:** `sn_basis/data/basemap_legende.json` (Style-ID → Eintrag), erzeugt/aktualisiert mit + `python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2. +- **Stilvorlagen:** lokal unter `/sn_basis/basemap_stile/`, Standard in `QSettings` + `sn_basis/basemap_standardstil`. Die Begleit-JSON trägt seit #100 das Feld `thema` (Schlüssel aus + `basemap_themen.THEMEN`; fehlt bei älteren Vorlagen). Austauschformat seit #101: ZIP mit genau einer + `.qml` und optional `.json`; der Import nimmt auch eine reine QML-Datei an. +- **Thema und Stil (Basis #100):** *Thema* = vom Dienst als Style-JSON gelieferte Darstellung (`farbe` → + `bm_web_col.json`, `grau` → `bm_web_gry.json`); *Stil* = lokale Anpassung (Stilvorlage). Alle Themen nutzen + dieselbe Kachel-URL `…/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`; das Thema steht im Quellparameter `styleUrl`. + „Schon vorhanden“ heißt: ein Layer mit identischer Kachel-URL (Quellparameter `url`). +- **Layer-Eigenschaften des Basemap-Layers (#100):** `sn_basemap_thema` (Schlüssel des zuletzt geladenen Themas) + und `sn_basemap_pruefwert` (SHA-256 der QML von Symbologie und Beschriftung ohne QGIS-Version, gesetzt nach + Laden eines Themas oder Stils und nach Speichern als Stil). Fehlt der Prüfwert, gilt der Änderungsstand als + unbekannt und vor dem Themenwechsel wird nachgefragt. +- **Themenwechsel:** `basemap_themen.wechsle_thema` stellt die Quelle per `setDataSource` um und lädt + `loadDefaultStyle()`; Layer-ID, Position im Layerbaum und Bezüge in Layouts bleiben. Ziel ist der im Layerbaum + ausgewählte Basemap-Layer, sonst der erste des Projekts. +- **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` – + Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch. +- **Neue öffentliche Funktionen (#100):** `dialog_wrapper.ask_auswahl(title, message, optionen, default, abbrechen)` – + Rückfrage mit frei benannten Knöpfen `[(schluessel, beschriftung), …]`, liefert den Schlüssel; Esc/Fehler → + `abbrechen`, Mock-Modus → `default`. `qgisui_wrapper.get_active_layer()` – ausgewählter Layer oder `None`. + +## 12. Kartenrand im Standardlayout (Basis #93) + +`print_kartenrand.ordne_kartenrand_an(layout)` läuft am Ende von `PrintLayout.create_single_page_layout` und findet +die Elemente **über ihre IDs**: `Hauptkarte`, `Legende`, `Legende Basemap`, `Quellenangabe`, `Kartenschild`, +`Kartenschild_Linie_1` … `_6` und die Schriftfeld-Beschriftungen (`Herausgeber` … `Aktenzeichen`, Liste +`UNTERE_ELEMENTE`). Wer im Layoutaufbau eine dieser IDs ändert oder ein neues Element unter die Legenden setzt, +muss die Liste anpassen. Die Seitengröße aus `print_logic` ist danach nur noch eine Mindestgröße: Breite und Höhe +können wachsen, die Karte bleibt bei 10 mm / 10 mm mit unveränderter Größe. + +Legenden sind mindestens so breit wie das Kartenschild (180 mm, `MIN_SPALTENBREITE_MM`; die 210 mm in +`print_logic.STANDARD_LAYOUT_FIXBREITE_MM` sind der gesamte Kartenrand mit Abständen). Weil QGIS die Mindestgröße +nicht speichert, trägt das Layout die Eigenschaft `sn_legende_mindestbreite`; `sn_basis.main` verbindet beim Start +`setup_mindestbreiten_listener()` (readProject) und trennt ihn in `unload()` (Basis #96). + diff --git a/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md new file mode 100644 index 0000000..c1a1860 --- /dev/null +++ b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md @@ -0,0 +1,42 @@ +# 0010 – Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog + +Status: vorgeschlagen · Datum: 2026-10-02 · Beteiligte: Daniel Helbig, Claude Code · Ticket Plugin_SN_Basis #88 + +## Kontext +QGIS erzeugt für Vektorkachel-Layer keine Legende (`QgsVectorTileLayer` setzt keine `QgsMapLayerLegend`, +geprüft im Quellcode 3.40, 3.44, master). Die §-38-/§-41-Drucklayouts brauchen aber eine Legende der +basemap.de-Objekte, die im Verfahrensgebiet tatsächlich dargestellt werden, mit der Signatur des Projektlayers. +Die Style-JSON von basemap.de hat 569 Style-Layer mit technischen IDs; ein Legendeneintrag wie „Kreisstraße“ +verteilt sich auf bis zu 20 Styles (Kontur, Decker, Brücke, getrennte Fahrbahn …). + +Ein Spike in QGIS 3.44.4 (offscreen, echte Kacheln, Gebiet bei Meißen, 619 ha) am 02.10.2026 ergab: +- `QgsVectorTileLayer.selectByGeometry()` holt und dekodiert die Kacheln zur Zoomstufe des Maßstabs selbst + (5,8 s für 10 250 Objekte, entdoppelt 3 990; Style-Zuordnung 0,5 s). +- Hilfslayer in der **Hauptlegende** verschwinden beim Rendern, weil diese nach Karteninhalt filtert + (`setLegendFilterByMapEnabled(True)`) und die Hilfslayer nicht in der Karte liegen. Außerdem müsste die + Hauptlegende dafür ihre automatische Aktualisierung verlieren. +- Ein **eigenes Legendenelement** ohne Kartenfilter zeigt alle Einträge, übersteht Speichern und Neuladen des + Projekts; leere Speicherlayer lösen beim Schließen keine Rückfrage aus. + +## Entscheidung +Die Basemap-Legende ist ein **eigenes Legendenelement „Legende Basemap“** ohne Kartenfilter und ohne +automatische Aktualisierung, dessen Einträge aus **leeren, kategorisierten Speicherlayern** stammen (je Gruppe +und Geometrietyp, nicht im Layerbaum, mit dem Projekt gespeichert). Welche Styles zu welchem Eintrag gehören +und welche das Symbol bilden, steht in einem **versionierten Legendenkatalog** `sn_basis/data/basemap_legende.json`, +der aus der Style-JSON erzeugt und fachlich nachbearbeitet wird. Signaturen werden zur Laufzeit aus dem +Projektlayer genommen, nicht aus dem Katalog. + +## Folgen +- Hauptlegende bleibt unverändert (Auto-Update, Kartenfilter); der Basemap-Layer selbst erscheint dort nicht, + weil er keine Legendenknoten hat und der Filter ihn ausblendet. +- Lokale Änderungen am Basemap-Stil (Farben, abgeschaltete Styles) wirken automatisch auch in der Legende. +- Die Basemap-Legende ist eine Momentaufnahme; nach Änderungen an Karte oder Stil „Basemap-Legende aktualisieren“. +- Bei einer neuen Basemap-Stilversion muss der Katalog mit `modules/basemap_katalog.py` erneuert werden; + unbekannte Styles erscheinen bis dahin unter „Sonstiges“ (abgewählt). +- Je Layout liegen einige leere Speicherlayer im Projekt (nicht im Layerbaum sichtbar). + +## Verworfene Alternativen +- **Hilfslayer in der Hauptlegende:** vom Kartenfilter ausgeblendet (Spike), und Verlust der Auto-Aktualisierung. +- **Eigene `QgsMapLayerLegend` (Python-Unterklasse) am Vektorkachel-Layer:** wird nicht im Projekt gespeichert, + nach Neuladen wäre die Legende weg. +- **Legende direkt aus dem Signaturenkatalog:** zeigt die Standarddarstellung, nicht die im Projekt angepasste. diff --git a/docs/entscheidungen/README.md b/docs/entscheidungen/README.md index 33a4c75..453a71f 100644 --- a/docs/entscheidungen/README.md +++ b/docs/entscheidungen/README.md @@ -34,3 +34,4 @@ Kurz, mit Grund. | 0007 | Branchmodell: drei Kanäle, `stable` als stabiler Kanal, Rückführungspflicht | vorgeschlagen | | 0008 | Werkzeugunterstützung auf Claude Code fokussiert | vorgeschlagen | | 0009 | Tests sind Teil der Definition of Done, und sie werden für Menschen dokumentiert | vorgeschlagen | +| 0010 | Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog | vorgeschlagen | diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md new file mode 100644 index 0000000..d233514 --- /dev/null +++ b/docs/konzept/10_basemap_legende.md @@ -0,0 +1,276 @@ +# 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 (`Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
Prüf-Audit: docs/status/testpruefungen.json (SHA-256 2d7704ddcd6e)
-
698Testfälle
-
655grün
+
859Testfälle
+
816grün
35rot (Baseline)
-
79%mit Docstring
-
44%aktuell geprüft
-
55Testdateien
+
83%mit Docstring
+
35%aktuell geprüft
+
66Testdateien
34%ohne Testbezug
Verteilung je Paket
-
sn_basis32 Dateien · 365 Testfälle
+
sn_basis43 Dateien · 526 Testfälle
sn_verfahrensgebiet18 Dateien · 289 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -159,12 +159,12 @@

sn_basis

Plattform-Plugin · Ordner Plugin_SN_Basis

- -
32Testdateien
365Testfälle
365grün
0rot (Baseline)
100%mit Docstring
83%geprüft
70ohne Testbezug
-

Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).

CloseEventTests 5
flowchart TD
+  
43Testdateien
526Testfälle
526grün
0rot (Baseline)
100%mit Docstring
58%geprüft
126ohne Testbezug
+

Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104).

CloseEventTests 5ScrollbereichTests 5
flowchart TD
   root(["test_base_dockwidget.py"])
   class root rootnode
   root --> g1["CloseEventTests (5)"]
@@ -174,16 +174,415 @@
   g1 --> t3("fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter")
   g1 --> t4("setzt_toolbar_action_zurueck")
   g1 --> t5("ohne_action_wird_nichts_zurueckgesetzt")
+  root --> g2["ScrollbereichTests (5)"]
+  class g2 groupnode
+  g2 --> t6("jeder_tab_liegt_in_einem_scrollbereich")
+  g2 --> t7("fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich")
+  g2 --> t8("scrollbereich_wird_nicht_doppelt_angelegt")
+  g2 --> t9("setwidget_none_und_andere_widgets_bleiben_unveraendert")
+  g2 --> t10("cleanup_erreicht_tabs_im_scrollbereich")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grüngeprüft am 2026-09-21
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grüngeprüft am 2026-09-21
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grüngeprüft am 2026-09-21
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grüngeprüft am 2026-09-21
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grüngeprüft am 2026-09-21

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grüngeprüft am 2026-09-21
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grüngeprüft am 2026-09-21
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grüngeprüft am 2026-09-21
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grüngeprüft am 2026-09-21
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grüngeprüft am 2026-09-21
ScrollbereichTests
test_jeder_tab_liegt_in_einem_scrollbereich
Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern.grünnie geprüft
ScrollbereichTests
test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich
Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt.grünnie geprüft
ScrollbereichTests
test_scrollbereich_wird_nicht_doppelt_angelegt
Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird.grünnie geprüft
ScrollbereichTests
test_setwidget_none_und_andere_widgets_bleiben_unveraendert
Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden.grünnie geprüft
ScrollbereichTests
test_cleanup_erreicht_tabs_im_scrollbereich
Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen.grünnie geprüft

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 11TestStilvorlagenAustausch 5
flowchart TD
+  root(["test_basemap_anbindung.py"])
+  class root rootnode
+  root --> g1["TestMehrfachauswahl (1)"]
+  class g1 groupnode
+  g1 --> t1("mock_modus_liefert_vorauswahl_in_listenreihenfolge")
+  root --> g2["TestDruckanbindung (4)"]
+  class g2 groupnode
+  g2 --> t2("basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter")
+  g2 --> t3("layout_wird_markiert_und_legende_erzeugt")
+  g2 --> t4("fehler_der_basemap_legende_verhindert_die_vorlage_nicht")
+  g2 --> t5("druck_tab_aktualisiert_basemap_bereich_falls_vorhanden")
+  root --> g3["TestBasemapBereich (11)"]
+  class g3 groupnode
+  g3 --> t6("auswertebereich_wird_gespeichert")
+  g3 --> t7("aktualisieren_ohne_vorlage_warnt")
+  g3 --> t8("aktualisieren_wertet_gewaehlte_vorlage_aus")
+  g3 --> t9("stil_laden_uebernimmt_legendenauswahl")
+  g3 --> t10("stil_laden_bricht_bei_fremder_vorlage_ab")
+  g3 --> t11("vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt")
+  g3 --> t12("speichern_mit_aktueller_legendenauswahl")
+  g3 --> t13("punktlinien_korrigieren_meldet_anzahl")
+  g3 --> t14("punktlinien_ohne_basemap_layer_warnt")
+  g3 --> t15("stil_speichern_ohne_basemap_layer_warnt")
+  g3 --> t16("stil_laden_bricht_bei_fremdem_thema_ab")
+  root --> g4["TestStilvorlagenAustausch (5)"]
+  class g4 groupnode
+  g4 --> t17("export_schreibt_gewaehlte_vorlage")
+  g4 --> t18("export_abgebrochen_oder_ohne_vorlage")
+  g4 --> t19("import_uebernimmt_datei_unter_gewaehltem_namen")
+  g4 --> t20("import_ersetzt_vorhandene_nur_nach_rueckfrage")
+  g4 --> t21("import_ungeeigneter_datei_warnt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grünnie geprüft
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grünnie geprüft
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grünnie geprüft
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grünnie geprüft
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grünnie geprüft
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grünnie geprüft
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grünnie geprüft
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grünnie geprüft
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grünnie geprüft
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grünnie geprüft
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grünnie geprüft
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grünnie geprüft
TestBasemapBereich
test_punktlinien_korrigieren_meldet_anzahl
Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet.grünnie geprüft
TestBasemapBereich
test_punktlinien_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird.grünnie geprüft
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grünnie geprüft
TestBasemapBereich
test_stil_laden_bricht_bei_fremdem_thema_ab
Prüft, dass eine Vorlage eines anderen Themas (Grau auf Farbe) nur nach Bestätigung geladen wird.grünnie geprüft
TestStilvorlagenAustausch
test_export_schreibt_gewaehlte_vorlage
Prüft, dass „Exportieren …“ die gewählte Vorlage in die im Dateidialog gewählte Datei schreibt und den Pfad meldet.grünnie geprüft
TestStilvorlagenAustausch
test_export_abgebrochen_oder_ohne_vorlage
Prüft, dass ohne gewählte Vorlage oder bei abgebrochenem Dateidialog nichts exportiert wird.grünnie geprüft
TestStilvorlagenAustausch
test_import_uebernimmt_datei_unter_gewaehltem_namen
Prüft, dass „Importieren …“ den Namen aus der Datei vorschlägt, unter dem gewählten Namen importiert und die neue Vorlage auswählt.grünnie geprüft
TestStilvorlagenAustausch
test_import_ersetzt_vorhandene_nur_nach_rueckfrage
Prüft, dass ein Import eine gleichnamige Vorlage ohne Bestätigung nicht überschreibt.grünnie geprüft
TestStilvorlagenAustausch
test_import_ungeeigneter_datei_warnt
Prüft, dass eine ungeeignete Importdatei gemeldet wird, ohne nach einem Namen zu fragen oder etwas zu importieren.grünnie geprüft

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
+  root(["test_basemap_kacheln.py"])
+  class root rootnode
+  root --> g1["TestSammleObjekte (4)"]
+  class g1 groupnode
+  g1 --> t1("gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt")
+  g1 --> t2("kacheln_ausserhalb_der_geometrie_werden_uebersprungen")
+  g1 --> t3("abbruch_liefert_none_und_hebt_auswahl_auf")
+  g1 --> t4("anzahl_kacheln_aus_kachelbereich")
+  root --> g2["TestZoomUndStile (2)"]
+  class g2 groupnode
+  g2 --> t5("zoomstufen_wie_im_qgis_renderer")
+  g2 --> t6("stile_aus_layer_liest_renderer_styles")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestSammleObjekte
test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt
Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden.grünnie geprüft
TestSammleObjekte
test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen
Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden.grünnie geprüft
TestSammleObjekte
test_abbruch_liefert_none_und_hebt_auswahl_auf
Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird.grünnie geprüft
TestSammleObjekte
test_anzahl_kacheln_aus_kachelbereich
Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird.grünnie geprüft
TestZoomUndStile
test_zoomstufen_wie_im_qgis_renderer
Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden.grünnie geprüft
TestZoomUndStile
test_stile_aus_layer_liest_renderer_styles
Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden.grünnie geprüft

Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88).

TestRendererStil 3TestErzeugeKatalog 6TestSymbolwahlUndPruefung 3
flowchart TD
+  root(["test_basemap_katalog.py"])
+  class root rootnode
+  root --> g1["TestRendererStil (3)"]
+  class g1 groupnode
+  g1 --> t1("beschriftung_und_3d_sind_keine_renderer_stile")
+  g1 --> t2("ausschluss_ueber_liste_und_praefix")
+  g1 --> t3("normalisierung_entfernt_variantenkennungen")
+  root --> g2["TestErzeugeKatalog (6)"]
+  class g2 groupnode
+  g2 --> t4("kontur_decker_und_bruecke_bilden_einen_eintrag")
+  g2 --> t5("zustand_im_bau_wird_eigener_eintrag")
+  g2 --> t6("punkt_und_flaeche_derselben_objektart_werden_zusammengefasst")
+  g2 --> t7("ausgeschlossene_stile_fehlen_im_katalog")
+  g2 --> t8("fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten")
+  g2 --> t9("entfallene_style_ids_werden_gemeldet")
+  root --> g3["TestSymbolwahlUndPruefung (3)"]
+  class g3 groupnode
+  g3 --> t10("symbol_stile_je_rolle_nur_grundvariante")
+  g3 --> t11("pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole")
+  g3 --> t12("ausgelieferter_katalog_ist_konsistent")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestRendererStil
test_beschriftung_und_3d_sind_keine_renderer_stile
Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon.grünnie geprüft
TestRendererStil
test_ausschluss_ueber_liste_und_praefix
Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden.grünnie geprüft
TestRendererStil
test_normalisierung_entfernt_variantenkennungen
Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen.grünnie geprüft
TestErzeugeKatalog
test_kontur_decker_und_bruecke_bilden_einen_eintrag
Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden.grünnie geprüft
TestErzeugeKatalog
test_zustand_im_bau_wird_eigener_eintrag
Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt.grünnie geprüft
TestErzeugeKatalog
test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst
Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet.grünnie geprüft
TestErzeugeKatalog
test_ausgeschlossene_stile_fehlen_im_katalog
Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden.grünnie geprüft
TestErzeugeKatalog
test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten
Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben.grünnie geprüft
TestErzeugeKatalog
test_entfallene_style_ids_werden_gemeldet
Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird.grünnie geprüft
TestSymbolwahlUndPruefung
test_symbol_stile_je_rolle_nur_grundvariante
Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird.grünnie geprüft
TestSymbolwahlUndPruefung
test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole
Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet.grünnie geprüft
TestSymbolwahlUndPruefung
test_ausgelieferter_katalog_ist_konsistent
Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält.grünnie geprüft

Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88).

TestStileUndEintraege 4TestAuswahlUndEinstellungen 6TestLayerErkennung 2TestAblauf 6
flowchart TD
+  root(["test_basemap_legende.py"])
+  class root rootnode
+  root --> g1["TestStileUndEintraege (4)"]
+  class g1 groupnode
+  g1 --> t1("stil_aktiv_nur_eingeschaltet_und_im_zoombereich")
+  g1 --> t2("getroffene_stile_pruefen_layer_geometrie_zoom_und_filter")
+  g1 --> t3("eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe")
+  g1 --> t4("ausgeschlossene_und_unbekannte_styles")
+  root --> g2["TestAuswahlUndEinstellungen (6)"]
+  class g2 groupnode
+  g2 --> t5("vorauswahl_bekannte_an_unbekannte_aus")
+  g2 --> t6("auswahl_wird_gemerkt_und_vorbelegt")
+  g2 --> t7("merken_ergaenzt_die_bisherige_auswahl")
+  g2 --> t8("kaputte_projektvariable_ergibt_leere_auswahl")
+  g2 --> t9("auswertebereich_standard_und_umstellung")
+  g2 --> t10("legende_erzeugen_standard_an")
+  root --> g3["TestLayerErkennung (2)"]
+  class g3 groupnode
+  g3 --> t11("erkennung_ueber_quelle_und_namen")
+  g3 --> t12("sichtbare_layer_aus_thema_feste_liste_oder_layerbaum")
+  root --> g4["TestAblauf (6)"]
+  class g4 groupnode
+  g4 --> t13("ohne_basemap_layer_passiert_nichts")
+  g4 --> t14("gewaehlte_eintraege_werden_geschrieben_und_gemerkt")
+  g4 --> t15("abbruch_im_dialog_aendert_nichts")
+  g4 --> t16("rueckfall_auf_druckbereich_wird_gemeldet")
+  g4 --> t17("ohne_objekte_keine_legende")
+  g4 --> t18("viele_kacheln_werden_vorher_bestaetigt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grünnie geprüft
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grünnie geprüft
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grünnie geprüft
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grünnie geprüft
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grünnie geprüft
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grünnie geprüft
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grünnie geprüft
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grünnie geprüft
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grünnie geprüft
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grünnie geprüft
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grünnie geprüft
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grünnie geprüft
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grünnie geprüft
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grünnie geprüft
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grünnie geprüft
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grünnie geprüft
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grünnie geprüft
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grünnie geprüft

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestAutobahnSymbol 3TestHilfslayer 3TestPlatzierung 7
flowchart TD
+  root(["test_basemap_legende_layout.py"])
+  class root rootnode
+  root --> g1["TestKennzeichenUndKarte (2)"]
+  class g1 groupnode
+  g1 --> t1("layout_kennzeichen_setzen_und_erkennen")
+  g1 --> t2("hauptkarte_ueber_id")
+  root --> g2["TestSymbolwahl (2)"]
+  class g2 groupnode
+  g2 --> t3("symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge")
+  g2 --> t4("ohne_aktive_symbol_stile_erster_getroffener_style")
+  root --> g3["TestAutobahnSymbol (3)"]
+  class g3 groupnode
+  g3 --> t5("druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur")
+  g3 --> t6("zoom_11_bis_13_bisherige_kontur_und_decker")
+  g3 --> t7("unter_zoom_11_blauer_decker")
+  root --> g4["TestHilfslayer (3)"]
+  class g4 groupnode
+  g4 --> t8("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
+  g4 --> t9("alte_hilfslayer_werden_entfernt")
+  g4 --> t10("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
+  root --> g5["TestPlatzierung (7)"]
+  class g5 groupnode
+  g5 --> t11("einspaltig_ueber_der_quellenangabe")
+  g5 --> t12("mehrspaltig_wenn_hoehe_nicht_reicht")
+  g5 --> t13("spalten_ungleich_breit")
+  g5 --> t14("mindestgroesse_mit_ungleichen_spalten_und_umbruch")
+  g5 --> t15("neue_basemap_legende_mindestens_so_breit_wie_kartenschild")
+  g5 --> t16("titel_zweizeilig")
+  g5 --> t17("nie_breiter_als_der_seitenrand_mit_hinweis")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grünnie geprüft
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grünnie geprüft
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grünnie geprüft
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grünnie geprüft
TestAutobahnSymbol
test_druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur
Prüft, dass das Autobahn-Symbol ab Zoom 14 aus grauer Kontur und blauem Decker der Fahrbahnachse besteht statt nur aus einem grauen Style.grünnie geprüft
TestAutobahnSymbol
test_zoom_11_bis_13_bisherige_kontur_und_decker
Prüft, dass das Autobahn-Symbol bei Zoom 11 bis 13 weiterhin aus Kontur und Decker der getrennten Fahrbahn besteht.grünnie geprüft
TestAutobahnSymbol
test_unter_zoom_11_blauer_decker
Prüft, dass das Autobahn-Symbol unter Zoom 11 den blauen Decker der Übersichtsdarstellung nutzt.grünnie geprüft
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grünnie geprüft
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grünnie geprüft
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grünnie geprüft
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grünnie geprüft
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grünnie geprüft
TestPlatzierung
test_spalten_ungleich_breit
Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96).grünnie geprüft
TestPlatzierung
test_mindestgroesse_mit_ungleichen_spalten_und_umbruch
Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96).grünnie geprüft
TestPlatzierung
test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild
Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt.grünnie geprüft
TestPlatzierung
test_titel_zweizeilig
Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96).grünnie geprüft
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grünnie geprüft

Tests für den Legendenrand weißer Basemap-Flächen (Basis #92).

TestBrauchtLegendenrand 4TestErgaenzeLegendenrand 2
flowchart TD
+  root(["test_basemap_legende_rand.py"])
+  class root rootnode
+  root --> g1["TestBrauchtLegendenrand (4)"]
+  class g1 groupnode
+  g1 --> t1("weisse_flaeche_ohne_rand")
+  g1 --> t2("farbige_flaeche_oder_flaeche_mit_kontur")
+  g1 --> t3("heller_rand_und_fast_weiss_zaehlen_als_unsichtbar")
+  g1 --> t4("andere_ebenen_und_leeres_symbol")
+  root --> g2["TestErgaenzeLegendenrand (2)"]
+  class g2 groupnode
+  g2 --> t5("weisse_flaeche_bekommt_grauen_rand")
+  g2 --> t6("farbige_flaeche_und_linien_bleiben_unveraendert")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestBrauchtLegendenrand
test_weisse_flaeche_ohne_rand
Prüft, dass eine weiße Fläche ohne Umrandung einen Legendenrand braucht.grünnie geprüft
TestBrauchtLegendenrand
test_farbige_flaeche_oder_flaeche_mit_kontur
Prüft, dass farbige Flächen und weiße Flächen mit dunkler Kontur oder eigener Konturlinie keinen Rand bekommen.grünnie geprüft
TestBrauchtLegendenrand
test_heller_rand_und_fast_weiss_zaehlen_als_unsichtbar
Prüft, dass fast weiße Füllungen und helle oder durchsichtige Ränder als unsichtbar gelten.grünnie geprüft
TestBrauchtLegendenrand
test_andere_ebenen_und_leeres_symbol
Prüft, dass Symbole mit anderen Ebenen (Muster, Punkte) und leere Symbole keinen Rand bekommen.grünnie geprüft
TestErgaenzeLegendenrand
test_weisse_flaeche_bekommt_grauen_rand
Prüft, dass eine weiße Fläche ohne Kontur eine graue Randlinie als oberste Ebene bekommt.grünnie geprüft
TestErgaenzeLegendenrand
test_farbige_flaeche_und_linien_bleiben_unveraendert
Prüft, dass farbige Flächen, Flächen mit Kontur und Liniensymbole keinen Rand bekommen.grünnie geprüft

Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90).

TestMusterUndAusdruck 2TestLayerkorrektur 4
flowchart TD
+  root(["test_basemap_stilkorrektur.py"])
+  class root rootnode
+  root --> g1["TestMusterUndAusdruck (2)"]
+  class g1 groupnode
+  g1 --> t1("strichlaenge_null_im_festen_muster")
+  g1 --> t2("strichlaenge_null_im_zoomabhaengigen_ausdruck")
+  root --> g2["TestLayerkorrektur (4)"]
+  class g2 groupnode
+  g2 --> t3("punktlinien_werden_korrigiert_andere_styles_nicht")
+  g2 --> t4("zweiter_lauf_korrigiert_nichts")
+  g2 --> t5("nur_ausdruck_ohne_festes_muster")
+  g2 --> t6("layer_ohne_renderer")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestMusterUndAusdruck
test_strichlaenge_null_im_festen_muster
Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden.grünnie geprüft
TestMusterUndAusdruck
test_strichlaenge_null_im_zoomabhaengigen_ausdruck
Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt.grünnie geprüft
TestLayerkorrektur
test_punktlinien_werden_korrigiert_andere_styles_nicht
Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt.grünnie geprüft
TestLayerkorrektur
test_zweiter_lauf_korrigiert_nichts
Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt.grünnie geprüft
TestLayerkorrektur
test_nur_ausdruck_ohne_festes_muster
Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht.grünnie geprüft
TestLayerkorrektur
test_layer_ohne_renderer
Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert.grünnie geprüft

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88),

TestStilvorlagen 8TestExportImport 6
flowchart TD
+  root(["test_basemap_stilvorlagen.py"])
+  class root rootnode
+  root --> g1["TestStilvorlagen (8)"]
+  class g1 groupnode
+  g1 --> t1("speichern_und_auflisten")
+  g1 --> t2("mehrere_varianten_alphabetisch")
+  g1 --> t3("leerer_name_oder_fehler_beim_schreiben")
+  g1 --> t4("laden_setzt_stil_und_zeichnet_neu")
+  g1 --> t5("passung_meldet_fremde_styles")
+  g1 --> t6("standard_setzen_und_beim_loeschen_entfernen")
+  g1 --> t7("fehlende_begleitdatei_wird_toleriert")
+  g1 --> t8("ohne_ordner_leere_liste")
+  root --> g2["TestExportImport (6)"]
+  class g2 groupnode
+  g2 --> t9("export_und_import_uebertragen_vorlage_mit_begleitdaten")
+  g2 --> t10("export_einer_fehlenden_vorlage")
+  g2 --> t11("import_einer_reinen_qml_datei")
+  g2 --> t12("import_lehnt_ungeeignete_dateien_ab")
+  g2 --> t13("import_uebernimmt_nur_bekannte_begleitdaten")
+  g2 --> t14("thema_der_vorlage_passt_nicht_zum_layer")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grünnie geprüft
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grünnie geprüft
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grünnie geprüft
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grünnie geprüft
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grünnie geprüft
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grünnie geprüft
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grünnie geprüft
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grünnie geprüft
TestExportImport
test_export_und_import_uebertragen_vorlage_mit_begleitdaten
Prüft, dass eine exportierte Vorlage auf einem anderen Rechner mit Anzeigename, Thema, Style-Namen und Legendenauswahl ankommt.grünnie geprüft
TestExportImport
test_export_einer_fehlenden_vorlage
Prüft, dass der Export einer nicht vorhandenen Vorlage als ValueError gemeldet wird und keine Datei entsteht.grünnie geprüft
TestExportImport
test_import_einer_reinen_qml_datei
Prüft, dass eine QML-Datei ohne Begleitdatei mit dem Dateinamen als Namensvorschlag und ohne Thema übernommen wird.grünnie geprüft
TestExportImport
test_import_lehnt_ungeeignete_dateien_ab
Prüft, dass Dateien ohne QGIS-Stil und ZIP-Dateien ohne genau eine QML-Datei abgelehnt werden, ohne etwas anzulegen.grünnie geprüft
TestExportImport
test_import_uebernimmt_nur_bekannte_begleitdaten
Prüft, dass ein Import unbekannte oder falsch typisierte Angaben der Begleitdatei verwirft und den gewählten Namen nutzt.grünnie geprüft
TestExportImport
test_thema_der_vorlage_passt_nicht_zum_layer
Prüft, dass ein abweichendes Thema der Vorlage gemeldet wird und ohne Thema in Vorlage oder Layer keine Meldung kommt.grünnie geprüft

Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100):

TestQuelleUndThema 4TestPruefwert 3TestLayerUndThemenwechsel 7TestStilvorlageMitThema 1TestAuswahlDialog 1TestBasemapHinzufuegen 5TestThemaWechseln 8TestZielLayer 1
flowchart TD
+  root(["test_basemap_themen.py"])
+  class root rootnode
+  root --> g1["TestQuelleUndThema (4)"]
+  class g1 groupnode
+  g1 --> t1("quelle_enthaelt_kachel_url_und_style_des_themas")
+  g1 --> t2("unbekannte_style_json_ergibt_kein_thema")
+  g1 --> t3("unbekannter_schluessel_faellt_auf_farbe_zurueck")
+  g1 --> t4("vorhandener_layer_wird_an_der_quell_url_erkannt")
+  root --> g2["TestPruefwert (3)"]
+  class g2 groupnode
+  g2 --> t5("gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version")
+  g2 --> t6("ohne_ausgabe_der_darstellung_kein_pruefwert")
+  g2 --> t7("aenderungsstand_unbekannt_unveraendert_geaendert")
+  root --> g3["TestLayerUndThemenwechsel (7)"]
+  class g3 groupnode
+  g3 --> t8("neuer_layer_hat_thema_namen_und_gemerkten_stand")
+  g3 --> t9("ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler")
+  g3 --> t10("ohne_vektorkacheln_kein_layer")
+  g3 --> t11("layer_kommt_ganz_unten_in_den_layerbaum")
+  g3 --> t12("themenwechsel_stellt_quelle_um_und_merkt_neuen_stand")
+  g3 --> t13("themenwechsel_behaelt_eigenen_layernamen")
+  g3 --> t14("themenwechsel_ohne_umstellbare_quelle_meldet_fehler")
+  root --> g4["TestStilvorlageMitThema (1)"]
+  class g4 groupnode
+  g4 --> t15("stilvorlage_vermerkt_das_thema")
+  root --> g5["TestAuswahlDialog (1)"]
+  class g5 groupnode
+  g5 --> t16("mock_modus_liefert_vorbelegung")
+  root --> g6["TestBasemapHinzufuegen (5)"]
+  class g6 groupnode
+  g6 --> t17("ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt")
+  g6 --> t18("vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen")
+  g6 --> t19("zusaetzlich_laden_fuegt_zweiten_layer_hinzu")
+  g6 --> t20("ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema")
+  g6 --> t21("fehler_beim_anlegen_wird_gemeldet")
+  root --> g7["TestThemaWechseln (8)"]
+  class g7 groupnode
+  g7 --> t22("unveraenderter_layer_wechselt_ohne_rueckfrage")
+  g7 --> t23("gleiches_thema_aendert_nichts")
+  g7 --> t24("geaenderte_darstellung_abbrechen_behaelt_thema")
+  g7 --> t25("geaenderte_darstellung_verwerfen_wechselt")
+  g7 --> t26("als_stil_speichern_sichert_vor_dem_wechsel")
+  g7 --> t27("abgebrochenes_speichern_verhindert_den_wechsel")
+  g7 --> t28("unbekannter_stand_fragt_nach")
+  g7 --> t29("ohne_basemap_layer_nur_vorauswahl")
+  root --> g8["TestZielLayer (1)"]
+  class g8 groupnode
+  g8 --> t30("ausgewaehlter_basemap_layer_hat_vorrang")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestQuelleUndThema
test_quelle_enthaelt_kachel_url_und_style_des_themas
Prüft, dass die Layerquelle eines Themas die gemeinsame Kachel-URL und die Style-JSON des Themas enthält.grünnie geprüft
TestQuelleUndThema
test_unbekannte_style_json_ergibt_kein_thema
Prüft, dass ein Layer mit fremder oder fehlender Style-JSON keinem Thema zugeordnet wird.grünnie geprüft
TestQuelleUndThema
test_unbekannter_schluessel_faellt_auf_farbe_zurueck
Prüft, dass ein unbekannter Themenschlüssel auf das Standardthema „Farbe“ zurückfällt.grünnie geprüft
TestQuelleUndThema
test_vorhandener_layer_wird_an_der_quell_url_erkannt
Prüft, dass ein vorhandener Basemap-Layer an der Kachel-URL erkannt wird – unabhängig von Name, Thema und URL-Kodierung.grünnie geprüft
TestPruefwert
test_gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version
Prüft, dass dieselbe Darstellung denselben Prüfwert ergibt, auch wenn die QGIS-Version in der QML abweicht.grünnie geprüft
TestPruefwert
test_ohne_ausgabe_der_darstellung_kein_pruefwert
Prüft, dass kein Prüfwert entsteht, wenn der Layer seine Darstellung nicht ausgeben kann.grünnie geprüft
TestPruefwert
test_aenderungsstand_unbekannt_unveraendert_geaendert
Prüft, dass der Änderungsstand ohne gemerkten Prüfwert „unbekannt“ ist, nach dem Merken „unverändert“ und nach einer Änderung „geändert“.grünnie geprüft
TestLayerUndThemenwechsel
test_neuer_layer_hat_thema_namen_und_gemerkten_stand
Prüft, dass ein neuer Basemap-Layer im gewählten Thema entsteht, den Themenstil lädt und als unverändert gilt.grünnie geprüft
TestLayerUndThemenwechsel
test_ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler
Prüft, dass ein ungültiger Layer oder ein nicht ladbarer Themenstil als ValueError gemeldet wird.grünnie geprüft
TestLayerUndThemenwechsel
test_ohne_vektorkacheln_kein_layer
Prüft, dass ohne Vektorkachel-Unterstützung (Mock-Modus) kein Layer erzeugt, sondern ein Fehler gemeldet wird.grünnie geprüft
TestLayerUndThemenwechsel
test_layer_kommt_ganz_unten_in_den_layerbaum
Prüft, dass der Basemap-Layer ohne automatische Einordnung ins Projekt kommt und unten an den Layerbaum angehängt wird.grünnie geprüft
TestLayerUndThemenwechsel
test_themenwechsel_stellt_quelle_um_und_merkt_neuen_stand
Prüft, dass der Themenwechsel die Style-JSON der Quelle umstellt, den Themenstil lädt, den automatischen Namen anpasst und den neuen Stand merkt.grünnie geprüft
TestLayerUndThemenwechsel
test_themenwechsel_behaelt_eigenen_layernamen
Prüft, dass ein vom Nutzer vergebener Layername beim Themenwechsel erhalten bleibt.grünnie geprüft
TestLayerUndThemenwechsel
test_themenwechsel_ohne_umstellbare_quelle_meldet_fehler
Prüft, dass ein Layer ohne setDataSource den Themenwechsel mit einem ValueError ablehnt.grünnie geprüft
TestStilvorlageMitThema
test_stilvorlage_vermerkt_das_thema
Prüft, dass eine gespeicherte Stilvorlage das Thema des Layers in ihrer Begleitdatei und in der Liste vermerkt.grünnie geprüft
TestAuswahlDialog
test_mock_modus_liefert_vorbelegung
Prüft, dass die Rückfrage mit frei benannten Knöpfen im Mock-Modus den vorbelegten Schlüssel liefert.grünnie geprüft
TestBasemapHinzufuegen
test_ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt
Prüft, dass ohne Basemap-Layer gleicher Quelle sofort ein Layer im gewählten Thema hinzugefügt wird.grünnie geprüft
TestBasemapHinzufuegen
test_vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen
Prüft, dass ein vorhandener Layer gleicher Quelle in der Rückfrage genannt wird und „Abbrechen“ nichts verändert.grünnie geprüft
TestBasemapHinzufuegen
test_zusaetzlich_laden_fuegt_zweiten_layer_hinzu
Prüft, dass „Zusätzlich laden“ einen weiteren Basemap-Layer neben dem vorhandenen hinzufügt.grünnie geprüft
TestBasemapHinzufuegen
test_ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema
Prüft, dass „Ersetzen“ keinen neuen Layer anlegt, sondern den vorhandenen im gewählten Thema neu lädt.grünnie geprüft
TestBasemapHinzufuegen
test_fehler_beim_anlegen_wird_gemeldet
Prüft, dass ein nicht erreichbarer Dienst als Warnung gemeldet und kein Layer hinzugefügt wird.grünnie geprüft
TestThemaWechseln
test_unveraenderter_layer_wechselt_ohne_rueckfrage
Prüft, dass ein unveränderter Basemap-Layer ohne Rückfrage auf das gewählte Thema wechselt.grünnie geprüft
TestThemaWechseln
test_gleiches_thema_aendert_nichts
Prüft, dass die Wahl des Themas, das der Layer schon hat, nichts neu lädt.grünnie geprüft
TestThemaWechseln
test_geaenderte_darstellung_abbrechen_behaelt_thema
Prüft, dass bei geänderter Darstellung nachgefragt wird und „Abbrechen“ Thema und Darstellung unverändert lässt.grünnie geprüft
TestThemaWechseln
test_geaenderte_darstellung_verwerfen_wechselt
Prüft, dass „Verwerfen“ die geänderte Darstellung aufgibt und das Thema wechselt.grünnie geprüft
TestThemaWechseln
test_als_stil_speichern_sichert_vor_dem_wechsel
Prüft, dass „Als Stil speichern“ die Darstellung vor dem Themenwechsel als Stilvorlage sichert.grünnie geprüft
TestThemaWechseln
test_abgebrochenes_speichern_verhindert_den_wechsel
Prüft, dass das Thema nicht wechselt, wenn „Als Stil speichern“ gewählt, die Namensabfrage aber abgebrochen wird.grünnie geprüft
TestThemaWechseln
test_unbekannter_stand_fragt_nach
Prüft, dass bei einem Layer ohne gemerkten Prüfwert (vor dieser Funktion geladen) vorsichtshalber nachgefragt wird.grünnie geprüft
TestThemaWechseln
test_ohne_basemap_layer_nur_vorauswahl
Prüft, dass die Themenwahl ohne Basemap-Layer nur als Vorauswahl für „Basemap hinzufügen“ dient und nichts meldet.grünnie geprüft
TestZielLayer
test_ausgewaehlter_basemap_layer_hat_vorrang
Prüft, dass der im Layerbaum ausgewählte Basemap-Layer Ziel des Themenwechsels ist, sonst der erste Basemap-Layer.grünnie geprüft

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
   root(["test_datagrabber.py"])
   class root rootnode
   root --> g1["TestDataGrabber (8)"]
@@ -390,7 +789,44 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grüngeprüft am 2026-09-21
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grüngeprüft am 2026-09-21
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grüngeprüft am 2026-09-21
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grüngeprüft am 2026-09-21
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grüngeprüft am 2026-09-21
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grüngeprüft am 2026-09-21

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grüngeprüft am 2026-09-21
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grüngeprüft am 2026-09-21
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grüngeprüft am 2026-09-21
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grüngeprüft am 2026-09-21
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grüngeprüft am 2026-09-21
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grüngeprüft am 2026-09-21

Tests zu Basis #99: Ist das Projekt-KBS ein anderes als das KBS des Verfahrensgebiets, müssen

TestTransformiereRechteck 3TestZoomAufLayer 2TestDruckKbs 5TestLayoutkarteImDruckKbs 1
flowchart TD
+  root(["test_druck_kbs.py"])
+  class root rootnode
+  root --> g1["TestTransformiereRechteck (3)"]
+  class g1 groupnode
+  g1 --> t1("verschiedene_kbs_werden_umgerechnet")
+  g1 --> t2("gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert")
+  g1 --> t3("fehler_bei_der_umrechnung_liefert_das_rechteck")
+  root --> g2["TestZoomAufLayer (2)"]
+  class g2 groupnode
+  g2 --> t4("ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet")
+  g2 --> t5("ohne_layer_oder_kartenansicht_kein_zoom")
+  root --> g3["TestDruckKbs (5)"]
+  class g3 groupnode
+  g3 --> t6("druck_kbs_ist_das_kbs_des_verfahrensgebiets")
+  g3 --> t7("kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet")
+  g3 --> t8("auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet")
+  g3 --> t9("ohne_druck_kbs_wird_nicht_umgerechnet")
+  g3 --> t10("druckrechteck_wird_ins_kbs_seines_layers_umgerechnet")
+  root --> g4["TestLayoutkarteImDruckKbs (1)"]
+  class g4 groupnode
+  g4 --> t11("hauptkarte_bekommt_das_kbs_vor_der_ausdehnung")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestTransformiereRechteck
test_verschiedene_kbs_werden_umgerechnet
Prüft, dass ein Rechteck bei verschiedenen KBS mit QgsCoordinateTransform von Quell- nach Ziel-KBS umgerechnet wird.grünnie geprüft
TestTransformiereRechteck
test_gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert
Prüft, dass bei gleichem, fehlendem oder ungültigem KBS nicht umgerechnet und das Rechteck unverändert geliefert wird.grünnie geprüft
TestTransformiereRechteck
test_fehler_bei_der_umrechnung_liefert_das_rechteck
Prüft, dass ein Fehler bei der Umrechnung nicht durchschlägt, sondern das ursprüngliche Rechteck zurückkommt.grünnie geprüft
TestZoomAufLayer
test_ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet
Prüft, dass zoom_auf_layer die Layer-Ausdehnung vom Layer-KBS in das KBS der Kartenansicht umrechnet, bevor gezoomt wird.grünnie geprüft
TestZoomAufLayer
test_ohne_layer_oder_kartenansicht_kein_zoom
Prüft, dass zoom_auf_layer ohne Layer oder ohne Kartenansicht nichts tut und False liefert.grünnie geprüft
TestDruckKbs
test_druck_kbs_ist_das_kbs_des_verfahrensgebiets
Prüft, dass die Layoutkarte im KBS des Verfahrensgebiet-Layers steht und ohne Verfahrensgebiet das Projekt-KBS gilt.grünnie geprüft
TestDruckKbs
test_kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet
Prüft, dass der Druckbereich „wie Kartenfenster“ vom KBS der Kartenansicht in das Druck-KBS umgerechnet wird.grünnie geprüft
TestDruckKbs
test_auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet
Prüft, dass der Druckbereich „Auswahl“ aus dem KBS des Auswahl-Layers umgerechnet und erst danach gepuffert wird.grünnie geprüft
TestDruckKbs
test_ohne_druck_kbs_wird_nicht_umgerechnet
Prüft, dass _resolve_extent ohne Druck-KBS wie bisher nicht umrechnet (Aufrufe ohne den neuen Parameter).grünnie geprüft
TestDruckKbs
test_druckrechteck_wird_ins_kbs_seines_layers_umgerechnet
Prüft, dass ein in der Kartenansicht gezeichnetes Druckrechteck vor dem Speichern in das KBS des Druckrechteck-Layers umgerechnet wird.grünnie geprüft
TestLayoutkarteImDruckKbs
test_hauptkarte_bekommt_das_kbs_vor_der_ausdehnung
Prüft, dass die Hauptkarte eines neuen Layouts das Druck-KBS erhält, bevor ihre Ausdehnung gesetzt wird.grünnie geprüft

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
   root(["test_excel_importer.py"])
   class root rootnode
   root --> g1["ImportXlsxTests (5)"]
@@ -731,7 +1167,52 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29

— (Docstring fehlt)

TestPrintLayout 8
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grüngeprüft am 2026-09-29

Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).

TestBerechneAnordnung 5TestOrdneKartenrandAn 5TestMindestbreiteBeimLaden 3TestAnbindungPrintLayout 2
flowchart TD
+  root(["test_print_kartenrand.py"])
+  class root rootnode
+  root --> g1["TestBerechneAnordnung (5)"]
+  class g1 groupnode
+  g1 --> t1("passt_einspaltig_nichts_aendert_sich")
+  g1 --> t2("mehr_spalten_und_breiterer_rand")
+  g1 --> t3("seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen")
+  g1 --> t4("seite_wird_nie_kleiner")
+  g1 --> t5("ohne_legenden_nur_kartenschild")
+  root --> g2["TestOrdneKartenrandAn (5)"]
+  class g2 groupnode
+  g2 --> t6("seite_hoeher_und_kartenschild_verschoben")
+  g2 --> t7("spalten_ungleich_breit_und_text_umgebrochen")
+  g2 --> t8("legenden_mindestens_so_breit_wie_kartenschild")
+  g2 --> t9("passt_nichts_wird_verschoben")
+  g2 --> t10("ohne_karte_keine_anordnung")
+  root --> g3["TestMindestbreiteBeimLaden (3)"]
+  class g3 groupnode
+  g3 --> t11("mindestbreite_nur_fuer_vermerkte_layouts")
+  g3 --> t12("listener_verbinden_und_trennen")
+  g3 --> t13("ohne_qgis_kein_listener")
+  root --> g4["TestAnbindungPrintLayout (2)"]
+  class g4 groupnode
+  g4 --> t14("vorlage_ordnet_kartenrand_an")
+  g4 --> t15("fehler_bei_der_anordnung_verhindert_die_vorlage_nicht")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestBerechneAnordnung
test_passt_einspaltig_nichts_aendert_sich
Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben.grünnie geprüft
TestBerechneAnordnung
test_mehr_spalten_und_breiterer_rand
Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird.grünnie geprüft
TestBerechneAnordnung
test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen
Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen.grünnie geprüft
TestBerechneAnordnung
test_seite_wird_nie_kleiner
Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen.grünnie geprüft
TestBerechneAnordnung
test_ohne_legenden_nur_kartenschild
Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird.grünnie geprüft
TestOrdneKartenrandAn
test_seite_hoeher_und_kartenschild_verschoben
Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen.grünnie geprüft
TestOrdneKartenrandAn
test_spalten_ungleich_breit_und_text_umgebrochen
Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96).grünnie geprüft
TestOrdneKartenrandAn
test_legenden_mindestens_so_breit_wie_kartenschild
Prüft, dass die Legenden die Breite des Kartenschilds als Mindestbreite bekommen und das Layout sie für das Neuladen vermerkt.grünnie geprüft
TestOrdneKartenrandAn
test_passt_nichts_wird_verschoben
Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.grünnie geprüft
TestOrdneKartenrandAn
test_ohne_karte_keine_anordnung
Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt.grünnie geprüft
TestMindestbreiteBeimLaden
test_mindestbreite_nur_fuer_vermerkte_layouts
Prüft, dass beim Laden die Mindestbreite nur an Layouts mit Vermerk wieder gesetzt wird.grünnie geprüft
TestMindestbreiteBeimLaden
test_listener_verbinden_und_trennen
Prüft, dass der Listener readProject einmal verbindet, beim Trennen wieder löst und beides mehrfach aufgerufen werden darf.grünnie geprüft
TestMindestbreiteBeimLaden
test_ohne_qgis_kein_listener
Prüft, dass im Mock-Modus kein Listener verbunden wird.grünnie geprüft
TestAnbindungPrintLayout
test_vorlage_ordnet_kartenrand_an
Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt.grünnie geprüft
TestAnbindungPrintLayout
test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grünnie geprüft

— (Docstring fehlt)

TestPrintLayout 8
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (8)"]
@@ -1280,7 +1761,7 @@
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
   classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatusgeprüft
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert.grüngeprüft am 2026-10-26
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert.grüngeprüft am 2026-10-26
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert.grüngeprüft am 2026-10-26
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist.grüngeprüft am 2026-10-26
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert.grüngeprüft am 2026-10-26
OnHandlerTests
test_on_project_changed_ohne_layer_setzt_variable_leer
Prüft, dass _on_project_changed die Variable leert, wenn kein Verfahrensgebiet-Layer existiert.grüngeprüft am 2026-10-26
OnHandlerTests
test_on_project_changed_mit_layer_setzt_variable
Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt.grüngeprüft am 2026-10-26
OnHandlerTests
test_on_layers_added_findet_neuen_layer
Prüft, dass _on_layers_added projektweit nach einem Layer namens „Verfahrensgebiet" sucht und die Variable aktualisiert.grüngeprüft am 2026-10-26
OnHandlerTests
test_on_layers_added_setzt_manuelle_auswahl_derzeit_zurueck
Prüft, dass _on_layers_added derzeit eine manuelle Auswahl eines anders benannten Layers auf leer zurücksetzt (Ist-Zustand, nicht bestätigt gewünscht).grüngeprüft am 2026-10-26
OnHandlerTests
test_on_layer_removed_bei_aktuellem_layer_sucht_neu
Prüft, dass das Entfernen des aktuell aktiven Verfahrensgebiet-Layers eine Neusuche auslöst.grüngeprüft am 2026-10-26
OnHandlerTests
test_on_layer_removed_bei_anderem_layer_aendert_nichts
Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst.grüngeprüft am 2026-10-26
ListenerSetupTests
test_setup_ohne_qgis_tut_nichts
Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert.grüngeprüft am 2026-10-26
ListenerSetupTests
test_setup_verbindet_signale_und_ist_idempotent
Prüft, dass setup_verfahrensgebiet_listener alle fünf Projektsignale verbindet und ein zweiter Aufruf nichts doppelt registriert.grüngeprüft am 2026-10-26
ListenerSetupTests
test_teardown_trennt_alle_signale_und_ist_idempotent
Prüft, dass teardown_verfahrensgebiet_listener alle fünf Signale trennt und mehrfacher Aufruf nicht fehlschlägt.grüngeprüft am 2026-10-26
-

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe docs/tests/testkatalog.md). beabsichtigt liest den Docstring der Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

DateiFunktion/MethodeZeileStatus
functions/dialog_wrapper.pyshow_info_dialog42beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_vg_laden192beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_abbrechen244beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_total339beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_value348beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_label361beabsichtigt
functions/dialog_wrapper.pyProgressDialog.is_canceled371beabsichtigt
functions/dialog_wrapper.pyProgressDialog.raise_to_front389beabsichtigt
functions/dialog_wrapper.pycreate_progress_dialog408beabsichtigt
functions/os_wrapper.pynormalize_path43beabsichtigt
functions/os_wrapper.pyget_home_dir55beabsichtigt
functions/os_wrapper.pyis_absolute_path64beabsichtigt
functions/os_wrapper.pybasename75beabsichtigt
functions/os_wrapper.pypath_suffix106beabsichtigt
functions/qgisui_wrapper.pyadd_toolbar204beabsichtigt
functions/qt_wrapper.pyexec_dialog60beabsichtigt
functions/qt_wrapper.pydebug_qt_status67beabsichtigt
modules/DataGrabber.pyDataGrabber.set_source68beabsichtigt
modules/DataGrabber.pyDataGrabber._process_database_source312beabsichtigt
modules/DataGrabber.pyDataGrabber._process_service_source324beabsichtigt
modules/LayerLoader.pyLayerLoader._was_canceled42beabsichtigt
modules/LayerLoader.pyLayerLoader._process_events51beabsichtigt
modules/Pruefmanager.pyPruefmanager.report_summary45beabsichtigt
modules/Pruefmanager.pyPruefmanager.zeige_hinweis63beabsichtigt
modules/Pruefmanager.pyPruefmanager._handle_datei_existiert87beabsichtigt
modules/Pruefmanager.pyPruefmanager._ask_use_or_replace_pufferlayer280beabsichtigt
modules/linkpruefer.pyLinkpruefer._ist_url41beabsichtigt
modules/linkpruefer.pyLinkpruefer._pruefe_url84beabsichtigt
modules/linkpruefer.pyLinkpruefer._pruefe_dateipfad121beabsichtigt
modules/print_layout.pyPrintLayout._create_map_item98beabsichtigt
modules/print_layout.pyPrintLayout._set_item_frame_stroke_width120beabsichtigt
modules/print_layout.pyPrintLayout._fit_extent_to_map_item156beabsichtigt
modules/print_layout.pyPrintLayout.add_text_label214beabsichtigt
modules/print_layout.pyPrintLayout._setup_layout_objects246beabsichtigt
modules/print_layout.pyPrintLayout._create_label_item378beabsichtigt
modules/print_layout.pyPrintLayout._resolve_visible_map_layers476beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_common_settings798beabsichtigt
modules/print_layout.pyPrintLayout._safe_grid_call805beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_annotation_text_style817beabsichtigt
modules/print_layout.pyPrintLayout._create_rectangle_box_item918beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe.create_baufreigabe_layout90beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._setup_layout_objects_baufreigabe264beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._calc_atlas_margin_percent398beabsichtigt
modules/print_logic.pyPrintLogic._failed_result123beabsichtigt
modules/print_logic.pyPrintLogic._show_target_size_too_small127beabsichtigt
modules/print_logic.pyPrintLogic._is_enabled_flag135beabsichtigt
modules/print_logic.pyPrintLogic._resolve_layer_from_project141beabsichtigt
modules/print_logic.pyPrintLogic._resolve_active_layer162beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_cm172beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_m181beabsichtigt
modules/print_logic.pyPrintLogic._import_qgis_symbol185beabsichtigt
modules/print_logic.pyPrintLogic._build_rectangle193beabsichtigt
modules/print_logic.pyPrintLogic._clone_extent203beabsichtigt
modules/print_logic.pyPrintLogic._get_existing_layer_by_name270beabsichtigt
modules/print_logic.pyPrintLogic._create_druckrechteck_layer284beabsichtigt
modules/print_logic.pyPrintLogic._clear_layer_features313beabsichtigt
modules/print_logic.pyPrintLogic.configure_atlas_for_baufreigabe_layout483beabsichtigt
modules/print_logic.pyPrintLogic._store_druckrechteck631beabsichtigt
modules/print_logic.pyPrintLogic.activate_druckrechteck_capture680beabsichtigt
modules/print_logic.pyPrintLogic._resolve_selection_layer811beabsichtigt
modules/print_logic.pyPrintLogic._get_selected_extent825beabsichtigt
modules/print_logic.pyPrintLogic._get_plot_dimensions_mm1078beabsichtigt
modules/print_logic.pyPrintLogic._get_required_map_dimensions_mm1088beabsichtigt
ui/tabs/print_tab.pyPrintTab._build_ui115beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_kartenname_changed335beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_massstab_changed349beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_zielgroesse_changed361beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_formfaktor_changed367beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_druckbereich_changed374beabsichtigt
ui/tabs/print_tab.pyPrintTab._format_druckpuffer_label432beabsichtigt
+

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe docs/tests/testkatalog.md). beabsichtigt liest den Docstring der Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

DateiFunktion/MethodeZeileStatus
functions/dialog_wrapper.pyshow_info_dialog42beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_vg_laden241beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_abbrechen293beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_total388beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_value397beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_label410beabsichtigt
functions/dialog_wrapper.pyProgressDialog.is_canceled420beabsichtigt
functions/dialog_wrapper.pyProgressDialog.raise_to_front438beabsichtigt
functions/dialog_wrapper.pycreate_progress_dialog457beabsichtigt
functions/os_wrapper.pynormalize_path43beabsichtigt
functions/os_wrapper.pyget_home_dir55beabsichtigt
functions/os_wrapper.pyis_absolute_path64beabsichtigt
functions/os_wrapper.pypath_suffix106beabsichtigt
functions/qgiscore_wrapper.py_crs_kennung1259ungeklärt
functions/qgiscore_wrapper.py_crs_gueltig1267ungeklärt
functions/qgisui_wrapper.pyadd_toolbar204beabsichtigt
functions/qt_wrapper.pyexec_dialog64beabsichtigt
functions/qt_wrapper.pydebug_qt_status71beabsichtigt
modules/DataGrabber.pyDataGrabber.set_source68beabsichtigt
modules/DataGrabber.pyDataGrabber._process_database_source312beabsichtigt
modules/DataGrabber.pyDataGrabber._process_service_source324beabsichtigt
modules/LayerLoader.pyLayerLoader._was_canceled42beabsichtigt
modules/LayerLoader.pyLayerLoader._process_events51beabsichtigt
modules/Pruefmanager.pyPruefmanager.report_summary45beabsichtigt
modules/Pruefmanager.pyPruefmanager.zeige_hinweis63beabsichtigt
modules/Pruefmanager.pyPruefmanager._handle_datei_existiert87beabsichtigt
modules/Pruefmanager.pyPruefmanager._ask_use_or_replace_pufferlayer280beabsichtigt
modules/basemap_kacheln.py_aufruf28ungeklärt
modules/basemap_kacheln.py_enum_int61ungeklärt
modules/basemap_kacheln.pyqgis_filterpruefer101ungeklärt
modules/basemap_kacheln.py_objekt_schluessel122ungeklärt
modules/basemap_katalog.py_sammle_bedingungen138ungeklärt
modules/basemap_katalog.py_slug177ungeklärt
modules/basemap_katalog.py_bezeichnung_aus_id184ungeklärt
modules/basemap_katalog.py_bezeichnung202ungeklärt
modules/basemap_katalog.pyeintrags_schluessel216ungeklärt
modules/basemap_katalog.py_ist_grundvariante235ungeklärt
modules/basemap_legende.pyKatalog.eintrag_fuer112ungeklärt
modules/basemap_legende.pyKatalog.sortierschluessel115ungeklärt
modules/basemap_legende.py_aufruf267ungeklärt
modules/basemap_legende.pyfinde_basemap_layer311ungeklärt
modules/basemap_legende_layout.py_enum_int79ungeklärt
modules/basemap_legende_layout.py_aufruf86ungeklärt
modules/basemap_legende_layout.pyfriere_zoom_ein131ungeklärt
modules/basemap_legende_layout.pybaue_symbole176ungeklärt
modules/basemap_legende_layout.py_ist_unsichtbar199ungeklärt
modules/basemap_legende_layout.py_rgba222ungeklärt
modules/basemap_legende_layout.py_ebenen_beschreiben226ungeklärt
modules/basemap_legende_layout.pyvorschau_icon259ungeklärt
modules/basemap_legende_layout.py_rechteck_mm316ungeklärt
modules/basemap_legende_layout.py_layout_messung341ungeklärt
modules/basemap_stilkorrektur.py_ist_null28ungeklärt
modules/basemap_stilkorrektur.py_korrigiere_symbol50ungeklärt
modules/basemap_stilvorlagen.pystandard_ordner47ungeklärt
modules/basemap_stilvorlagen.py_stilnamen58ungeklärt
modules/basemap_stilvorlagen.pylies_importdatei65ungeklärt
modules/basemap_stilvorlagen.pyBasemapStilvorlagen._pfade113ungeklärt
modules/basemap_stilvorlagen.pyBasemapStilvorlagen._metadaten117ungeklärt
modules/basemap_stilvorlagen.pyBasemapStilvorlagen.dateiname127ungeklärt
modules/basemap_themen.py_aufruf62ungeklärt
modules/basemap_themen.py_erfolg72ungeklärt
modules/basemap_themen.py_parameter101ungeklärt
modules/basemap_themen.py_normiert106ungeklärt
modules/basemap_themen.py_lade_themenstil183ungeklärt
modules/linkpruefer.pyLinkpruefer._ist_url41beabsichtigt
modules/linkpruefer.pyLinkpruefer._pruefe_url84beabsichtigt
modules/linkpruefer.pyLinkpruefer._pruefe_dateipfad121beabsichtigt
modules/print_kartenrand.py_aufruf119ungeklärt
modules/print_kartenrand.py_rechteck134ungeklärt
modules/print_kartenrand.py_legende_groesse139ungeklärt
modules/print_kartenrand.py_rendere_legenden149ungeklärt
modules/print_kartenrand.pysetze_legenden_mindestbreite156ungeklärt
modules/print_kartenrand.py_on_projekt_gelesen203ungeklärt
modules/print_kartenrand.py_verschiebe234ungeklärt
modules/print_layout.pyPrintLayout._create_map_item98beabsichtigt
modules/print_layout.pyPrintLayout._set_item_frame_stroke_width120beabsichtigt
modules/print_layout.pyPrintLayout._fit_extent_to_map_item156beabsichtigt
modules/print_layout.pyPrintLayout.add_text_label214beabsichtigt
modules/print_layout.pyPrintLayout._setup_layout_objects246beabsichtigt
modules/print_layout.pyPrintLayout._create_label_item378beabsichtigt
modules/print_layout.pyPrintLayout._resolve_visible_map_layers476beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_common_settings798beabsichtigt
modules/print_layout.pyPrintLayout._safe_grid_call805beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_annotation_text_style817beabsichtigt
modules/print_layout.pyPrintLayout._create_rectangle_box_item918beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe.create_baufreigabe_layout90beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._setup_layout_objects_baufreigabe274beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._calc_atlas_margin_percent408beabsichtigt
modules/print_logic.pyPrintLogic._failed_result125beabsichtigt
modules/print_logic.pyPrintLogic._show_target_size_too_small129beabsichtigt
modules/print_logic.pyPrintLogic._is_enabled_flag137beabsichtigt
modules/print_logic.pyPrintLogic._resolve_layer_from_project143beabsichtigt
modules/print_logic.pyPrintLogic._resolve_active_layer164beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_cm174beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_m183beabsichtigt
modules/print_logic.pyPrintLogic._build_rectangle195beabsichtigt
modules/print_logic.pyPrintLogic._clone_extent205beabsichtigt
modules/print_logic.pyPrintLogic._get_existing_layer_by_name272beabsichtigt
modules/print_logic.pyPrintLogic._create_druckrechteck_layer286beabsichtigt
modules/print_logic.pyPrintLogic.configure_atlas_for_baufreigabe_layout485beabsichtigt
modules/print_logic.pyPrintLogic.activate_druckrechteck_capture689beabsichtigt
modules/print_logic.pyPrintLogic._layer_crs943ungeklärt
modules/print_logic.pyPrintLogic._get_plot_dimensions_mm1134beabsichtigt
modules/print_logic.pyPrintLogic._get_required_map_dimensions_mm1144beabsichtigt
ui/base_dockwidget.py_inhalt23ungeklärt
ui/base_dockwidget.pytabs_mit_scrollbereich30ungeklärt
ui/tabs/print_tab.pyPrintTab._build_ui116beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_kartenname_changed340beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_massstab_changed354beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_zielgroesse_changed366beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_formfaktor_changed372beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_druckbereich_changed379beabsichtigt
ui/tabs/print_tab.pyPrintTab._format_druckpuffer_label437beabsichtigt
ui/widgets/basemap_bereich.pyBasemapBereich._build_ui61beabsichtigt
ui/widgets/basemap_bereich.pyBasemapBereich._ueberschrift164ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._vorlagen174ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._gewaehltes_thema195ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._zeige_thema198ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._umschalten207ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._aenderungen_klaeren241ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._ersetzen298ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._on_erzeugen_geaendert336ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._gewaehlte_vorlage370ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._on_stil_standard435ungeklärt
ui/widgets/basemap_bereich.pyBasemapBereich._on_stil_loeschen446ungeklärt
ui/widgets/basemap_bereich.py_thema_text500ungeklärt
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md index b3a1591..ba691f8 100644 --- a/docs/tests/testkatalog.md +++ b/docs/tests/testkatalog.md @@ -10,19 +10,19 @@ selbst (Docstrings) und aus der Testbaseline. Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`) Stand des Prüf-Audits: `docs/status/testpruefungen.json` (SHA-256 `2d7704ddcd6e`) -Gesamt: 3 Pakete, 55 Testdateien, 698 Testfälle – 655 grün, 35 rot (Baseline), 8 übersprungen. -Mit Beschreibung (Docstring): 553 von 698 (79 %). -Fachlich geprüft: 304 von 698 (44 %) aktuell, 0 veraltet seit Codeänderung, 394 nie geprüft. -Strukturelle Lücken (heuristisch, Namensabgleich): 160 von 470 (34 %) Funktionen/Methoden ohne erkennbaren Testbezug – Abschnitt „Ungetestete Funktionen“. +Gesamt: 3 Pakete, 66 Testdateien, 859 Testfälle – 816 grün, 35 rot (Baseline), 8 übersprungen. +Mit Beschreibung (Docstring): 714 von 859 (83 %). +Fachlich geprüft: 304 von 859 (35 %) aktuell, 0 veraltet seit Codeänderung, 555 nie geprüft. +Strukturelle Lücken (heuristisch, Namensabgleich): 216 von 634 (34 %) Funktionen/Methoden ohne erkennbaren Testbezug – Abschnitt „Ungetestete Funktionen“. ## Zusammenfassung | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung | fachlich geprüft | ohne Testbezug | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | -| `sn_basis` | 32 | 365 | 365 | 0 | 0 | 365 von 365 (100 %) | 304 von 365 (83 %) | 70 von 229 (31 %) | +| `sn_basis` | 43 | 526 | 526 | 0 | 0 | 526 von 526 (100 %) | 304 von 526 (58 %) | 126 von 393 (32 %) | | `sn_verfahrensgebiet` | 18 | 289 | 247 | 34 | 8 | 187 von 289 (65 %) | 0 von 289 (0 %) | 51 von 161 (32 %) | | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) | 0 von 44 (0 %) | 39 von 80 (49 %) | -| **Summe** | **55** | **698** | **655** | **35** | **8** | **553 von 698 (79 %)** | **304 von 698 (44 %)** | **160 von 470 (34 %)** | +| **Summe** | **66** | **859** | **816** | **35** | **8** | **714 von 859 (83 %)** | **304 von 859 (35 %)** | **216 von 634 (34 %)** | ### Legende @@ -36,15 +36,16 @@ Strukturelle Lücken (heuristisch, Namensabgleich): 160 von 470 (34 %) Funktione ## Paket `sn_basis` -Plattform-Plugin, Ordner `Plugin_SN_Basis` – 32 Testdateien mit 365 Testfällen (365 grün, 0 rot, 0 übersprungen). +Plattform-Plugin, Ordner `Plugin_SN_Basis` – 43 Testdateien mit 526 Testfällen (526 grün, 0 rot, 0 übersprungen). ### `Plugin_SN_Basis/tests/test_base_dockwidget.py` -Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65). +Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104). Testgruppen: - `CloseEventTests` (5 Testfälle): — (Docstring fehlt) +- `ScrollbereichTests` (5 Testfälle): Lange Tabs dürfen das QGIS-Fenster nicht über den Bildschirmrand schieben (Basis #104). | Testfall | prüft | Status | geprüft | | --- | --- | --- | --- | @@ -53,6 +54,263 @@ Testgruppen: | `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün | geprüft am 2026-09-21 | | `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün | geprüft am 2026-09-21 | | `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün | geprüft am 2026-09-21 | +| `ScrollbereichTests.test_jeder_tab_liegt_in_einem_scrollbereich` | Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern. | grün | nie geprüft | +| `ScrollbereichTests.test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich` | Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt. | grün | nie geprüft | +| `ScrollbereichTests.test_scrollbereich_wird_nicht_doppelt_angelegt` | Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird. | grün | nie geprüft | +| `ScrollbereichTests.test_setwidget_none_und_andere_widgets_bleiben_unveraendert` | Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden. | grün | nie geprüft | +| `ScrollbereichTests.test_cleanup_erreicht_tabs_im_scrollbereich` | Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_anbindung.py` + +Worum es in der Datei geht: Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88): + +Testgruppen: + +- `TestMehrfachauswahl` (1 Testfälle): — (Docstring fehlt) +- `TestDruckanbindung` (4 Testfälle): — (Docstring fehlt) +- `TestBasemapBereich` (11 Testfälle): — (Docstring fehlt) +- `TestStilvorlagenAustausch` (5 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestMehrfachauswahl.test_mock_modus_liefert_vorauswahl_in_listenreihenfolge` | Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert. | grün | nie geprüft | +| `TestDruckanbindung.test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter` | Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist. | grün | nie geprüft | +| `TestDruckanbindung.test_layout_wird_markiert_und_legende_erzeugt` | Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt. | grün | nie geprüft | +| `TestDruckanbindung.test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün | nie geprüft | +| `TestDruckanbindung.test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden` | Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut. | grün | nie geprüft | +| `TestBasemapBereich.test_auswertebereich_wird_gespeichert` | Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet. | grün | nie geprüft | +| `TestBasemapBereich.test_aktualisieren_ohne_vorlage_warnt` | Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet. | grün | nie geprüft | +| `TestBasemapBereich.test_aktualisieren_wertet_gewaehlte_vorlage_aus` | Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet. | grün | nie geprüft | +| `TestBasemapBereich.test_stil_laden_uebernimmt_legendenauswahl` | Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt. | grün | nie geprüft | +| `TestBasemapBereich.test_stil_laden_bricht_bei_fremder_vorlage_ab` | Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird. | grün | nie geprüft | +| `TestBasemapBereich.test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt` | Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird. | grün | nie geprüft | +| `TestBasemapBereich.test_speichern_mit_aktueller_legendenauswahl` | Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird. | grün | nie geprüft | +| `TestBasemapBereich.test_punktlinien_korrigieren_meldet_anzahl` | Prüft, dass „Punktlinien korrigieren“ den Basemap-Layer korrigiert und die Anzahl korrigierter Styles meldet. | grün | nie geprüft | +| `TestBasemapBereich.test_punktlinien_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer nichts korrigiert, sondern gewarnt wird. | grün | nie geprüft | +| `TestBasemapBereich.test_stil_speichern_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird. | grün | nie geprüft | +| `TestBasemapBereich.test_stil_laden_bricht_bei_fremdem_thema_ab` | Prüft, dass eine Vorlage eines anderen Themas (Grau auf Farbe) nur nach Bestätigung geladen wird. | grün | nie geprüft | +| `TestStilvorlagenAustausch.test_export_schreibt_gewaehlte_vorlage` | Prüft, dass „Exportieren …“ die gewählte Vorlage in die im Dateidialog gewählte Datei schreibt und den Pfad meldet. | grün | nie geprüft | +| `TestStilvorlagenAustausch.test_export_abgebrochen_oder_ohne_vorlage` | Prüft, dass ohne gewählte Vorlage oder bei abgebrochenem Dateidialog nichts exportiert wird. | grün | nie geprüft | +| `TestStilvorlagenAustausch.test_import_uebernimmt_datei_unter_gewaehltem_namen` | Prüft, dass „Importieren …“ den Namen aus der Datei vorschlägt, unter dem gewählten Namen importiert und die neue Vorlage auswählt. | grün | nie geprüft | +| `TestStilvorlagenAustausch.test_import_ersetzt_vorhandene_nur_nach_rueckfrage` | Prüft, dass ein Import eine gleichnamige Vorlage ohne Bestätigung nicht überschreibt. | grün | nie geprüft | +| `TestStilvorlagenAustausch.test_import_ungeeigneter_datei_warnt` | Prüft, dass eine ungeeignete Importdatei gemeldet wird, ohne nach einem Namen zu fragen oder etwas zu importieren. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_kacheln.py` + +Worum es in der Datei geht: Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88). + +Testgruppen: + +- `TestSammleObjekte` (4 Testfälle): — (Docstring fehlt) +- `TestZoomUndStile` (2 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestSammleObjekte.test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt` | Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden. | grün | nie geprüft | +| `TestSammleObjekte.test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen` | Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden. | grün | nie geprüft | +| `TestSammleObjekte.test_abbruch_liefert_none_und_hebt_auswahl_auf` | Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird. | grün | nie geprüft | +| `TestSammleObjekte.test_anzahl_kacheln_aus_kachelbereich` | Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird. | grün | nie geprüft | +| `TestZoomUndStile.test_zoomstufen_wie_im_qgis_renderer` | Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden. | grün | nie geprüft | +| `TestZoomUndStile.test_stile_aus_layer_liest_renderer_styles` | Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_katalog.py` + +Worum es in der Datei geht: Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88). + +Testgruppen: + +- `TestRendererStil` (3 Testfälle): — (Docstring fehlt) +- `TestErzeugeKatalog` (6 Testfälle): — (Docstring fehlt) +- `TestSymbolwahlUndPruefung` (3 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestRendererStil.test_beschriftung_und_3d_sind_keine_renderer_stile` | Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon. | grün | nie geprüft | +| `TestRendererStil.test_ausschluss_ueber_liste_und_praefix` | Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden. | grün | nie geprüft | +| `TestRendererStil.test_normalisierung_entfernt_variantenkennungen` | Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen. | grün | nie geprüft | +| `TestErzeugeKatalog.test_kontur_decker_und_bruecke_bilden_einen_eintrag` | Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden. | grün | nie geprüft | +| `TestErzeugeKatalog.test_zustand_im_bau_wird_eigener_eintrag` | Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt. | grün | nie geprüft | +| `TestErzeugeKatalog.test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst` | Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet. | grün | nie geprüft | +| `TestErzeugeKatalog.test_ausgeschlossene_stile_fehlen_im_katalog` | Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden. | grün | nie geprüft | +| `TestErzeugeKatalog.test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten` | Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben. | grün | nie geprüft | +| `TestErzeugeKatalog.test_entfallene_style_ids_werden_gemeldet` | Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird. | grün | nie geprüft | +| `TestSymbolwahlUndPruefung.test_symbol_stile_je_rolle_nur_grundvariante` | Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird. | grün | nie geprüft | +| `TestSymbolwahlUndPruefung.test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole` | Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet. | grün | nie geprüft | +| `TestSymbolwahlUndPruefung.test_ausgelieferter_katalog_ist_konsistent` | Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_legende.py` + +Worum es in der Datei geht: Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88). + +Testgruppen: + +- `TestStileUndEintraege` (4 Testfälle): — (Docstring fehlt) +- `TestAuswahlUndEinstellungen` (6 Testfälle): — (Docstring fehlt) +- `TestLayerErkennung` (2 Testfälle): — (Docstring fehlt) +- `TestAblauf` (6 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestStileUndEintraege.test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich` | Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt). | grün | nie geprüft | +| `TestStileUndEintraege.test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter` | Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe. | grün | nie geprüft | +| `TestStileUndEintraege.test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe` | Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind. | grün | nie geprüft | +| `TestStileUndEintraege.test_ausgeschlossene_und_unbekannte_styles` | Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_vorauswahl_bekannte_an_unbekannte_aus` | Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_auswahl_wird_gemerkt_und_vorbelegt` | Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_merken_ergaenzt_die_bisherige_auswahl` | Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_kaputte_projektvariable_ergibt_leere_auswahl` | Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_auswertebereich_standard_und_umstellung` | Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden. | grün | nie geprüft | +| `TestAuswahlUndEinstellungen.test_legende_erzeugen_standard_an` | Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann. | grün | nie geprüft | +| `TestLayerErkennung.test_erkennung_ueber_quelle_und_namen` | Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen. | grün | nie geprüft | +| `TestLayerErkennung.test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum` | Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen. | grün | nie geprüft | +| `TestAblauf.test_ohne_basemap_layer_passiert_nichts` | Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen. | grün | nie geprüft | +| `TestAblauf.test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt` | Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird. | grün | nie geprüft | +| `TestAblauf.test_abbruch_im_dialog_aendert_nichts` | Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert. | grün | nie geprüft | +| `TestAblauf.test_rueckfall_auf_druckbereich_wird_gemeldet` | Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird. | grün | nie geprüft | +| `TestAblauf.test_ohne_objekte_keine_legende` | Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen. | grün | nie geprüft | +| `TestAblauf.test_viele_kacheln_werden_vorher_bestaetigt` | Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_legende_layout.py` + +Worum es in der Datei geht: Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88). + +Testgruppen: + +- `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt) +- `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt) +- `TestAutobahnSymbol` (3 Testfälle): Autobahn-Symbol aus dem ausgelieferten Katalog (Basis #98). +- `TestHilfslayer` (3 Testfälle): — (Docstring fehlt) +- `TestPlatzierung` (7 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestKennzeichenUndKarte.test_layout_kennzeichen_setzen_und_erkennen` | Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird. | grün | nie geprüft | +| `TestKennzeichenUndKarte.test_hauptkarte_ueber_id` | Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird. | grün | nie geprüft | +| `TestSymbolwahl.test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge` | Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden. | grün | nie geprüft | +| `TestSymbolwahl.test_ohne_aktive_symbol_stile_erster_getroffener_style` | Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert. | grün | nie geprüft | +| `TestAutobahnSymbol.test_druckmassstab_ab_zoom_14_blauer_decker_auf_grauer_kontur` | Prüft, dass das Autobahn-Symbol ab Zoom 14 aus grauer Kontur und blauem Decker der Fahrbahnachse besteht statt nur aus einem grauen Style. | grün | nie geprüft | +| `TestAutobahnSymbol.test_zoom_11_bis_13_bisherige_kontur_und_decker` | Prüft, dass das Autobahn-Symbol bei Zoom 11 bis 13 weiterhin aus Kontur und Decker der getrennten Fahrbahn besteht. | grün | nie geprüft | +| `TestAutobahnSymbol.test_unter_zoom_11_blauer_decker` | Prüft, dass das Autobahn-Symbol unter Zoom 11 den blauen Decker der Übersichtsdarstellung nutzt. | grün | nie geprüft | +| `TestHilfslayer.test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer` | Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen. | grün | nie geprüft | +| `TestHilfslayer.test_alte_hilfslayer_werden_entfernt` | Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden. | grün | nie geprüft | +| `TestHilfslayer.test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt` | Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird. | grün | nie geprüft | +| `TestPlatzierung.test_einspaltig_ueber_der_quellenangabe` | Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht. | grün | nie geprüft | +| `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün | nie geprüft | +| `TestPlatzierung.test_spalten_ungleich_breit` | Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96). | grün | nie geprüft | +| `TestPlatzierung.test_mindestgroesse_mit_ungleichen_spalten_und_umbruch` | Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96). | grün | nie geprüft | +| `TestPlatzierung.test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild` | Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt. | grün | nie geprüft | +| `TestPlatzierung.test_titel_zweizeilig` | Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96). | grün | nie geprüft | +| `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_legende_rand.py` + +Worum es in der Datei geht: Tests für den Legendenrand weißer Basemap-Flächen (Basis #92). + +Testgruppen: + +- `TestBrauchtLegendenrand` (4 Testfälle): — (Docstring fehlt) +- `TestErgaenzeLegendenrand` (2 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestBrauchtLegendenrand.test_weisse_flaeche_ohne_rand` | Prüft, dass eine weiße Fläche ohne Umrandung einen Legendenrand braucht. | grün | nie geprüft | +| `TestBrauchtLegendenrand.test_farbige_flaeche_oder_flaeche_mit_kontur` | Prüft, dass farbige Flächen und weiße Flächen mit dunkler Kontur oder eigener Konturlinie keinen Rand bekommen. | grün | nie geprüft | +| `TestBrauchtLegendenrand.test_heller_rand_und_fast_weiss_zaehlen_als_unsichtbar` | Prüft, dass fast weiße Füllungen und helle oder durchsichtige Ränder als unsichtbar gelten. | grün | nie geprüft | +| `TestBrauchtLegendenrand.test_andere_ebenen_und_leeres_symbol` | Prüft, dass Symbole mit anderen Ebenen (Muster, Punkte) und leere Symbole keinen Rand bekommen. | grün | nie geprüft | +| `TestErgaenzeLegendenrand.test_weisse_flaeche_bekommt_grauen_rand` | Prüft, dass eine weiße Fläche ohne Kontur eine graue Randlinie als oberste Ebene bekommt. | grün | nie geprüft | +| `TestErgaenzeLegendenrand.test_farbige_flaeche_und_linien_bleiben_unveraendert` | Prüft, dass farbige Flächen, Flächen mit Kontur und Liniensymbole keinen Rand bekommen. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_stilkorrektur.py` + +Worum es in der Datei geht: Tests für modules/basemap_stilkorrektur.py – Punktlinien des Basemap-Layers sichtbar machen (Basis #90). + +Testgruppen: + +- `TestMusterUndAusdruck` (2 Testfälle): — (Docstring fehlt) +- `TestLayerkorrektur` (4 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestMusterUndAusdruck.test_strichlaenge_null_im_festen_muster` | Prüft, dass Strichlängen 0 (gerade Positionen) im Strichmuster ersetzt und Lücken der Länge 0 nicht verändert werden. | grün | nie geprüft | +| `TestMusterUndAusdruck.test_strichlaenge_null_im_zoomabhaengigen_ausdruck` | Prüft, dass jede Strichlänge 0 in den array(...)-Mustern des Konverter-Ausdrucks ersetzt wird und der übrige Ausdruck gleich bleibt. | grün | nie geprüft | +| `TestLayerkorrektur.test_punktlinien_werden_korrigiert_andere_styles_nicht` | Prüft, dass nur Styles mit Strichlänge 0 korrigiert werden – Muster und Ausdruck – und die Anzahl zurückkommt. | grün | nie geprüft | +| `TestLayerkorrektur.test_zweiter_lauf_korrigiert_nichts` | Prüft, dass ein zweiter Lauf keine Styles mehr korrigiert und nicht neu zeichnen lässt. | grün | nie geprüft | +| `TestLayerkorrektur.test_nur_ausdruck_ohne_festes_muster` | Prüft, dass auch ein Style korrigiert wird, dessen Strichlänge 0 nur im zoomabhängigen Ausdruck steht. | grün | nie geprüft | +| `TestLayerkorrektur.test_layer_ohne_renderer` | Prüft, dass ein Layer ohne Renderer 0 korrigierte Styles liefert. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_stilvorlagen.py` + +Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88), + +Testgruppen: + +- `TestStilvorlagen` (8 Testfälle): — (Docstring fehlt) +- `TestExportImport` (6 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestStilvorlagen.test_speichern_und_auflisten` | Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird. | grün | nie geprüft | +| `TestStilvorlagen.test_mehrere_varianten_alphabetisch` | Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden. | grün | nie geprüft | +| `TestStilvorlagen.test_leerer_name_oder_fehler_beim_schreiben` | Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden. | grün | nie geprüft | +| `TestStilvorlagen.test_laden_setzt_stil_und_zeichnet_neu` | Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt. | grün | nie geprüft | +| `TestStilvorlagen.test_passung_meldet_fremde_styles` | Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt. | grün | nie geprüft | +| `TestStilvorlagen.test_standard_setzen_und_beim_loeschen_entfernen` | Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt. | grün | nie geprüft | +| `TestStilvorlagen.test_fehlende_begleitdatei_wird_toleriert` | Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird. | grün | nie geprüft | +| `TestStilvorlagen.test_ohne_ordner_leere_liste` | Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt. | grün | nie geprüft | +| `TestExportImport.test_export_und_import_uebertragen_vorlage_mit_begleitdaten` | Prüft, dass eine exportierte Vorlage auf einem anderen Rechner mit Anzeigename, Thema, Style-Namen und Legendenauswahl ankommt. | grün | nie geprüft | +| `TestExportImport.test_export_einer_fehlenden_vorlage` | Prüft, dass der Export einer nicht vorhandenen Vorlage als ValueError gemeldet wird und keine Datei entsteht. | grün | nie geprüft | +| `TestExportImport.test_import_einer_reinen_qml_datei` | Prüft, dass eine QML-Datei ohne Begleitdatei mit dem Dateinamen als Namensvorschlag und ohne Thema übernommen wird. | grün | nie geprüft | +| `TestExportImport.test_import_lehnt_ungeeignete_dateien_ab` | Prüft, dass Dateien ohne QGIS-Stil und ZIP-Dateien ohne genau eine QML-Datei abgelehnt werden, ohne etwas anzulegen. | grün | nie geprüft | +| `TestExportImport.test_import_uebernimmt_nur_bekannte_begleitdaten` | Prüft, dass ein Import unbekannte oder falsch typisierte Angaben der Begleitdatei verwirft und den gewählten Namen nutzt. | grün | nie geprüft | +| `TestExportImport.test_thema_der_vorlage_passt_nicht_zum_layer` | Prüft, dass ein abweichendes Thema der Vorlage gemeldet wird und ohne Thema in Vorlage oder Layer keine Meldung kommt. | grün | nie geprüft | + +### `Plugin_SN_Basis/tests/test_basemap_themen.py` + +Worum es in der Datei geht: Tests für modules/basemap_themen.py und den Abschnitt „Basemap-Layer“ im Basemap-Bereich (Basis #100): + +Testgruppen: + +- `TestQuelleUndThema` (4 Testfälle): Layerquelle der Themen, Thema eines Layers und Erkennung über die Kachel-URL. +- `TestPruefwert` (3 Testfälle): Prüfwert der Darstellung und Änderungsstand für ungespeicherte Änderungen. +- `TestLayerUndThemenwechsel` (7 Testfälle): Basemap-Layer anlegen, in den Layerbaum einfügen und Thema wechseln. +- `TestStilvorlageMitThema` (1 Testfälle): Stilvorlagen vermerken das Thema des Layers. +- `TestAuswahlDialog` (1 Testfälle): Rückfrage mit frei benannten Knöpfen. +- `TestBasemapHinzufuegen` (5 Testfälle): Knopf „Basemap hinzufügen“ mit Rückfrage bei vorhandenem Layer gleicher Quelle. +- `TestThemaWechseln` (8 Testfälle): Themenauswahl mit Rückfrage bei geänderter oder unbekannter Darstellung. +- `TestZielLayer` (1 Testfälle): Auswahl des Basemap-Layers, auf den Thema und Stil wirken. + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestQuelleUndThema.test_quelle_enthaelt_kachel_url_und_style_des_themas` | Prüft, dass die Layerquelle eines Themas die gemeinsame Kachel-URL und die Style-JSON des Themas enthält. | grün | nie geprüft | +| `TestQuelleUndThema.test_unbekannte_style_json_ergibt_kein_thema` | Prüft, dass ein Layer mit fremder oder fehlender Style-JSON keinem Thema zugeordnet wird. | grün | nie geprüft | +| `TestQuelleUndThema.test_unbekannter_schluessel_faellt_auf_farbe_zurueck` | Prüft, dass ein unbekannter Themenschlüssel auf das Standardthema „Farbe“ zurückfällt. | grün | nie geprüft | +| `TestQuelleUndThema.test_vorhandener_layer_wird_an_der_quell_url_erkannt` | Prüft, dass ein vorhandener Basemap-Layer an der Kachel-URL erkannt wird – unabhängig von Name, Thema und URL-Kodierung. | grün | nie geprüft | +| `TestPruefwert.test_gleiche_darstellung_gleicher_pruefwert_auch_bei_anderer_qgis_version` | Prüft, dass dieselbe Darstellung denselben Prüfwert ergibt, auch wenn die QGIS-Version in der QML abweicht. | grün | nie geprüft | +| `TestPruefwert.test_ohne_ausgabe_der_darstellung_kein_pruefwert` | Prüft, dass kein Prüfwert entsteht, wenn der Layer seine Darstellung nicht ausgeben kann. | grün | nie geprüft | +| `TestPruefwert.test_aenderungsstand_unbekannt_unveraendert_geaendert` | Prüft, dass der Änderungsstand ohne gemerkten Prüfwert „unbekannt“ ist, nach dem Merken „unverändert“ und nach einer Änderung „geändert“. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_neuer_layer_hat_thema_namen_und_gemerkten_stand` | Prüft, dass ein neuer Basemap-Layer im gewählten Thema entsteht, den Themenstil lädt und als unverändert gilt. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_ungueltiger_layer_oder_fehlender_themenstil_meldet_fehler` | Prüft, dass ein ungültiger Layer oder ein nicht ladbarer Themenstil als ValueError gemeldet wird. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_ohne_vektorkacheln_kein_layer` | Prüft, dass ohne Vektorkachel-Unterstützung (Mock-Modus) kein Layer erzeugt, sondern ein Fehler gemeldet wird. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_layer_kommt_ganz_unten_in_den_layerbaum` | Prüft, dass der Basemap-Layer ohne automatische Einordnung ins Projekt kommt und unten an den Layerbaum angehängt wird. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_themenwechsel_stellt_quelle_um_und_merkt_neuen_stand` | Prüft, dass der Themenwechsel die Style-JSON der Quelle umstellt, den Themenstil lädt, den automatischen Namen anpasst und den neuen Stand merkt. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_themenwechsel_behaelt_eigenen_layernamen` | Prüft, dass ein vom Nutzer vergebener Layername beim Themenwechsel erhalten bleibt. | grün | nie geprüft | +| `TestLayerUndThemenwechsel.test_themenwechsel_ohne_umstellbare_quelle_meldet_fehler` | Prüft, dass ein Layer ohne setDataSource den Themenwechsel mit einem ValueError ablehnt. | grün | nie geprüft | +| `TestStilvorlageMitThema.test_stilvorlage_vermerkt_das_thema` | Prüft, dass eine gespeicherte Stilvorlage das Thema des Layers in ihrer Begleitdatei und in der Liste vermerkt. | grün | nie geprüft | +| `TestAuswahlDialog.test_mock_modus_liefert_vorbelegung` | Prüft, dass die Rückfrage mit frei benannten Knöpfen im Mock-Modus den vorbelegten Schlüssel liefert. | grün | nie geprüft | +| `TestBasemapHinzufuegen.test_ohne_vorhandenen_layer_wird_ohne_rueckfrage_hinzugefuegt` | Prüft, dass ohne Basemap-Layer gleicher Quelle sofort ein Layer im gewählten Thema hinzugefügt wird. | grün | nie geprüft | +| `TestBasemapHinzufuegen.test_vorhandener_layer_wird_in_der_rueckfrage_genannt_abbrechen` | Prüft, dass ein vorhandener Layer gleicher Quelle in der Rückfrage genannt wird und „Abbrechen“ nichts verändert. | grün | nie geprüft | +| `TestBasemapHinzufuegen.test_zusaetzlich_laden_fuegt_zweiten_layer_hinzu` | Prüft, dass „Zusätzlich laden“ einen weiteren Basemap-Layer neben dem vorhandenen hinzufügt. | grün | nie geprüft | +| `TestBasemapHinzufuegen.test_ersetzen_laedt_vorhandenen_layer_im_gewaehlten_thema` | Prüft, dass „Ersetzen“ keinen neuen Layer anlegt, sondern den vorhandenen im gewählten Thema neu lädt. | grün | nie geprüft | +| `TestBasemapHinzufuegen.test_fehler_beim_anlegen_wird_gemeldet` | Prüft, dass ein nicht erreichbarer Dienst als Warnung gemeldet und kein Layer hinzugefügt wird. | grün | nie geprüft | +| `TestThemaWechseln.test_unveraenderter_layer_wechselt_ohne_rueckfrage` | Prüft, dass ein unveränderter Basemap-Layer ohne Rückfrage auf das gewählte Thema wechselt. | grün | nie geprüft | +| `TestThemaWechseln.test_gleiches_thema_aendert_nichts` | Prüft, dass die Wahl des Themas, das der Layer schon hat, nichts neu lädt. | grün | nie geprüft | +| `TestThemaWechseln.test_geaenderte_darstellung_abbrechen_behaelt_thema` | Prüft, dass bei geänderter Darstellung nachgefragt wird und „Abbrechen“ Thema und Darstellung unverändert lässt. | grün | nie geprüft | +| `TestThemaWechseln.test_geaenderte_darstellung_verwerfen_wechselt` | Prüft, dass „Verwerfen“ die geänderte Darstellung aufgibt und das Thema wechselt. | grün | nie geprüft | +| `TestThemaWechseln.test_als_stil_speichern_sichert_vor_dem_wechsel` | Prüft, dass „Als Stil speichern“ die Darstellung vor dem Themenwechsel als Stilvorlage sichert. | grün | nie geprüft | +| `TestThemaWechseln.test_abgebrochenes_speichern_verhindert_den_wechsel` | Prüft, dass das Thema nicht wechselt, wenn „Als Stil speichern“ gewählt, die Namensabfrage aber abgebrochen wird. | grün | nie geprüft | +| `TestThemaWechseln.test_unbekannter_stand_fragt_nach` | Prüft, dass bei einem Layer ohne gemerkten Prüfwert (vor dieser Funktion geladen) vorsichtshalber nachgefragt wird. | grün | nie geprüft | +| `TestThemaWechseln.test_ohne_basemap_layer_nur_vorauswahl` | Prüft, dass die Themenwahl ohne Basemap-Layer nur als Vorauswahl für „Basemap hinzufügen“ dient und nichts meldet. | grün | nie geprüft | +| `TestZielLayer.test_ausgewaehlter_basemap_layer_hat_vorrang` | Prüft, dass der im Layerbaum ausgewählte Basemap-Layer Ziel des Themenwechsels ist, sonst der erste Basemap-Layer. | grün | nie geprüft | ### `Plugin_SN_Basis/tests/test_bootstrap.py` @@ -196,6 +454,31 @@ Testgruppen: | `DockManagerShowTests.test_fehler_beim_entfernen_wird_als_warnung_gemeldet` | Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird. | grün | geprüft am 2026-09-21 | | `DockManagerShowTests.test_fehler_beim_anzeigen_wird_gemeldet` | Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird. | grün | geprüft am 2026-09-21 | +### `Plugin_SN_Basis/tests/test_druck_kbs.py` + +Worum es in der Datei geht: Tests zu Basis #99: Ist das Projekt-KBS ein anderes als das KBS des Verfahrensgebiets, müssen + +Testgruppen: + +- `TestTransformiereRechteck` (3 Testfälle): Umrechnung eines Rechtecks zwischen zwei KBS. +- `TestZoomAufLayer` (2 Testfälle): Zoom der Kartenansicht auf einen Layer in einem anderen KBS. +- `TestDruckKbs` (5 Testfälle): KBS der Layoutkarte und Umrechnung der Druckbereiche. +- `TestLayoutkarteImDruckKbs` (1 Testfälle): Die Layoutkarte bekommt das Druck-KBS, bevor die Ausdehnung gesetzt wird. + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestTransformiereRechteck.test_verschiedene_kbs_werden_umgerechnet` | Prüft, dass ein Rechteck bei verschiedenen KBS mit QgsCoordinateTransform von Quell- nach Ziel-KBS umgerechnet wird. | grün | nie geprüft | +| `TestTransformiereRechteck.test_gleiches_fehlendes_oder_ungueltiges_kbs_laesst_rechteck_unveraendert` | Prüft, dass bei gleichem, fehlendem oder ungültigem KBS nicht umgerechnet und das Rechteck unverändert geliefert wird. | grün | nie geprüft | +| `TestTransformiereRechteck.test_fehler_bei_der_umrechnung_liefert_das_rechteck` | Prüft, dass ein Fehler bei der Umrechnung nicht durchschlägt, sondern das ursprüngliche Rechteck zurückkommt. | grün | nie geprüft | +| `TestZoomAufLayer.test_ausdehnung_wird_ins_kbs_der_kartenansicht_umgerechnet` | Prüft, dass zoom_auf_layer die Layer-Ausdehnung vom Layer-KBS in das KBS der Kartenansicht umrechnet, bevor gezoomt wird. | grün | nie geprüft | +| `TestZoomAufLayer.test_ohne_layer_oder_kartenansicht_kein_zoom` | Prüft, dass zoom_auf_layer ohne Layer oder ohne Kartenansicht nichts tut und False liefert. | grün | nie geprüft | +| `TestDruckKbs.test_druck_kbs_ist_das_kbs_des_verfahrensgebiets` | Prüft, dass die Layoutkarte im KBS des Verfahrensgebiet-Layers steht und ohne Verfahrensgebiet das Projekt-KBS gilt. | grün | nie geprüft | +| `TestDruckKbs.test_kartenfenster_wird_vom_kbs_der_kartenansicht_umgerechnet` | Prüft, dass der Druckbereich „wie Kartenfenster“ vom KBS der Kartenansicht in das Druck-KBS umgerechnet wird. | grün | nie geprüft | +| `TestDruckKbs.test_auswahl_wird_vor_dem_puffern_vom_layer_kbs_umgerechnet` | Prüft, dass der Druckbereich „Auswahl“ aus dem KBS des Auswahl-Layers umgerechnet und erst danach gepuffert wird. | grün | nie geprüft | +| `TestDruckKbs.test_ohne_druck_kbs_wird_nicht_umgerechnet` | Prüft, dass _resolve_extent ohne Druck-KBS wie bisher nicht umrechnet (Aufrufe ohne den neuen Parameter). | grün | nie geprüft | +| `TestDruckKbs.test_druckrechteck_wird_ins_kbs_seines_layers_umgerechnet` | Prüft, dass ein in der Kartenansicht gezeichnetes Druckrechteck vor dem Speichern in das KBS des Druckrechteck-Layers umgerechnet wird. | grün | nie geprüft | +| `TestLayoutkarteImDruckKbs.test_hauptkarte_bekommt_das_kbs_vor_der_ausdehnung` | Prüft, dass die Hauptkarte eines neuen Layouts das Druck-KBS erhält, bevor ihre Ausdehnung gesetzt wird. | grün | nie geprüft | + ### `Plugin_SN_Basis/tests/test_excel_importer.py` Worum es in der Datei geht: Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63). @@ -438,6 +721,35 @@ Testgruppen: | `IsCaseSensitiveFsTests.test_macos_ist_nicht_case_sensitiv` | Prüft, dass macOS als nicht case-sensitives Dateisystem gilt. | grün | geprüft am 2026-09-29 | | `IsCaseSensitiveFsTests.test_linux_ist_case_sensitiv` | Prüft, dass Linux als case-sensitives Dateisystem gilt. | grün | geprüft am 2026-09-29 | +### `Plugin_SN_Basis/tests/test_print_kartenrand.py` + +Worum es in der Datei geht: Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93). + +Testgruppen: + +- `TestBerechneAnordnung` (5 Testfälle): — (Docstring fehlt) +- `TestOrdneKartenrandAn` (5 Testfälle): — (Docstring fehlt) +- `TestMindestbreiteBeimLaden` (3 Testfälle): — (Docstring fehlt) +- `TestAnbindungPrintLayout` (2 Testfälle): — (Docstring fehlt) + +| Testfall | prüft | Status | geprüft | +| --- | --- | --- | --- | +| `TestBerechneAnordnung.test_passt_einspaltig_nichts_aendert_sich` | Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben. | grün | nie geprüft | +| `TestBerechneAnordnung.test_mehr_spalten_und_breiterer_rand` | Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird. | grün | nie geprüft | +| `TestBerechneAnordnung.test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen` | Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen. | grün | nie geprüft | +| `TestBerechneAnordnung.test_seite_wird_nie_kleiner` | Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen. | grün | nie geprüft | +| `TestBerechneAnordnung.test_ohne_legenden_nur_kartenschild` | Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird. | grün | nie geprüft | +| `TestOrdneKartenrandAn.test_seite_hoeher_und_kartenschild_verschoben` | Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen. | grün | nie geprüft | +| `TestOrdneKartenrandAn.test_spalten_ungleich_breit_und_text_umgebrochen` | Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96). | grün | nie geprüft | +| `TestOrdneKartenrandAn.test_legenden_mindestens_so_breit_wie_kartenschild` | Prüft, dass die Legenden die Breite des Kartenschilds als Mindestbreite bekommen und das Layout sie für das Neuladen vermerkt. | grün | nie geprüft | +| `TestOrdneKartenrandAn.test_passt_nichts_wird_verschoben` | Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden. | grün | nie geprüft | +| `TestOrdneKartenrandAn.test_ohne_karte_keine_anordnung` | Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt. | grün | nie geprüft | +| `TestMindestbreiteBeimLaden.test_mindestbreite_nur_fuer_vermerkte_layouts` | Prüft, dass beim Laden die Mindestbreite nur an Layouts mit Vermerk wieder gesetzt wird. | grün | nie geprüft | +| `TestMindestbreiteBeimLaden.test_listener_verbinden_und_trennen` | Prüft, dass der Listener readProject einmal verbindet, beim Trennen wieder löst und beides mehrfach aufgerufen werden darf. | grün | nie geprüft | +| `TestMindestbreiteBeimLaden.test_ohne_qgis_kein_listener` | Prüft, dass im Mock-Modus kein Listener verbunden wird. | grün | nie geprüft | +| `TestAnbindungPrintLayout.test_vorlage_ordnet_kartenrand_an` | Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt. | grün | nie geprüft | +| `TestAnbindungPrintLayout.test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün | nie geprüft | + ### `Plugin_SN_Basis/tests/test_print_layout.py` Worum es in der Datei geht: — (Docstring fehlt) @@ -1451,7 +1763,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen, aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall, dann verschwindet er hier von selbst. -### `sn_basis` – 0 von 365 Testfällen ohne Beschreibung +### `sn_basis` – 0 von 526 Testfällen ohne Beschreibung Keine Lücken. @@ -1657,36 +1969,98 @@ Keine Lücken. Heuristik, kein Beweis: Diese Funktionen und Methoden kommen unter ihrem Namen in keiner Testdatei des Pakets vor. Das schließt nicht aus, dass sie indirekt über eine andere, getestete Funktion mitlaufen – umgekehrt gilt: Taucht der Name irgendwo in einer Testdatei auf, gilt die Funktion schon als referenziert, auch bei zufälliger Namensgleichheit mit etwas anderem. `main.py`/`__init__.py` (QGIS-Einstiegspunkte wie `classFactory`, `initGui`, `unload`) und Dunder-Methoden sind ausgenommen. Diese Liste ersetzt keine fachliche Prüfung, ob ein Test sinnvoll wäre – dafür braucht es einen Menschen, der die Funktion kennt. „beabsichtigt“ liest den Docstring der Funktion: Enthält er einen Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“, gilt der fehlende Test als bewusste Entscheidung. Alles andere ist „ungeklärt“ – die eigentliche Arbeitsliste. -### `sn_basis` – 70 von 229 (31 %) ohne Testbezug (0 ungeklärt, 70 beabsichtigt) +### `sn_basis` – 126 von 393 (32 %) ohne Testbezug (61 ungeklärt, 65 beabsichtigt) #### `functions/dialog_wrapper.py` (9) - `show_info_dialog` (Zeile 42) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ask_detailpruefung_oder_vg_laden` (Zeile 192) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ask_detailpruefung_oder_abbrechen` (Zeile 244) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ProgressDialog.set_total` (Zeile 339) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ProgressDialog.set_value` (Zeile 348) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ProgressDialog.set_label` (Zeile 361) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ProgressDialog.is_canceled` (Zeile 371) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `ProgressDialog.raise_to_front` (Zeile 389) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `create_progress_dialog` (Zeile 408) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ask_detailpruefung_oder_vg_laden` (Zeile 241) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ask_detailpruefung_oder_abbrechen` (Zeile 293) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_total` (Zeile 388) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_value` (Zeile 397) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_label` (Zeile 410) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.is_canceled` (Zeile 420) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.raise_to_front` (Zeile 438) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `create_progress_dialog` (Zeile 457) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -#### `functions/os_wrapper.py` (5) +#### `functions/os_wrapper.py` (4) - `normalize_path` (Zeile 43) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 - `get_home_dir` (Zeile 55) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 - `is_absolute_path` (Zeile 64) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `basename` (Zeile 75) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 - `path_suffix` (Zeile 106) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +#### `functions/qgiscore_wrapper.py` (2) + +- `_crs_kennung` (Zeile 1259) — ungeklärt +- `_crs_gueltig` (Zeile 1267) — ungeklärt + #### `functions/qgisui_wrapper.py` (1) - `add_toolbar` (Zeile 204) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `functions/qt_wrapper.py` (2) -- `exec_dialog` (Zeile 60) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `debug_qt_status` (Zeile 67) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `exec_dialog` (Zeile 64) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `debug_qt_status` (Zeile 71) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 + +#### `modules/basemap_kacheln.py` (4) + +- `_aufruf` (Zeile 28) — ungeklärt +- `_enum_int` (Zeile 61) — ungeklärt +- `qgis_filterpruefer` (Zeile 101) — ungeklärt +- `_objekt_schluessel` (Zeile 122) — ungeklärt + +#### `modules/basemap_katalog.py` (6) + +- `_sammle_bedingungen` (Zeile 138) — ungeklärt +- `_slug` (Zeile 177) — ungeklärt +- `_bezeichnung_aus_id` (Zeile 184) — ungeklärt +- `_bezeichnung` (Zeile 202) — ungeklärt +- `eintrags_schluessel` (Zeile 216) — ungeklärt +- `_ist_grundvariante` (Zeile 235) — ungeklärt + +#### `modules/basemap_legende.py` (4) + +- `Katalog.eintrag_fuer` (Zeile 112) — ungeklärt +- `Katalog.sortierschluessel` (Zeile 115) — ungeklärt +- `_aufruf` (Zeile 267) — ungeklärt +- `finde_basemap_layer` (Zeile 311) — ungeklärt + +#### `modules/basemap_legende_layout.py` (10) + +- `_enum_int` (Zeile 79) — ungeklärt +- `_aufruf` (Zeile 86) — ungeklärt +- `friere_zoom_ein` (Zeile 131) — ungeklärt +- `baue_symbole` (Zeile 176) — ungeklärt +- `_ist_unsichtbar` (Zeile 199) — ungeklärt +- `_rgba` (Zeile 222) — ungeklärt +- `_ebenen_beschreiben` (Zeile 226) — ungeklärt +- `vorschau_icon` (Zeile 259) — ungeklärt +- `_rechteck_mm` (Zeile 316) — ungeklärt +- `_layout_messung` (Zeile 341) — ungeklärt + +#### `modules/basemap_stilkorrektur.py` (2) + +- `_ist_null` (Zeile 28) — ungeklärt +- `_korrigiere_symbol` (Zeile 50) — ungeklärt + +#### `modules/basemap_stilvorlagen.py` (6) + +- `standard_ordner` (Zeile 47) — ungeklärt +- `_stilnamen` (Zeile 58) — ungeklärt +- `lies_importdatei` (Zeile 65) — ungeklärt +- `BasemapStilvorlagen._pfade` (Zeile 113) — ungeklärt +- `BasemapStilvorlagen._metadaten` (Zeile 117) — ungeklärt +- `BasemapStilvorlagen.dateiname` (Zeile 127) — ungeklärt + +#### `modules/basemap_themen.py` (5) + +- `_aufruf` (Zeile 62) — ungeklärt +- `_erfolg` (Zeile 72) — ungeklärt +- `_parameter` (Zeile 101) — ungeklärt +- `_normiert` (Zeile 106) — ungeklärt +- `_lade_themenstil` (Zeile 183) — ungeklärt #### `modules/DataGrabber.py` (3) @@ -1705,6 +2079,16 @@ Heuristik, kein Beweis: Diese Funktionen und Methoden kommen unter ihrem Namen i - `Linkpruefer._pruefe_url` (Zeile 84) — **beabsichtigt:** von Test abgesehen, weil bereits über pruefe() in test_linkpruefer.py vollständig - `Linkpruefer._pruefe_dateipfad` (Zeile 121) — **beabsichtigt:** von Test abgesehen, weil bereits über pruefe() in test_linkpruefer.py vollständig +#### `modules/print_kartenrand.py` (7) + +- `_aufruf` (Zeile 119) — ungeklärt +- `_rechteck` (Zeile 134) — ungeklärt +- `_legende_groesse` (Zeile 139) — ungeklärt +- `_rendere_legenden` (Zeile 149) — ungeklärt +- `setze_legenden_mindestbreite` (Zeile 156) — ungeklärt +- `_on_projekt_gelesen` (Zeile 203) — ungeklärt +- `_verschiebe` (Zeile 234) — ungeklärt + #### `modules/print_layout.py` (11) - `PrintLayout._create_map_item` (Zeile 98) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor @@ -1722,31 +2106,27 @@ Heuristik, kein Beweis: Diese Funktionen und Methoden kommen unter ihrem Namen i #### `modules/print_layout_baufreigabe.py` (3) - `PrintLayoutBaufreigabe.create_baufreigabe_layout` (Zeile 90) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLayoutBaufreigabe._setup_layout_objects_baufreigabe` (Zeile 264) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLayoutBaufreigabe._calc_atlas_margin_percent` (Zeile 398) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayoutBaufreigabe._setup_layout_objects_baufreigabe` (Zeile 274) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayoutBaufreigabe._calc_atlas_margin_percent` (Zeile 408) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -#### `modules/print_logic.py` (20) +#### `modules/print_logic.py` (16) -- `PrintLogic._failed_result` (Zeile 123) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._show_target_size_too_small` (Zeile 127) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._is_enabled_flag` (Zeile 135) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._resolve_layer_from_project` (Zeile 141) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._resolve_active_layer` (Zeile 162) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_druckpuffer_cm` (Zeile 172) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_druckpuffer_m` (Zeile 181) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._import_qgis_symbol` (Zeile 185) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._build_rectangle` (Zeile 193) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._clone_extent` (Zeile 203) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_existing_layer_by_name` (Zeile 270) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._create_druckrechteck_layer` (Zeile 284) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._clear_layer_features` (Zeile 313) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic.configure_atlas_for_baufreigabe_layout` (Zeile 483) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._store_druckrechteck` (Zeile 631) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic.activate_druckrechteck_capture` (Zeile 680) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._resolve_selection_layer` (Zeile 811) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_selected_extent` (Zeile 825) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_plot_dimensions_mm` (Zeile 1078) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor -- `PrintLogic._get_required_map_dimensions_mm` (Zeile 1088) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._failed_result` (Zeile 125) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._show_target_size_too_small` (Zeile 129) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._is_enabled_flag` (Zeile 137) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._resolve_layer_from_project` (Zeile 143) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._resolve_active_layer` (Zeile 164) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_druckpuffer_cm` (Zeile 174) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_druckpuffer_m` (Zeile 183) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._build_rectangle` (Zeile 195) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._clone_extent` (Zeile 205) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_existing_layer_by_name` (Zeile 272) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._create_druckrechteck_layer` (Zeile 286) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic.configure_atlas_for_baufreigabe_layout` (Zeile 485) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic.activate_druckrechteck_capture` (Zeile 689) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._layer_crs` (Zeile 943) — ungeklärt +- `PrintLogic._get_plot_dimensions_mm` (Zeile 1134) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_required_map_dimensions_mm` (Zeile 1144) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor #### `modules/Pruefmanager.py` (4) @@ -1755,15 +2135,36 @@ Heuristik, kein Beweis: Diese Funktionen und Methoden kommen unter ihrem Namen i - `Pruefmanager._handle_datei_existiert` (Zeile 87) — **beabsichtigt:** von Test abgesehen, weil bereits über verarbeite() in test_pruefmanager.py vollständig - `Pruefmanager._ask_use_or_replace_pufferlayer` (Zeile 280) — **beabsichtigt:** von Test abgesehen, weil toter Code: referenziert self.pruefmanager, das auf +#### `ui/base_dockwidget.py` (2) + +- `_inhalt` (Zeile 23) — ungeklärt +- `tabs_mit_scrollbereich` (Zeile 30) — ungeklärt + #### `ui/tabs/print_tab.py` (7) -- `PrintTab._build_ui` (Zeile 115) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._on_kartenname_changed` (Zeile 335) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._on_massstab_changed` (Zeile 349) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._on_zielgroesse_changed` (Zeile 361) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._on_formfaktor_changed` (Zeile 367) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._on_druckbereich_changed` (Zeile 374) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -- `PrintTab._format_druckpuffer_label` (Zeile 432) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._build_ui` (Zeile 116) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_kartenname_changed` (Zeile 340) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_massstab_changed` (Zeile 354) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_zielgroesse_changed` (Zeile 366) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_formfaktor_changed` (Zeile 372) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_druckbereich_changed` (Zeile 379) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._format_druckpuffer_label` (Zeile 437) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 + +#### `ui/widgets/basemap_bereich.py` (13) + +- `BasemapBereich._build_ui` (Zeile 61) — **beabsichtigt:** von Test abgesehen, weil reiner Widget-Aufbau; im QGIS-Test geprüft +- `BasemapBereich._ueberschrift` (Zeile 164) — ungeklärt +- `BasemapBereich._vorlagen` (Zeile 174) — ungeklärt +- `BasemapBereich._gewaehltes_thema` (Zeile 195) — ungeklärt +- `BasemapBereich._zeige_thema` (Zeile 198) — ungeklärt +- `BasemapBereich._umschalten` (Zeile 207) — ungeklärt +- `BasemapBereich._aenderungen_klaeren` (Zeile 241) — ungeklärt +- `BasemapBereich._ersetzen` (Zeile 298) — ungeklärt +- `BasemapBereich._on_erzeugen_geaendert` (Zeile 336) — ungeklärt +- `BasemapBereich._gewaehlte_vorlage` (Zeile 370) — ungeklärt +- `BasemapBereich._on_stil_standard` (Zeile 435) — ungeklärt +- `BasemapBereich._on_stil_loeschen` (Zeile 446) — ungeklärt +- `_thema_text` (Zeile 500) — ungeklärt ### `sn_verfahrensgebiet` – 51 von 161 (32 %) ohne Testbezug (51 ungeklärt, 0 beabsichtigt) diff --git a/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md new file mode 100644 index 0000000..a49de90 --- /dev/null +++ b/docs/tests/verfahrensgebiet-eigentuemer-anzeige.md @@ -0,0 +1,93 @@ +# Testkonzept: Anzeige für den Eigentümer und Korrekturen + +Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderung an diesem Ablauf + +## Worum es fachlich geht + +Im Termin sitzt der Eigentümer vor einem zweiten Bildschirm. Dort sieht er nur seine eigenen gespeicherten Daten: +Name, Anschrift und E-Mail. Den QGIS-Bildschirm mit Trefferliste, Konten und anderen Personen sieht er nicht. +Die Sachbearbeitung beginnt zu Beginn der Termine eine Sitzung und schiebt das Anzeigefenster auf den Bildschirm des Eigentümers; beim nächsten Mal erscheint es wieder dort. Für jeden Eigentümer wählt sie in der Trefferliste +die passenden Einträge und zeigt sie an. Stimmt etwas nicht, korrigiert sie es im Plugin. Der Eigentümer sieht die +Korrektur sofort. Vor dem nächsten Eigentümer leert sie die Anzeige, ohne das Fenster zu schließen. Am Ende beendet +sie die Sitzung. + +Die Eigentümerdatei selbst wird nie verändert. Jede Korrektur landet als eigene Zeile in einer Korrektur-CSV neben +der Eigentümerdatei und kann später in das Fachverfahren übernommen werden. + +Die Suche bis zur Trefferliste beschreibt [`verfahrensgebiet-eigentuemersuche.md`](verfahrensgebiet-eigentuemersuche.md). + +## Der Ablauf + +```mermaid +flowchart TD + A["1 Sitzung beginnen"] --> B["2 Anzeigefenster öffnet leer, mit Rahmen;
Bearbeiter schiebt es auf den zweiten Bildschirm"] + T["Trefferliste: Einträge wählen,
„Daten anzeigen“"] --> C{"Sitzung läuft?"} + C -->|nein| A + C -->|ja| D["3 Eigentümer sieht Name,
Anschrift, E-Mail"] + B --> D + D --> E{"4 Daten richtig?"} + E -->|nein| F["5 Korrigieren: Änderung
in Korrektur-CSV, sofort angezeigt"] + F --> E + E -->|ja| G["6 Anzeige leeren
(Fenster bleibt offen)"] + G --> T + G --> H["7 Sitzung beenden:
Fenster schließt"] + + style G fill:#e9f5e9,stroke:#2b8a3e +``` + +## Was geprüft wird + +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_anzeige.py` (Anzeigetext, Fenster öffnen), +`tests/test_eigentuemer_korrektur.py` (Korrekturlogik, Korrektur-CSV) und `tests/test_eigentuemer_tab.py` +(Sitzung, Korrektur im Tab). + +| # | Schritt | Wird geprüft durch | Ebene | +|---|---|---|---| +| 2 | Sitzung beginnen: leeres Fenster mit Rahmen, nie Vollbild | `test_sitzung_beginnen_oeffnet_leeres_fenster_mit_rahmen`, `test_normales_fenster_in_standardgroesse` | Mock | +| 2 | Lage des Fensters gilt für die nächste Sitzung | `test_lage_gilt_fuer_naechste_sitzung`, `test_gemerkte_lage_wird_wiederhergestellt`, `test_unbrauchbare_lage_ergibt_standardgroesse` | Mock | +| 3 | „Daten anzeigen“ beginnt bei Bedarf die Sitzung | `test_daten_anzeigen_beginnt_sitzung_bei_bedarf`, `test_daten_anzeigen_gibt_auswahl_weiter` | Mock | +| 3 | Ohne Auswahl nur ein Hinweis | `test_daten_anzeigen_ohne_auswahl` | Mock | +| 3 | Inhalt: Name, Anschrift, E-Mail; mehrere Einträge | `test_name_anschrift_email`, `test_mehrere_eintraege_getrennt` | Mock | +| 3 | Keine internen Angaben für den Eigentümer | `test_keine_internen_angaben` | Mock | +| 3 | Fehlende E-Mail ausdrücklich nennen | `test_fehlende_email_wird_genannt` | Mock | +| 5 | Nur echte Änderungen, Leeren zählt | `test_nur_echte_aenderungen`, `test_feld_leeren_ist_aenderung`, `test_feldwerte` | Mock | +| 5 | Korrektur festhalten und sofort anzeigen | `test_korrektur_wird_festgehalten_und_angezeigt`, `test_wende_an` | Mock | +| 5 | Unverändert oder abgebrochen: nichts schreiben | `test_ohne_aenderung_nichts_schreiben`, `test_abbruch_nichts_schreiben` | Mock | +| 5 | Korrektur-CSV: Kopfzeile, BOM, anhängen | `test_neue_datei_mit_kopfzeile_und_bom`, `test_anhaengen_ohne_zweite_kopfzeile`, `test_ohne_aenderung_keine_datei` | Mock | +| 5 | Pfad neben der Eigentümerdatei, sonst Rückfrage | `test_textdatei_provider`, `test_ogr_provider`, `test_keine_csv`, `test_vorschlag_neben_der_eigentuemerdatei`, `test_ohne_csv_quelle_wird_gefragt` | Mock | +| 5 | Schreibfehler | `test_schreibfehler_wird_weitergereicht`, `test_schreibfehler_warnung_und_anzeige_unveraendert` | Mock | +| 5 | Ohne Anzeige keine Korrektur | `test_ohne_anzeige_keine_korrektur` | Mock | +| 5 | Pfad der Korrektur-Datei unter den Knöpfen | `test_ohne_eigentuemerdatei`, `test_pfad_neben_der_eigentuemerdatei`, `test_ohne_csv_quelle_wird_abgefragt`, `test_gewaehlter_pfad_wird_angezeigt` | Mock | +| 6 | Anzeige leeren, Fenster bleibt offen | `test_anzeige_leeren_laesst_fenster_offen`, `test_leer_nur_platzhalter` | Mock | +| 7 | Sitzung beenden bzw. Fenster von außen geschlossen | `test_sitzung_beenden_schliesst_fenster`, `test_von_aussen_geschlossen_beendet_sitzung` | Mock | +| — | Keine Personendaten auf der Konsole | `test_schreibt_nichts_auf_die_konsole` | Mock | +| — | Sieht das in QGIS richtig aus, auf zwei Bildschirmen? | manueller Test | manuell | + +Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` (Mensch in QGIS). + +## Fachliche Sonderfälle + +| Sonderfall | Kommt vor bei | Wird geprüft durch | +|---|---|---| +| Nur ein Bildschirm (Notebook) | Termin ohne zweiten Monitor, Ausprobieren | `test_normales_fenster_in_standardgroesse` (immer mit Rahmen, verdeckt QGIS nie ausweglos) | +| Eheleute oder Miteigentümer erscheinen gemeinsam | mehrere Einträge auf einmal anzeigen und einzeln korrigieren | `test_mehrere_eintraege_getrennt`, `test_korrektur_wird_festgehalten_und_angezeigt` | +| Eigentümer hat keine E-Mail hinterlegt | häufig; soll beim Termin ergänzt werden | `test_fehlende_email_wird_genannt` | +| Neue Anschrift mit Komma („Am Anger 3, Haus B“) | Zusatz zur Hausnummer | `test_neue_datei_mit_kopfzeile_und_bom` | +| Eigentümerdatei liegt in einem schreibgeschützten Ordner | Export auf einem Netzlaufwerk ohne Schreibrecht | `test_ohne_csv_quelle_wird_gefragt` (Rückfrage), `test_schreibfehler_warnung_und_anzeige_unveraendert` | +| Sonderzeichen oder spitze Klammern in den Daten | Firmennamen | `test_werte_werden_maskiert` | + +## Bewusst nicht getestet + +| Was | Warum | Wie stattdessen abgesichert | +|---|---|---| +| Wiederherstellen der Lage auf dem zweiten Bildschirm | braucht echte Bildschirme; im Mock-Modus gibt es keine | Aufruf mit Attrappe geprüft, manueller Test mit zwei Bildschirmen | +| Korrekturdialog mit echten Eingabefeldern | im Mock-Modus gibt es keine Widgets; die Vorbelegung (`feldwerte`) und die Auswertung sind als reine Funktionen getestet | manueller Test | +| Schriftgröße und Lesbarkeit der Anzeige | Geschmack und Bildschirmgröße | manueller Test im Termin | + +## Bekannte Lücken + +| Lücke | Auswirkung | Issue | +|---|---|---| +| Wiederherstellen der Lage in der nächsten Sitzung ist in QGIS noch nicht bestätigt | Das Fenster erscheint dann wieder auf dem Hauptbildschirm und muss erneut verschoben werden – lästig, aber gefahrlos | Verfahrensgebiet #56 | +| Die Lage wird nur bis zum Neustart von QGIS gemerkt | nach jedem Start einmal verschieben | bei Bedarf eigenes Issue (QSettings) | +| Eine neue Suche leert die Anzeige nicht von selbst | Wer „Anzeige leeren“ vergisst, zeigt dem nächsten Eigentümer noch die vorherigen Daten | bewusst so (Bearbeiter entscheidet); bei Bedarf eigenes Issue | diff --git a/docs/tests/verfahrensgebiet-eigentuemersuche.md b/docs/tests/verfahrensgebiet-eigentuemersuche.md new file mode 100644 index 0000000..2db9cd4 --- /dev/null +++ b/docs/tests/verfahrensgebiet-eigentuemersuche.md @@ -0,0 +1,88 @@ +# Testkonzept: Eigentümersuche im Tab „Eigentümer“ + +Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderung an diesem Ablauf + +## Worum es fachlich geht + +Im Termin mit Eigentümern gleicht die Sachbearbeitung die gespeicherten Personendaten ab: Name, Anschrift und +E-Mail. Grundlage ist der Serienbriefexport als CSV-Datei, den die Sachbearbeitung von Hand ins Projekt lädt. +Sie sucht nach Name und/oder Vorname. Die Trefferliste fasst Einträge derselben Person mit derselben Anschrift +zusammen, denn im Export steht eine Person für jedes Eigentumskonto einmal. Dieselbe Person mit einer anderen +Anschrift bleibt ein eigener Eintrag. Damit fällt eine abweichende Anschrift sofort auf. Verstorbene und +Rechtspersonen sind in der Liste gekennzeichnet: +verstorben als Hinweis (`PER_VERSTORBEN` = 1), die Art der Person als Rohwert von `PER_RECHTSPERSON` +(0, 1, 5 oder 9; die Bedeutung der Werte wird später entschlüsselt). + +Die Trefferliste sieht nur die Sachbearbeitung. Wie die gewählten Einträge dem Eigentümer angezeigt und korrigiert +werden, beschreibt [`verfahrensgebiet-eigentuemer-anzeige.md`](verfahrensgebiet-eigentuemer-anzeige.md). + +## Der Ablauf + +```mermaid +flowchart TD + A["1 Anwender lädt den Serienbriefexport
als CSV-Layer ins Projekt"] --> B["2 Eigentümerdatei wählen
(nur Layer mit Nachname und Vorname)"] + B --> C{"3 Eigentümerdatei
gewählt?"} + C -->|nein| D["Hinweis: CSV laden und wählen"] + C -->|ja| E{"4 Name oder Vorname
eingegeben?"} + E -->|nein| F["Hinweis: Namen eingeben"] + E -->|ja| G["5 Einträge suchen
(Teilwort, Groß-/Kleinschreibung
und Umlaute egal)"] + G --> H["6 Gleiche Person mit gleicher
Anschrift zusammenfassen"] + H --> I{"7 Treffer?"} + I -->|nein| J["Hinweis: keine Einträge
(ohne den Namen zu wiederholen)"] + I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,
Rechtsperson-Wert, verstorben;
Mehrfachauswahl"] + + style D fill:#fff4e6,stroke:#d9822b + style F fill:#fff4e6,stroke:#d9822b +``` + +## Was geprüft wird + +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_suche.py` (Suchlogik), +`tests/test_eigentuemer_tab.py` (Bedienung, Trefferliste) und `tests/test_verfahrensgebiet_dialoge.py` +(gemeinsame Auswahltabelle). + +| # | Schritt | Wird geprüft durch | Ebene | +|---|---|---|---| +| 2 | Nur Layer mit Nachname und Vorname, auch mit BOM oder anderer Schreibweise | `test_layer_mit_name_und_vorname_ist_eigentuemerlayer`, `test_eigentuemerlayer_im_projekt` | Mock | +| 3 | Keine Eigentümerdatei gewählt | `test_ohne_eigentuemerdatei_warnung` | Mock | +| 3 | Layer ohne die nötigen Spalten | `test_layer_ohne_pflichtspalten` | Mock | +| 4 | Ohne Eingabe keine Komplettliste | `test_ohne_eingabe_fehler`, `test_suchfehler_als_warnung` | Mock | +| 5 | Teilwort, Groß-/Kleinschreibung | `test_teilstring_ohne_gross_kleinschreibung` | Mock | +| 5 | Umlaute und ß | `test_umlaute_und_eszett` | Mock | +| 6 | Zusammenfassen, Konten und E-Mails sammeln | `test_gleiche_person_gleiche_anschrift_zusammengefasst` | Mock | +| 6 | Andere Anschrift bleibt eigener Eintrag | `test_andere_anschrift_eigener_treffer` | Mock | +| 7 | Kein Treffer, Meldung ohne Namen | `test_kein_treffer_ohne_namen_in_meldung` | Mock | +| 8 | Trefferliste öffnen, alte ersetzen | `test_treffer_oeffnen_trefferliste_und_ersetzen_die_alte` | Mock | +| 8 | Spalten der Trefferliste, Kennzeichen | `test_trefferzeile_mit_hinweis`, `test_kennzeichen_verstorben`, `test_rechtsperson_als_rohwert` | Mock | +| 8 | Ausgewählte Einträge, auch nach Umsortieren | `test_ausgewaehlte_eigentuemer`, `test_auswahl_nach_umsortieren` | Mock | +| 8 | Suchfeld in der Trefferliste | `test_suchfilter_ueber_alle_spalten` | Mock | +| — | Keine Personendaten auf der Konsole | `test_suche_gibt_nichts_aus` | Mock | +| — | Sieht das in QGIS richtig aus? | manueller Test | manuell | + +Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` (Mensch in QGIS). + +## Fachliche Sonderfälle + +| Sonderfall | Kommt vor bei | Wird geprüft durch | +|---|---|---| +| Eine Person mit mehreren Eigentumskonten | Regelfall im Serienbriefexport | `test_gleiche_person_gleiche_anschrift_zusammengefasst` | +| Schreibweise weicht ab (Groß-/Kleinschreibung, Leerzeichen) | Handeingaben über Jahre | `test_gleiche_person_gleiche_anschrift_zusammengefasst` | +| `Müller` gesucht, `Mueller` gespeichert | ältere Datenbestände | `test_umlaute_und_eszett` | +| PLZ ohne führende Null (`1067`) | QGIS erkennt beim CSV-Laden die Spalte als Zahl | `test_plz_mit_fuehrender_null` | +| `PER_VERSTORBEN` als Text oder als Zahl gelesen | QGIS erkennt beim CSV-Laden die Spalte als Zahl | `test_kennzeichen_verstorben` | +| Eine Person mit verschiedenen `PER_RECHTSPERSON`-Werten | mehrere Konten mit unterschiedlicher Erfassung | `test_rechtsperson_als_rohwert` (Werte werden gesammelt) | +| Leere Felder bzw. QGIS-NULL | unvollständige Datensätze | `test_kennzeichen_verstorben`, `test_rechtsperson_als_rohwert` | +| Rechtsperson ohne Vornamen (z. B. Gemeinde) | Körperschaften als Eigentümer | `test_name_und_anschrift`, `test_rechtsperson_als_rohwert` | + +## Bewusst nicht getestet + +| Was | Warum | Wie stattdessen abgesichert | +|---|---|---| +| Aufbau von Trefferfenster und Auswahltabelle mit echten Qt-Widgets | im Mock-Modus gibt es keine Widgets; die Filter- und Zuordnungslogik ist als reine Funktion getestet | manueller Test in QGIS | +| Echter Serienbriefexport | personenbezogen, gehört nicht ins Repository | erfundene Testzeilen mit der Kopfzeile der Blanko-Vorlage aus dem Issue | + +## Bekannte Lücken + +| Lücke | Auswirkung | Issue | +|---|---|---| +| Der manuelle QGIS-Test steht aus, auch für den Landkreis-Dialog, dessen Tabelle jetzt aus der gemeinsamen Funktion kommt | Ein Fehler beim Herauslösen der Tabelle bliebe in beiden Dialogen unbemerkt | Verfahrensgebiet #56 | diff --git a/docs/tests/verfahrensgebiet-flurstueckssuche.md b/docs/tests/verfahrensgebiet-flurstueckssuche.md new file mode 100644 index 0000000..3eaf71a --- /dev/null +++ b/docs/tests/verfahrensgebiet-flurstueckssuche.md @@ -0,0 +1,84 @@ +# Testkonzept: Flurstückssuche im Tab „Eigentümer“ + +Plugin: `sn_verfahrensgebiet` · Stand: 06.10.2026 · Pflege: bei jeder Änderung an diesem Ablauf + +## Worum es fachlich geht + +Im Termin mit einem Eigentümer soll die Sachbearbeitung ein Flurstück schnell auf der Karte zeigen können. +Sie gibt Gemarkung und Flurstücksnummer ein, das Plugin wählt das Flurstück im geladenen Flurstückslayer aus +und springt dorthin. Gesucht wird nur in den Flurstücken, die vorher mit dem Verfahrensgebiet aus ALKIS geladen +wurden. So muss im Tab keine Gemarkung aus ganz Sachsen ausgewählt werden. + +Die Eigentümersuche im selben Tab beschreibt [`verfahrensgebiet-eigentuemersuche.md`](verfahrensgebiet-eigentuemersuche.md). + +## Der Ablauf + +```mermaid +flowchart TD + A["1 Anwender gibt Gemarkung
und Flurstücksnummer ein"] --> B{"2 Flurstückslayer
im Projekt?"} + B -->|nein| C["Hinweis: zuerst Verfahrensgebiet
und Flurstücke aus ALKIS laden"] + B -->|ja| D{"3 Flurstücksnummer lesbar?
z. B. 562 oder 12/3"} + D -->|nein| E["Hinweis zur Schreibweise"] + D -->|ja| F{"4 Gemarkung als Nummer
oder als Name?"} + F -->|Nummer| H["5 Flurstücke vergleichen"] + F -->|Name| G{"Gemarkungslayer geladen
und Name darin?"} + G -->|nein| E2["Hinweis: Nummer eingeben
oder Gemarkungen laden"] + G -->|ja| H + H --> I{"6 Treffer?"} + I -->|nein| J["Hinweis: nicht gefunden"] + I -->|ja| K["7 Treffer auswählen,
hinzoomen, aufblinken"] + + style C fill:#fff4e6,stroke:#d9822b + style E fill:#fff4e6,stroke:#d9822b + style E2 fill:#fff4e6,stroke:#d9822b +``` + +## Was geprüft wird + +Die Tests liegen in `Plugin_SN_Verfahrensgebiet/tests/test_flurstueck_suche.py` (Suchlogik) und +`tests/test_eigentuemer_tab.py` (Bedienung im Tab). + +| # | Schritt | Wird geprüft durch | Ebene | +|---|---|---|---| +| — | Tab erscheint nach „Bearbeitung“ | `test_tab_ist_im_dockwidget_eingetragen` | Mock | +| 2 | Kein Flurstückslayer: Suche bricht ab | `test_fehlender_flurstueckslayer_bricht_ab` | Mock | +| 2 | … und der Hinweis kommt als Dialog | `test_ohne_flurstueckslayer_erscheint_hinweisdialog` | Mock | +| 3 | Zähler und Nenner lesen, führende Nullen | `test_flurstuecksnummer_mit_und_ohne_nenner` | Mock | +| 3 | Unlesbare Nummer abweisen | `test_ungueltige_flurstuecksnummer`, `test_eingabefehler_als_warnung` | Mock | +| 4 | Gemarkungsnummer, auch ohne Nullen oder als langer Schlüssel | `test_gemarkungsnummer_aus_ziffern` | Mock | +| 4 | Gemarkungsname, auch mehrfach vorkommend | `test_gemarkungsname_ueber_gemarkungslayer` | Mock | +| 4 | Name ohne Gemarkungslayer oder unbekannt | `test_gemarkungsname_ohne_layer_oder_unbekannt` | Mock | +| 5 | Flurstückskennzeichen zerlegen | `test_kennzeichen_zerlegen` | Mock | +| 5 | Layer ohne Kennzeichenfeld | `test_layer_ohne_kennzeichenfeld` | Mock | +| 6 | Mit Nenner genau ein Treffer | `test_mit_nenner_genau_ein_treffer` | Mock | +| 6 | Ohne Nenner alle mit diesem Zähler, nur in der Gemarkung | `test_ohne_nenner_alle_mit_diesem_zaehler` | Mock | +| 6 | Kein Treffer | `test_kein_treffer` (Logik), `test_kein_treffer` (Tab) | Mock | +| 7 | Auswahl, Zoom, Aufblinken | `test_treffer_werden_ausgewaehlt_und_angezoomt` | Mock | +| 7 | Auswahl auch ohne Kartenansicht | `test_ohne_kartenansicht_bleibt_auswahl` | Mock | +| — | Sieht das in QGIS richtig aus? | manueller Test | manuell | + +Ebene: `Mock` (ohne QGIS, Sekunden) · `QGIS` (gegen echtes QGIS) · `manuell` (Mensch in QGIS). + +## Fachliche Sonderfälle + +| Sonderfall | Kommt vor bei | Wird geprüft durch | +|---|---|---| +| Buchstaben-Nenner (`12/a`) | ältere Flurstücke | `test_mit_nenner_genau_ein_treffer`, `test_kennzeichen_zerlegen` | +| Gleichnamige Gemarkungen | verbreitete Ortsnamen in mehreren Landkreisen | `test_gemarkungsname_ueber_gemarkungslayer` | +| Gemarkungsschlüssel mit Landesnummer (`140268`) | kopiert aus anderen Programmen | `test_gemarkungsnummer_aus_ziffern` | +| Flurstücke mit leerem Kennzeichen | unvollständige Daten | `test_ohne_nenner_alle_mit_diesem_zaehler` (Feature ohne Kennzeichen wird übersprungen) | +| Im Flurstückslayer ist schon etwas ausgewählt | Verfahrensgebiet aus der Flurstücksauswahl bilden | `test_treffer_werden_ausgewaehlt_und_angezoomt`: Die Suche ersetzt die Auswahl, wie die Suchen in QGIS selbst | + +## Bewusst nicht getestet + +| Was | Warum | Wie stattdessen abgesichert | +|---|---|---| +| Zoomen und Aufblinken in der Kartenansicht | QGIS-Funktionen (`zoomToSelected`, `flashFeatureIds`), im Mock-Modus gibt es keine Karte | manueller Test in QGIS | +| Aussehen des Tabs | nicht sinnvoll automatisiert prüfbar | manueller Test | +| Echter Flurstückslayer aus dem ALKIS-WFS | kein Netzwerk in Tests | Aufbau des Kennzeichens in `../wissen/alkis_objektarten.md`, manueller Test | + +## Bekannte Lücken + +| Lücke | Auswirkung | Issue | +|---|---|---| +| Der manuelle Test in QGIS steht aus | Zoom auf sehr kleine Flurstücke und das Aufblinken sind nicht gesehen | Verfahrensgebiet #56 | diff --git a/docs/wissen/alkis_objektarten.md b/docs/wissen/alkis_objektarten.md index 26b037b..45f011f 100644 --- a/docs/wissen/alkis_objektarten.md +++ b/docs/wissen/alkis_objektarten.md @@ -37,6 +37,14 @@ Versionsnummern unten nachziehen. - Praktisch relevant: das Flurstückskennzeichen ist objektbildend; jedes Flurstück gehört zu genau einer Gemarkung bzw. Flur/Gemarkungsteil. Das Plugin übernimmt das komplette Feldschema des WFS-Layers in den Memory-Layer, ohne Attribute fest zu benennen. +- **Aufbau von `flurstueckskennzeichen`** (20 Zeichen, unbesetzte Teile mit `_` gefüllt), am Dienst geprüft + am 06.10.2026: `14 0268 ___ 00562 ____ 00` = Land (2), Gemarkungsnummer (4), Flur (3, in Sachsen unbesetzt), + Zähler (5), Nenner (4, auch Buchstaben wie `000a`), Folge (2). Die Flurstückssuche (Verfahrensgebiet #56) + vergleicht deshalb dieses Kennzeichen und nicht die von QGIS abgeflachten Einzelfelder + (`flurstuecksnummer/…/zaehler`), deren Namen vom WFS-Provider abhängen. +- **Keine Eigentümer- oder Buchungsangaben:** Der öffentliche Dienst verweist nur per `istGebucht` auf ein + Buchungsblatt-Objekt, das er selbst nicht ausliefert. Eine Zuordnung Eigentümer ↔ Flurstück braucht eine eigene + Quelle (Verfahrensgebiet #56, dort zurückgestellt). ## `AX_BauRaumOderBodenordnungsrecht` — Bau-, Raum- oder Bodenordnungsrecht diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index a859a69..cba6e3e 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -5,6 +5,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik ## Umgebung und Werkzeuge +- **`qt_wrapper` meldet unter QGIS 3.44 `QT_VERSION == 6`, obwohl Qt5 läuft.** Der „PyQt6“-Zweig importiert + `qgis.PyQt`, das unter QGIS 3 PyQt5 ist; die Qt6-Schreibweise der Enums (`Qt.ItemFlag.ItemIsUserCheckable`) + funktioniert mit PyQt 5.15 trotzdem. `QT_VERSION` also nicht als Qt-Hauptversion verstehen, nur als + „echtes Qt (≠ 0)“. (02.10.2026, QGIS 3.44.4 geprüft, Basis #88) +- **Lücken im Mock-Modus:** `QgsGeometry` hat im Mock kein `fromRect`, `QgsLayoutPoint`/`QgsLayoutSize` haben + `x`/`width` als Attribute statt als Methoden; `QgsRectangle` hatte bis Basis #88 gar keinen Mock. Tests patchen + solche Stellen gezielt, statt die Produktionsfunktion umzubauen. (02.10.2026) - **QGIS 3.40 ist seit Januar 2026 End of Life.** Aktueller LTR ist 3.44, Stable 4.2 (wird 25.09.2026 LTR). `workspace.toml` und alle `metadata.template` stehen noch auf `3.40` – offene Produktentscheidung, siehe `docs/konzept/07_qgis_tests_plan.md` Abschnitt 2. (04.09.2026, QGIS-Release-Fahrplan) - **Zwei Qt-Welten.** QGIS 3.40/3.44 laufen mit Qt5/PyQt5, QGIS 4 mit Qt6/PyQt6. `qt_wrapper` lädt PyQt6 zuerst, dann PyQt5, sonst Mock. Enum-Zugriffe unterscheiden sich (`Qt.AlignmentFlag.AlignTop` statt `Qt.AlignTop`), deshalb nur Wrapper-Aliase verwenden. (09/2026, Code) - **Mock-Modus ist kein QGIS.** `python scripts/run_tests.py` läuft in Sekunden, prüft aber nur Logik gegen Mock-Klassen. Geometrieoperationen, WFS, Layouts und Signale müssen in QGIS geprüft werden (`tests/test_qgis.bat` unter Windows). Manuelle QGIS-Tests im PR dokumentieren. (09/2026) @@ -60,6 +67,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik ## QGIS-Tests (geplant, siehe `docs/konzept/07_qgis_tests_plan.md`) +- **Spike/Abnahme ohne QGIS-Oberfläche:** Skript mit `QT_QPA_PLATFORM=offscreen` über `python-qgis.bat` starten, + das Plugin-Repo per Junction (`mklink /J \sn_basis `) unter dem Paketnamen in `sys.path` + legen. Für Widgets und Dialoge `QgsApplication([], True)` (mit GUI) – ohne GUI stürzt z. B. + `QgsSymbolLayerUtils.symbolPreviewIcon` ab. Modale Dialoge: `exec_dialog` patchen und im Ersatz die Widgets + prüfen. Layout-Ergebnis mit `QgsLayoutExporter.renderRegionToImage` als PNG ansehen; ohne installierte Schriften + erscheinen Texte als Kästchen. `exportToImage` liefert bei Erfolg `0`. (02.10.2026, Basis #88) - **`QT_QPA_PLATFORM=offscreen` muss gesetzt sein, bevor die `QgsApplication` erzeugt wird.** Danach wirkt es nicht mehr. (04.09.2026, Recherche) - **`qgis.utils.iface` ist nach `start_app()` `None`.** Entweder `pytest-qgis` verwenden (patcht automatisch) oder selbst `qgis.utils.iface = get_iface()` setzen – und keine Module zu früh importieren. (04.09.2026) - **`QgsProject.instance()` ist global.** Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig. (04.09.2026) @@ -81,16 +94,90 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik ## Daten und Dienste +- **Punktlinien aus Mapbox-Styles sind in QGIS unsichtbar.** basemap.de (wie viele MapLibre-Stile) zeichnet Punktlinien + als `line-dasharray` mit Strichlänge 0 und `line-cap: round`. QGIS übernimmt das beim Import 1:1, Qt zeichnet + Striche der Länge 0 aber gar nicht – auch nicht als Punkt, weder in der Karte noch in der Legende. Betroffen + sind bei basemap.de 12 Styles (Baumreihe, Hecke, Durchlass, Seilbahnen …). Abhilfe: Strichlänge 0 durch 0,01 + ersetzen (`sn_basis.modules.basemap_stilkorrektur`). Beim Prüfen solcher Effekte die Beschriftung abschalten – + sonst zählen Labels als „sichtbare Pixel“. (02.10.2026, QGIS 3.44 / PyQt 5.15, Basis #90; QGIS 4 ungeprüft) +- **basemap.de-Vektorkacheln (Basis #88):** QGIS zeigt für Vektorkachel-Layer keine Legende. Objekte im Gebiet + liefert `QgsVectorTileLayer.selectByGeometry()` (lädt die Kacheln selbst; `getRawTile` ist in Python gesperrt). + Kacheln enden bei Zoom 15. Der Renderer prüft Styles mit `scaleToZoomLevel(maßstab, False)` und setzt + `@vector_tile_zoom = scaleToZoom(maßstab)` – diese Variable fehlt in der Legende, zoomabhängige Strichmuster + sind dort sonst unsichtbar. Beim Import der Style-JSON werden Beschriftungen zu Labeling (Hausnummern = + Labeling-Style `Hauskoordinate`), `Nummer_*`-Schilder zu Renderer-Symbolen, `fill-extrusion` (3D) entfällt. + Der Legendenkatalog `sn_basis/data/basemap_legende.json` gehört zur Style-Version `bm_web_col` v2; nach einer + neuen Version `python modules/basemap_katalog.py` laufen lassen und Meldungen prüfen. Eine QML-Stilvorlage des + Layers ist rund 3,7 MB groß. (02.10.2026) +- **`symbol_stile` im Basemap-Legendenkatalog müssen alle Zoomstufen abdecken (Basis #98).** Viele basemap.de-Styles + gelten nur in einem Zoombereich (z. B. `*_Autobahn_getrennt` bis Zoom 13, ab Zoom 14 + `*_Autobahn_getrennt_Fahrbahnachse`). Ist bei der Druck-Zoomstufe kein `symbol_stil` aktiv, nimmt + `waehle_symbolnamen` den ersten getroffenen Style – oft nur die graue Kontur. Deshalb je Zoombereich ein + Kontur-/Decker-Paar eintragen; inaktive werden übergangen. Lücken findet ein Abgleich der `minzoom`/`maxzoom` + der Style-JSON mit dem Katalog. Weitere Lücken im Druckbereich (Stand 06.10.2026): „Straße im Tunnel“ + ab Zoom 14, „Biosphärenreservat“ ab Zoom 11. (06.10.2026) +- **basemap.de-Themen (Basis #100):** Angeboten werden Farbe (`bm_web_col`), Grau (`bm_web_gry`) und Relief + (`bm_web_top`); `bm_web_bin` liegt auf dem Server, steht aber nicht auf basemap.de. Farbe und Grau nutzen nur die + Kachelquelle `bm_web_de_3857`. **Relief braucht zusätzlich** die Höhenlinien-Kacheln `bm_web_hl_de_3857` und zwei + WMS-Schummerungen – ein einzelner QGIS-Vektorkachel-Layer hat nur eine Kachelquelle, Relief ist deshalb vorerst + nicht im Themen-Dropdown. Style-Version der JSONs am 05.10.2026: `basemapde:style-version` 5.1.0. +- **QGIS kennt kein „Darstellung geändert“-Kennzeichen pro Layer.** Für ungespeicherte Änderungen am Thema wird + deshalb ein Prüfwert der QML (Symbologie + Beschriftung, ohne `version`-Attribut) als Layer-Eigenschaft gemerkt + und vor dem Themenwechsel verglichen (`basemap_themen.aenderungsstand`). Die Berechnung schreibt die rund 3,7 MB + große QML in eine temporäre Datei. Dieselbe Darstellung ergibt in QGIS dieselbe QML, der Prüfwert ist also stabil + (manueller QGIS-Test 05.10.2026). Auch „Punktlinien korrigieren“ zählt als Änderung. (05.10.2026) +- **basemap.de P10 Vektor ist kein Kacheldienst (Basis #102).** Es ist ein bundesweites GeoPackage (60,3 GB, 40 + Tabellen, **EPSG:25832**, Beta vom 14.08.2026, optimiert für QGIS 3.44.8). Die Basemap-Werkzeuge in `sn_basis` + (Erkennung, `selectByGeometry`, Legendenkatalog nach Style-IDs, Stilvorlagen über `renderer().styles()`) setzen + Vektorkacheln voraus und greifen dort nicht; die QGIS-Legende funktioniert dagegen selbst. Weitere Befunde der + Prüfung vom 06.10.2026: + - Stile gibt es nicht als `layer_styles`, sondern als ein in `qgis_projects` eingebettetes QGIS-Projekt (29 Layer). + Farbe/Grau steuert die Projektvariable `@project_theme` (`'gry'`) zusammen mit 73 benannten Projektfarben + (`project_color_object('Seeblau'|'Seeblau_gry')`). Wer nur die Layerstile übernimmt, verliert die Farben. + - Direktes Lesen aus dem Netz (`/vsicurl/`) ist zu langsam: Die Objekte liegen in der Datei nicht räumlich sortiert, + 3 × 3 km und 5 Layer kosteten rund 3 900 Teilabrufe (etwa 8 min); nach einigen tausend Abrufen trennt der Server + die Verbindung. Nur nach vollständigem Download zuschneiden; `ogr2ogr` kopiert `qgis_projects` voraussichtlich nicht mit. + - Die Beta-Lizenz erlaubt die Nutzung nur „zu Testzwecken“ (Nr. 2.2). (06.10.2026) - **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code) - **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026) - **ArcGIS-REST-Layer nicht nach Nummer deuten.** Gleiche Layer-URLs werden in der Linkliste und im Alternativansatz unterschiedlich benannt; Bedeutung und Geometrie immer gegen die Dienstmetadaten prüfen. (Vergleich Alternativansatz 09/2026) - **CRS:** Plan41 und VLN-API arbeiten in EPSG:25833. Der Alternativansatz erzeugt EPSG:25832. Für neue Layer 25833 verwenden, bis eine Entscheidung dokumentiert ist. - **Shape-Import erwartet Feld `FBSchl`** mit `901/10110F` (Neuordnungsgebiet) oder `901/10120F` (Neuvermessungsgebiet). Exporte mit abweichendem Feldnamen (`FBSchl.`) wurden bis 26.8.3 abgewiesen; seit PR Verfahrensgebiet #45 (02.09.2026, 26.8.4-unstable) ist die Feldsuche tolerant. Neue Exportvarianten trotzdem gegen die Erkennung testen. +- **Eigentümerdaten sind personenbezogen (Verfahrensgebiet #56).** Der Serienbriefexport wird vom Anwender von Hand als CSV-Layer geladen; das Plugin liest ihn nur. Namen und Anschriften gehören nicht in `print`, Meldungsleiste, Projektvariablen oder Testdaten (Tests nutzen erfundene Namen, `test_suche_gibt_nichts_aus` prüft die Konsole). Ein gespeichertes Projekt enthält nur den Pfad zur CSV, nicht die Daten. Die Korrektur-CSV (`_korrekturen.csv`) enthält ebenfalls Personendaten und liegt deshalb neben dem Export, nicht im Projekt- oder Plugin-Ordner. Das Anzeigefenster für den Eigentümer zeigt nur Name, Anschrift und E-Mail; Konten, Personen-IDs und Kennzeichen bleiben im Tab. (06.10.2026) +- **Kein randloses Vollbild für eigene Fenster (Verfahrensgebiet #56).** Das Anzeigefenster für den Eigentümer sollte per `windowHandle().setScreen()`, `setGeometry()` und `showFullScreen()` auf dem zweiten Bildschirm erscheinen. Im QGIS-Test (06.10.2026, Windows) landete es trotzdem immer auf dem Hauptbildschirm und ließ sich im Vollbild weder verschieben noch schließen; es verdeckte QGIS, nur ein hartes Beenden von QGIS half. Seitdem öffnet es als normales Fenster mit Rahmen, der Bearbeiter schiebt es selbst auf den richtigen Bildschirm und kann es dort maximieren; `saveGeometry()`/`restoreGeometry()` merkt die Lage für die nächste Sitzung. Allgemein: Fenster, die QGIS verdecken können, brauchen immer einen Weg hinaus (Rahmen, Schließen-Knopf). (06.10.2026) +- **QGIS liest Zahlen-Spalten einer CSV als Zahl.** Beim Laden als Textdatei-Layer erkennt QGIS Feldtypen; eine PLZ `01067` wird dabei zur Ganzzahl `1067` – in Sachsen beginnt fast jede PLZ mit 0. `eigentuemer_suche._plz` stellt die Null wieder her. Kennungen wie `EIG_KONTO` oder `PER_ID` deshalb als Text vergleichen, nie als Zahl. (06.10.2026) - **VLN-Upload ersetzt den Serverbestand der VKZ vollständig** (`PUT`). Vor dem Upload sicherstellen, dass alle Teillayer geladen sind. - **API-Key liegt in `QSettings`** (Registry/Konfigurationsdatei), kein sicherer Schlüsselspeicher. Niemals in Logs oder Tests ausgeben. ## Code-Konventionen, die man kennen muss +- **`layer.extent()` steht im KBS des Layers, nicht im Projekt-KBS.** Solange das Projekt in EPSG:25833 steht wie + alle sächsischen Daten, fällt das nicht auf. Mit einem anderen Projekt-KBS landen `canvas.setExtent(layer.extent())` + und `map_item.setExtent(layer.extent())` an der falschen Stelle; die Layer selbst rendert QGIS trotzdem richtig + (Umprojektion beim Zeichnen). Immer `zoom_auf_layer` bzw. `transformiere_rechteck` verwenden (Basis #99). + Layoutkarten ohne `setCrs` folgen dem Projekt-KBS; Gitter (500 Karteneinheiten) und Maßstab setzen Meter voraus. + (05.10.2026) +- **Dock-Inhalt ohne Scroll-Bereich verlängert das QGIS-Fenster.** Qt verlangt für ein Dock mindestens die Höhe seines + Inhalts (beim `QTabWidget`: des höchsten Tabs). Ist sie größer als der Bildschirm, wächst das Hauptfenster nach unten + aus dem Bild, statt dass ein Scrollbalken erscheint. Abhilfe: Inhalt in `QScrollArea` mit `setWidgetResizable(True)`; + in den LNO-Docks erledigt das `BaseDockWidget.setWidget` (Basis #104). Die VLN-Plugins („VLN Karte“ 2.0, „VLN Karten“ + 0.5) haben keine eigene Lösung, ihr Inhalt ist nur kurz genug. (05.10.2026) +- **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das + Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über + `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))` + berechnen; für mehrere Spalten zusätzlich `setSplitLayer(True)`, sonst bleibt ein Layer in einer Spalte. + Eine Legende mit `setLegendFilterByMapEnabled(True)` blendet Layer aus, die nicht in der Karte liegen, und ihre + Höhe steht erst nach dem Rendern fest. (02.10.2026, Spike Basis #88) + Auch `QgsLegendRenderer.minimumSize` liefert vor dem ersten Zeichnen die **ungefilterte** Höhe (Beispiel: 344 statt + 177 mm). Verlässlich vermessen: Legende einmal mit geringer Auflösung zeichnen + (`QgsLayoutExporter(layout).renderRegionToImage(legende.sceneBoundingRect(), QSize(), 25)`), danach wirken + Filter und `adjustBoxSize`. (02.10.2026, Basis #93) + Mehrspaltige Legenden: Mit `setEqualColumnWidth(True)` bestimmt der längste Text die Breite **aller** Spalten. + Schmaler wird es mit ungleichen Spalten und `setAutoWrapLinesAfter(mm)`; der Legendentitel bricht dabei **nicht** + automatisch um und bestimmt die Mindestbreite – Zeilenumbruch (`\n`) im Titel setzen. (02.10.2026, Basis #96) + `setMinimumSize` an Layoutelementen wird **nicht im Projekt gespeichert**: Nach dem Neuladen schrumpft eine + Legende mit „an Inhalt anpassen“ bei der nächsten Größenberechnung wieder (gemessen 180 → 43,7 mm). Mindestgröße + am Layout vermerken und beim Laden (`readProject`) neu setzen. (02.10.2026, Basis #96) - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen). - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen. - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe. diff --git a/docs/wissen/glossar.md b/docs/wissen/glossar.md index cc41149..ae74472 100644 --- a/docs/wissen/glossar.md +++ b/docs/wissen/glossar.md @@ -36,4 +36,7 @@ Flurbereinigungs-Hintergrund. Ergänzen, wenn ein Begriff im Issue erklärt werd | **Atlas** | QGIS-Serienkarte über einen Abdeckungslayer (`sn_abdeckungslayer`) | | **Kanal** | Release-Kanal: stable, testing, unstable (QGIS-Plugin-Feeds in `Repository`) | | **Mock-Modus** | Betrieb der Wrapper ohne QGIS/Qt für Tests | +| **basemap.de** | Bundesweite Hintergrundkarte der Vermessungsverwaltungen; in `sn_basis` als Vektorkachel-Layer genutzt (Basemap-Legende, Basemap-Bereich im Druck-Tab) | +| **Thema (Basemap)** | Vom Dienst basemap.de als Style-JSON bereitgestellte Darstellung: Farbe, Grau (Relief vorerst nicht). Nicht verwechseln mit *Stil* und mit QGIS-Kartenthemen | +| **Stil (Basemap)** | Lokale Anpassung der Darstellung durch den Nutzer, als Stilvorlage im QGIS-Profil gespeichert; vermerkt, zu welchem Thema sie gehört | | **AG QGIS** | Arbeitsgruppe QGIS der sächsischen Flurbereinigungsbehörden; Gitea-Organisation `AG_QGIS` |