Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17cb471fe7 | ||
|
|
abac5a015c | ||
|
|
5e87f2367e | ||
|
|
9046f929f7 | ||
|
|
aa7fe5b2e4 | ||
|
|
44cce10bda | ||
|
|
cf46d3ad1c | ||
|
|
7b0b7ea6c4 | ||
|
|
b6d3ba13dd | ||
|
|
1d3c98be11 | ||
|
|
7c0c38f937 | ||
|
|
8d317d33d3 | ||
|
|
d1d4469dc9 | ||
|
|
1b926b1b5c | ||
|
|
8a7348e38e | ||
|
|
05e36abe0d | ||
|
|
864026f50c | ||
|
|
7dd9b74961 | ||
|
|
e5c7d17831 | ||
|
|
c1c628ac97 | ||
|
|
b275df770e | ||
|
|
67f11e0da7 | ||
|
|
6b9fed933b | ||
|
|
0a81fd75fa | ||
|
|
4779624b14 | ||
|
|
00f770a22e | ||
|
|
9c7c4b900d | ||
|
|
77bf55500f | ||
|
|
99a8156036 | ||
|
|
d93ef2c833 | ||
|
|
c92a2ddb86 | ||
|
|
d965dd56c0 | ||
|
|
a1f399b150 | ||
|
|
bd8b215ddd |
@@ -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 |
|
||||
|
||||
@@ -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 \| <Untertitel>“, 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 \| <Untertitel>“, 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/` | 13 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 `<Eigentümerdatei>_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 |
|
||||
|
||||
@@ -129,12 +146,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 |
|
||||
|
||||
@@ -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 = <Layoutname>` 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 `<QGIS-Profil>/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
|
||||
`<name>.qml` und optional `<name>.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).
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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 |
|
||||
|
||||
@@ -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<br/>oder Basemap-Legende aktualisieren] --> B{Layout nutzt<br/>Basemap-Legende?}
|
||||
B -- nein --> Z[Layout wie bisher]
|
||||
B -- ja --> C{Sichtbarer Basemap-Layer<br/>im Projekt?}
|
||||
C -- nein --> Z
|
||||
C -- ja --> D[Auswertebereich bestimmen<br/>Verfahrensgebiet / Druckbereich]
|
||||
D --> E[Geometrie nach EPSG:3857,<br/>kachelweise selectByGeometry<br/>mit Fortschritt und Abbruch]
|
||||
E --> F[Feature → aktive Renderer-Styles<br/>bei Druck-Zoomstufe]
|
||||
F --> G[Styles → Legendeneinträge<br/>über Legendenkatalog]
|
||||
G --> H[Auswahldialog mit Symbolvorschau<br/>Vorbelegung aus Projektvariable]
|
||||
H --> I[Gruppe Basemap in der Legende<br/>zusammengesetzte Symbole]
|
||||
I --> J[Auswahl und Einstellungen<br/>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, `<Profilordner>/sn_basis/basemap_stile/<name>.qml` + `<name>.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**
|
||||
(`<name>.qml` + `<name>.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 (`<qgis` am Anfang) und höchstens 100 MB groß ist; ZIP-Einträge werden nie unter ihrem
|
||||
eigenen Pfad entpackt.
|
||||
- **Später möglich, nicht Teil dieses Tickets:** mitgelieferte Varianten im Plugin (`styles/basemap/`) und ein
|
||||
gemeinsamer Ordner (Netzlaufwerk) für die ganze Behörde (Export/Import seit #101 deckt die Weitergabe von Hand ab).
|
||||
|
||||
### 5.4 Werkzeug im Projektwissen
|
||||
|
||||
`scripts/basemap_katalog.py` (neu, nur Standardbibliothek): lädt `bm_web_col.json`, gruppiert Style-IDs nach
|
||||
Quell-Layer und `klasse` (Präfixe/Suffixe wie `Kontur_`, `Decker_`, `Bruecke_`, `Tunnel_`, `_getrennt`,
|
||||
`_Fahrbahnachse`, `_FSZ1` werden zusammengefasst), schlägt `symbol_stile` vor und schreibt einen Entwurf von
|
||||
`basemap_legende.json`. Bestehende fachliche Nachbearbeitung (Bezeichnungen, Gruppen, Reihenfolge) bleibt bei
|
||||
einem erneuten Lauf erhalten; neue oder entfallene Style-IDs werden gemeldet.
|
||||
|
||||
### 5.5 Neue Schnittstellen (in `schnittstellen.md` nachtragen)
|
||||
|
||||
| Art | Schlüssel | Bedeutung |
|
||||
|---|---|---|
|
||||
| Projektvariable | `basemap_auswertebereich` | `verfahrensgebiet` (Standard) oder `druckbereich` |
|
||||
| Projektvariable | `basemap_legende_erzeugen` | `1`/`0`, Standard `1` |
|
||||
| Projektvariable | `basemap_legende_auswahl` | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` – zuletzt ab-/angewählte Einträge (Vorbelegung) |
|
||||
| Layout-Eigenschaft | `sn_basemap_legende`, `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` | Kennzeichen und letzte Einstellungen je Layout |
|
||||
| QSettings | `sn_basis/basemap_standardstil` | Name der Standard-Stilvorlage |
|
||||
| Öffentliche Funktion | `dialog_wrapper.ask_mehrfachauswahl(...)` | neuer allgemeiner Dialog |
|
||||
|
||||
## 6. Tests (Mock-Modus, ohne Netzwerk)
|
||||
|
||||
Jede neue beobachtbare Funktion bekommt mindestens einen Test mit deutschem Docstring („Prüft, dass …").
|
||||
|
||||
| Bereich | Testfälle |
|
||||
|---|---|
|
||||
| Katalog | Laden und Schema; jede Style-ID höchstens einem Eintrag zugeordnet; `symbol_stile` ⊆ `treffer_stile`; Ausschlussliste greift |
|
||||
| Zuordnung | Mehrere Styles → ein Eintrag; deaktivierte Styles ignoriert; Zoombereich berücksichtigt; Filterprüfung als übergebene Funktion; unbekannte Style-IDs → „Sonstiges", abgewählt vorbelegt; Reihenfolge nach Gruppe/Reihenfolge |
|
||||
| Layer-Erkennung | Erkennung über Quell-URL; Name nur als Rückfall; unsichtbarer Layer ignoriert; kein Basemap-Layer → Druck unverändert |
|
||||
| Auswertebereich | Verfahrensgebiet als Standard; Druckbereich bei Einstellung; ohne Verfahrensgebiet Rückfall mit Hinweis; Auswahl des Layers danach wiederhergestellt |
|
||||
| Dialog | Mock gibt Vorauswahl zurück; Abbruch → keine Basemap-Gruppe, Vorlage trotzdem erzeugt |
|
||||
| Layout | Kennzeichen nur bei § 38/§ 41; Gruppe „Basemap" wird beim Aktualisieren ersetzt, nicht verdoppelt; Basemap-Layer-Knoten ausgeblendet |
|
||||
| Projektvariablen | Auswahl und Auswertebereich werden gespeichert und wieder gelesen |
|
||||
| Stilvorlagen | Speichern, Auflisten, Laden, Löschen in temporärem Profilordner; Name über `sanitize_bezeichner`; Überschreiben nur nach Bestätigung; Standard in QSettings; fehlende Begleitdatei toleriert |
|
||||
| Katalog-Skript | Gruppierung Kontur/Decker/Brücke zu einem Eintrag an einem Ausschnitt der Style-JSON (Testdaten im Repo, kein Download) |
|
||||
|
||||
**Nur in QGIS prüfbar** (Ergebnis im PR dokumentieren): echte Kachelauswertung, Laufzeit, Aussehen der Symbole
|
||||
in der Legende, QML-Laden/Speichern am Vektorkachel-Layer, Einklappen des Bereichs unter Qt5 und Qt6.
|
||||
|
||||
## 7. Umsetzungsschritte
|
||||
|
||||
Ein Feature-Branch `feature/88-basemap-legende` in `Plugin_SN_Basis` von `unstable`, Commits je Schritt.
|
||||
|
||||
1. **Spike in QGIS** (Python-Konsole, echtes Verfahrensgebiet, QGIS 3.44 und 4.x). Ergebnis als ADR in
|
||||
`docs/entscheidungen/`:
|
||||
- Laufzeit und Kachelzahl von `selectByGeometry()` für ein großes Verfahrensgebiet; kachelweise Aufteilung.
|
||||
- Auswertung der Style-Filter mit `@vector_tile_zoom`.
|
||||
- **Darstellung in der Legende:** (a) leere Hilfslayer (Speicherlayer je Geometrietyp, kategorisiert, nicht im
|
||||
Layerbaum) als Gruppe „Basemap" in der Hauptlegende – offen, ob `setLegendFilterByMapEnabled(True)` sie
|
||||
wegfiltert; oder (b) eigenes Legendenelement „Basemap" ohne Kartenfilter direkt unter der Hauptlegende.
|
||||
- Zusammengesetzte Symbole und festgeschriebene Strichbreiten in der Legende.
|
||||
- `saveNamedStyle`/`loadNamedStyle` am Vektorkachel-Layer inklusive deaktivierter Styles und Beschriftung.
|
||||
- `QgsCollapsibleGroupBox` unter Qt5/Qt6 verfügbar, sonst Muster des Atlas-Bereichs (Kontrollkästchen
|
||||
blendet Inhalt ein).
|
||||
2. **Legendenkatalog:** Skript, erster Entwurf, fachliche Durchsicht der Bezeichnungen und Gruppen.
|
||||
3. **Auswertung und Zuordnung** (`basemap_legende.py`) mit Tests.
|
||||
4. **Dialog** `ask_mehrfachauswahl` mit Test.
|
||||
5. **Layout-Anbindung** in `print_logic`/`print_layout` für § 38/§ 41, Projektvariablen.
|
||||
6. **Basemap-Bereich im Druck-Tab** mit Aktualisieren-Knopf.
|
||||
7. **Stilvorlagen** (`basemap_stilvorlagen.py`, Teil des Bereichs).
|
||||
8. `python scripts/run_tests.py sn_basis --baseline`, `python scripts/testkatalog.py`,
|
||||
`python scripts/pruefe_umlaute.py Plugin_SN_Basis`, QGIS-Abnahme, `changelog.txt`, PR-Text, Übergabe:
|
||||
`schnittstellen.md`, `modulkarte.md`, `fallstricke.md` (Katalog hängt an Stil-Version v2), `STATUS.md`.
|
||||
|
||||
## 8. Abnahmekriterien
|
||||
|
||||
- [ ] Beim Erstellen einer § 38- oder § 41-Vorlage mit sichtbarem Basemap-Layer erscheint der Auswahldialog mit
|
||||
genau den Kategorien, die im Auswertebereich vorkommen und im Projektlayer aktiv sind.
|
||||
- [ ] Die gewählten Kategorien stehen als Gruppe „Basemap" mit der Signatur des Projektlayers in der Legende;
|
||||
zusammengesetzte Signaturen (Straßen) sind als ein Symbol dargestellt.
|
||||
- [ ] Beschriftungen, Hausnummern und Hintergrund erscheinen nie.
|
||||
- [ ] Ohne Basemap-Layer, bei Abbruch des Dialogs und bei „Anlage zur Baufreigabe" entsteht die Vorlage wie bisher.
|
||||
- [ ] Auswertebereich ist im Basemap-Bereich auf Druckbereich umstellbar; die Einstellung bleibt im Projekt erhalten.
|
||||
- [ ] „Basemap-Legende aktualisieren" ersetzt die Gruppe in einer bestehenden Vorlage, ohne sie zu verdoppeln.
|
||||
- [ ] Stilvorlagen lassen sich unter eigenem Namen speichern, laden, löschen und als Standard markieren; mehrere
|
||||
Varianten sind möglich; nach dem Laden einer Variante ohne Hausnummern fehlen diese in Karte und Legende.
|
||||
- [ ] Neue Tests grün, Baseline nicht verschlechtert, Testkatalog aktualisiert, Ablauf in QGIS 3.44 und 4.x geprüft.
|
||||
|
||||
## 9. Risiken
|
||||
|
||||
- **Blockierender Netzwerkzugriff** im Hauptthread bei großen Gebieten → kachelweise mit Fortschrittsanzeige und
|
||||
Abbruch; Hinweis bei mehr als z. B. 400 Kacheln.
|
||||
- **Neue Basemap-Stilversion** (heute v2) → Katalog veraltet; Skript erneut laufen lassen, unbekannte IDs fallen
|
||||
nach „Sonstiges".
|
||||
- **Kachelränder:** Features sind an Kachelgrenzen beschnitten und können mehrfach vorkommen – für die Frage
|
||||
„kommt vor" unerheblich.
|
||||
- **Umfang:** Stilvorlagen sind fachlich eigenständig. Werden sie zu groß für einen PR, als eigenes Ticket
|
||||
abtrennen (Schritte 1–6 bleiben #88).
|
||||
|
||||
## 10. Umsetzung und Abweichungen (02.10.2026)
|
||||
|
||||
Umgesetzt in `Plugin_SN_Basis`, Branch `feature/88-basemap-legende` (drei Commits). Spike-Ergebnisse und
|
||||
Begründung der Legendenvariante: ADR 0010.
|
||||
|
||||
| Thema | Umsetzung | Abweichung vom Konzept |
|
||||
|---|---|---|
|
||||
| Legendendarstellung | Variante (b): eigenes Element „Legende Basemap“, Titel „Kartengrundlage (basemap.de)“ | Spike entschieden |
|
||||
| Platzierung | unten an der Quellenangabe verankert, wächst nach oben, 1–3 Spalten (nie über den Seitenrand), sonst Hinweis | statt „unter der Hauptlegende“: deren Höhe steht erst nach dem Rendern fest |
|
||||
| Mehrere Basemap-Layer | ausgewertet wird der erste sichtbare, Hinweis an den Nutzer | statt „je Layer eine Gruppe“ (vereinfacht) |
|
||||
| Katalog-Generator | `sn_basis/modules/basemap_katalog.py` (nur Standardbibliothek, eigenständig aufrufbar) | statt Skript im Projektwissen – so mit der sn_basis-Suite getestet |
|
||||
| Auswahl im Projekt | `basemap_legende_auswahl` (ab- und angewählte Einträge) | statt nur `…_ausgeblendet`, damit angewählte „Sonstiges“-Einträge gemerkt werden |
|
||||
| Module | `basemap_katalog`, `basemap_kacheln`, `basemap_legende`, `basemap_legende_layout`, `basemap_stilvorlagen`, `ui/widgets/basemap_bereich` | Kachelauswertung ausgelagert (Dateigröße) |
|
||||
| Einklappen | `QToolButton`-Muster wie Atlas-Bereich | `QgsCollapsibleGroupBox` nicht nötig |
|
||||
| Stilvorlagen | QML (rund 3,7 MB je Vorlage) + JSON mit Legendenauswahl, im QGIS-Profil | wie geplant |
|
||||
|
||||
**Katalog:** 434 darstellende Styles in 190 Einträgen; Beschriftungen (`Name_*`, Labeling), Nummernschilder
|
||||
(`Nummer_*`), Hintergrund, Mittellinien und kleinmaßstäbige Ortspunkte ausgeschlossen. Erster fachlicher
|
||||
Durchgang der Bezeichnungen ist gemacht; **die fachliche Abnahme steht aus.**
|
||||
|
||||
**Bekannte Punkte für die Durchsicht:**
|
||||
- ~~„Platz“ (Verkehr, Siedlung) in der Legende kaum zu sehen~~ – behoben in **Basis #92**: weiße Flächen ohne Kontur
|
||||
bekommen im Legendensymbol einen grauen Rand (0,15 mm); die Karte bleibt unverändert.
|
||||
- ~~„Durchlass, Düker“ und „Baumreihe“ ohne sichtbares Symbol~~ – Ursache gefunden und behoben in **Basis #90**:
|
||||
basemap.de zeichnet Punktlinien mit Strichlänge 0, die Qt nicht zeichnet (auch nicht in der Karte). Knopf
|
||||
„Punktlinien korrigieren“ im Basemap-Bereich (12 Styles betroffen).
|
||||
- Gewässerbreiten sind zu „Fließgewässer (Linie)“ zusammengefasst; Symbol ist die 6-m-Linie.
|
||||
|
||||
**Nicht geprüft:** QGIS 4 (Qt6) – lokal nicht verfügbar. QGIS 3.44.4 (Qt5) offscreen geprüft: Auswertung,
|
||||
Dialog (echte Widgets), Legende im §-41-Layout, Stilvorlage speichern/laden am echten Layer, Basemap-Bereich.
|
||||
+21
-2
@@ -1,7 +1,7 @@
|
||||
# Status des Projekts
|
||||
|
||||
Kurz und aktuell. Wird bei jedem Merge und jeder Übergabe gepflegt (Skill `sn-uebergabe`). Älteres wandert nach unten
|
||||
in „Erledigt" oder wird gelöscht. **Stand: 04.09.2026.**
|
||||
in „Erledigt" oder wird gelöscht. **Stand: 02.10.2026.**
|
||||
|
||||
## Aktuelle Phase
|
||||
|
||||
@@ -13,7 +13,7 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
|
||||
|
||||
| Person / Behörde | Schwerpunkt | Stand |
|
||||
|---|---|---|
|
||||
| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API | aktiv |
|
||||
| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API; Basemap-Legende im Druck (Basis #88, PR vorbereitet) | aktiv |
|
||||
| Michael Otto (LK Mittelsachsen) | Release-Infrastruktur, Linkliste, zuletzt Verfahrensgebiet (Shape-Laden, Beteiligung) | aktiv |
|
||||
| 22munzke | Druck/Layout-Anforderungen (Issues Basis #37–#44) | Issues |
|
||||
| weitere Behörden | Einstieg über dieses Repository | ab 09/2026 |
|
||||
@@ -63,6 +63,25 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
|
||||
(fest verdrahteter NAS-Namespace `gid/6.0` in `grenzpunkt_alkis.py`, abgeflachte WFS-Feldnamen in
|
||||
`sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`.
|
||||
Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`.
|
||||
15. **Basemap-Legende im Druck (Basis #88).** In `unstable` gemergt: #88 (PR #89), Punktlinien-Korrektur #90
|
||||
(PR #91), Rand für weiße Flächen #92. Offen: **fachliche Abnahme des Legendenkatalogs** `sn_basis/data/basemap_legende.json`
|
||||
(Bezeichnungen, Gruppen, Zusammenfassungen; Konzept `docs/konzept/10_basemap_legende.md` Abschnitt 10,
|
||||
ADR 0010; bisher unauffällig). Unter QGIS 4 geprüft (Plugin funktioniert, Rückmeldung 02.10.2026).
|
||||
16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler
|
||||
(#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das
|
||||
Kartenschild) auf
|
||||
`Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, gemergt (PR #97).
|
||||
17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Gemergt (PR #103, Doku PR #25).
|
||||
Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
|
||||
am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
|
||||
(Import/Export der Stile), P10 Vektor (#102: technisch geprüft 06.10.2026, fachliche Entscheidung offen –
|
||||
Beta-Lizenz nur zu Testzwecken, siehe `fallstricke.md`), Relief.
|
||||
18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Gemergt (PR #105, Doku PR #26): jeder Tab im
|
||||
Scroll-Bereich über `BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet.
|
||||
19. **Zoom und Drucklayout bei anderem Projekt-KBS (Basis #99).** Behoben auf `Plugin_SN_Basis:bugfix/99-druck-kbs`
|
||||
und `Plugin_SN_Verfahrensgebiet:bugfix/99-zoom-kbs` (Doku: `Projektwissen:docs/99-druck-kbs`), PRs stehen aus;
|
||||
**Basis zuerst mergen** (Verfahrensgebiet nutzt `zoom_auf_layer`). Layoutkarte im KBS des Verfahrensgebiets.
|
||||
Offen: QGIS-Test. Folge-Issue (Entwurf): Projekt-KBS prüfen und amtliches KBS nach Länderkennung vorschlagen.
|
||||
|
||||
**Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
|
||||
und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
|
||||
|
||||
+1510
-55
File diff suppressed because one or more lines are too long
+1072
-238
File diff suppressed because it is too large
Load Diff
@@ -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;<br/>Bearbeiter schiebt es auf den zweiten Bildschirm"]
|
||||
T["Trefferliste: Einträge wählen,<br/>„Daten anzeigen“"] --> C{"Sitzung läuft?"}
|
||||
C -->|nein| A
|
||||
C -->|ja| D["3 Eigentümer sieht Name,<br/>Anschrift, E-Mail"]
|
||||
B --> D
|
||||
D --> E{"4 Daten richtig?"}
|
||||
E -->|nein| F["5 Korrigieren: Änderung<br/>in Korrektur-CSV, sofort angezeigt"]
|
||||
F --> E
|
||||
E -->|ja| G["6 Anzeige leeren<br/>(Fenster bleibt offen)"]
|
||||
G --> T
|
||||
G --> H["7 Sitzung beenden:<br/>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 |
|
||||
@@ -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<br/>als CSV-Layer ins Projekt"] --> B["2 Eigentümerdatei wählen<br/>(nur Layer mit Nachname und Vorname)"]
|
||||
B --> C{"3 Eigentümerdatei<br/>gewählt?"}
|
||||
C -->|nein| D["Hinweis: CSV laden und wählen"]
|
||||
C -->|ja| E{"4 Name oder Vorname<br/>eingegeben?"}
|
||||
E -->|nein| F["Hinweis: Namen eingeben"]
|
||||
E -->|ja| G["5 Einträge suchen<br/>(Teilwort, Groß-/Kleinschreibung<br/>und Umlaute egal)"]
|
||||
G --> H["6 Gleiche Person mit gleicher<br/>Anschrift zusammenfassen"]
|
||||
H --> I{"7 Treffer?"}
|
||||
I -->|nein| J["Hinweis: keine Einträge<br/>(ohne den Namen zu wiederholen)"]
|
||||
I -->|ja| K["8 Trefferliste mit Anschrift, E-Mail,<br/>Rechtsperson-Wert, verstorben;<br/>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 |
|
||||
@@ -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<br/>und Flurstücksnummer ein"] --> B{"2 Flurstückslayer<br/>im Projekt?"}
|
||||
B -->|nein| C["Hinweis: zuerst Verfahrensgebiet<br/>und Flurstücke aus ALKIS laden"]
|
||||
B -->|ja| D{"3 Flurstücksnummer lesbar?<br/>z. B. 562 oder 12/3"}
|
||||
D -->|nein| E["Hinweis zur Schreibweise"]
|
||||
D -->|ja| F{"4 Gemarkung als Nummer<br/>oder als Name?"}
|
||||
F -->|Nummer| H["5 Flurstücke vergleichen"]
|
||||
F -->|Name| G{"Gemarkungslayer geladen<br/>und Name darin?"}
|
||||
G -->|nein| E2["Hinweis: Nummer eingeben<br/>oder Gemarkungen laden"]
|
||||
G -->|ja| H
|
||||
H --> I{"6 Treffer?"}
|
||||
I -->|nein| J["Hinweis: nicht gefunden"]
|
||||
I -->|ja| K["7 Treffer auswählen,<br/>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 |
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -19,6 +26,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 <tmp>\sn_basis <Plugin_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)
|
||||
@@ -40,16 +53,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 (`<Eigentümerdatei>_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.
|
||||
|
||||
@@ -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` |
|
||||
|
||||
Reference in New Issue
Block a user