BROR-Mehrfachauswahl, zweistufiger Fortschrittsdialog, Landkreis-Fallback
- Fortschrittsdialog beim Laden aus ALKIS schließt jetzt zuverlässig bei jedem Abbruch (progress.close() zentral im finally-Block). - Fortschrittsanzeige in zwei Dialoge aufgeteilt: Suche (1 Schritt) läuft und schließt separat, bevor der eigentliche Workflow (Gemarkung/ Flurstücke/Vergleich, 6 Schritte) startet – auch während des BROR- Auswahldialogs läuft kein störender Hintergrund-Fortschrittsbalken mehr. - verfahrensgebiet_alkis() in Suche-/Auswahl-Phasen aufgeteilt, damit mehrere gefundene BauRaumOderBodenordnungsrecht-Objekte gemeinsam ausgewählt und verschmolzen werden können (öffentliche Signatur unverändert). - Neu: Landkreis-weite Suche als Fallback, wenn keine Verfahrensnummer gesetzt ist (Landkreiskennzahl aus Projektvariable), mit Mehrfach- auswahl/"Alle auswählen" und "Nur laden"-Option (überspringt den restlichen Workflow). Gruppierung der Auswahl nach VKZ, damit je Verfahren ein eigenes Feature entsteht statt eines Gesamt-Blobs. - Tests entsprechend angepasst/ergänzt (29 Tests in test_verfahrensgebiet_alkis_komplett.py, davon 3 neu für den Landkreis-Fallback).
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
- Fortschrittsdialog beim Laden aus ALKIS schließt jetzt zuverlässig bei jedem Abbruch (Bugfix)
|
||||
- Fortschrittsanzeige beim Laden aus ALKIS in zwei Dialoge aufgeteilt (Suche / eigentlicher Ladevorgang), inkl. eigenem Suchschritt
|
||||
- Mehrfachauswahl bei mehreren BauRaumOderBodenordnungsrecht-Treffern zur Verfahrensnummer (gemeinsames Laden/Verschmelzen mehrerer Objekte)
|
||||
- Neu: Laden aller/mehrerer Objekte eines ganzen Landkreises als Fallback, wenn keine Verfahrensnummer gesetzt ist (Gruppierung je Verfahren, optional "Nur laden" ohne weiteren Ablauf)
|
||||
---
|
||||
Version 26.7.1-unstable:
|
||||
- Verfahrensgebiet als shape laden, inkl. Abgleich mit BROBOR-Objekt
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ from sn_basis.functions import (
|
||||
from sn_basis.functions import QMessageBox
|
||||
from sn_basis.functions.qt_wrapper import QVariant
|
||||
from sn_basis.functions.variable_wrapper import get_variable
|
||||
from sn_basis.functions.dialog_wrapper import ask_bror_kombination
|
||||
from sn_basis.functions.dialog_wrapper import ask_bror_kombinationen, ask_landkreis_objekte
|
||||
|
||||
alkis_sf_url = "https://geodienste.sachsen.de/aaa/public_alkis/sf/wfs"
|
||||
typename = "adv:AX_BauRaumOderBodenordnungsrecht"
|
||||
@@ -28,7 +28,7 @@ BROR_ATTRIBUTE_FELDER = [
|
||||
|
||||
class LoadStatus(Enum):
|
||||
"""Status eines Layer-Ladevorgangs.
|
||||
|
||||
|
||||
Attributes:
|
||||
NONE: Nichts geladen
|
||||
FIRST: Erstmalig geladen
|
||||
@@ -44,10 +44,10 @@ class LoadStatus(Enum):
|
||||
|
||||
def _check_existing_layer(tab_widget):
|
||||
"""Prüft, ob ein Verfahrensgebiet-Layer bereits existiert.
|
||||
|
||||
|
||||
Args:
|
||||
tab_widget: Parent-Widget für Dialoge
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus]: Layer und Status
|
||||
"""
|
||||
@@ -72,32 +72,36 @@ def _check_existing_layer(tab_widget):
|
||||
return existing_layers[0], LoadStatus.KEEP
|
||||
|
||||
|
||||
def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
"""Lädt Verfahrensgebiet aus ALKIS-WFS basierend auf Verfahrensnummer.
|
||||
def _suche_verfahrensgebiet_features(tab_widget, skip_existing_check: bool = False):
|
||||
"""Phase 1 ("Suchen"): vorhandenen Layer prüfen, WFS abfragen, nach
|
||||
(adv_name, bezeichnung) gruppieren.
|
||||
|
||||
Args:
|
||||
tab_widget: Parent-Widget für Dialoge und Meldungen
|
||||
skip_existing_check: Überspringt die Nachfrage bei bereits vorhandenem
|
||||
Layer (immer neu laden).
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, List[QgsFeature]]:
|
||||
Geladener Memory-Layer, Lade-Status und Liste der Roh-WFS-Features
|
||||
(für geometriebasierte Weiterverarbeitung, z.B. Gemarkungsschlüssel-
|
||||
Ermittlung via Spatial-Join). Bei Abbruch oder Fehler: ``(None,
|
||||
LoadStatus.NONE, [])``. Bei ``LoadStatus.KEEP`` werden keine
|
||||
WFS-Features neu geladen; die Feature-Liste ist in diesem Fall leer.
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, dict, Optional[str]]:
|
||||
``(fertig_layer, status, kombinationen, crs)``.
|
||||
Bei ``KEEP`` oder Fehler/Abbruch ist ``fertig_layer`` bereits das
|
||||
Endergebnis (ggf. ``None``) und ``kombinationen`` leer – Phase 2
|
||||
entfällt dann. Bei Erfolg mit mindestens einer Kombination ist
|
||||
``fertig_layer`` ``None`` und ``kombinationen``/``crs`` sind für
|
||||
Phase 2 (:func:`_baue_layer_aus_kombinationen`) befüllt.
|
||||
"""
|
||||
verfahrensnummer = get_variable("verfahrensnummer")
|
||||
if not verfahrensnummer:
|
||||
QMessageBox.critical(tab_widget, "Fehler",
|
||||
"In den Projekteigenschaften ist noch keine Verfahrensnummer eingetragen!")
|
||||
return None, LoadStatus.NONE, []
|
||||
return None, LoadStatus.NONE, {}, None
|
||||
|
||||
# Vorhandenen Layer prüfen (überspringbar für die Shape-Pipeline)
|
||||
status = LoadStatus.FIRST
|
||||
if not skip_existing_check:
|
||||
existing, status = _check_existing_layer(tab_widget)
|
||||
if status == LoadStatus.KEEP:
|
||||
return existing, status, []
|
||||
return existing, status, {}, None
|
||||
|
||||
# WFS mit Filter laden – nur erste 5 Stellen der VKZ für den Vergleich verwenden,
|
||||
# da die Daten im Dienst ggf. abweichende Suffixe führen.
|
||||
@@ -111,13 +115,13 @@ def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
|
||||
if not temp_layer.isValid():
|
||||
QMessageBox.critical(tab_widget, "Fehler", "Layer konnte nicht geladen werden.")
|
||||
return None, LoadStatus.NONE, []
|
||||
return None, LoadStatus.NONE, {}, None
|
||||
|
||||
features = list(temp_layer.getFeatures())
|
||||
if not features:
|
||||
QMessageBox.critical(tab_widget, "Fehler",
|
||||
f"Verfahrenskennzeichen {verfahrensnummer} nicht im WFS gefunden.")
|
||||
return None, LoadStatus.NONE, []
|
||||
return None, LoadStatus.NONE, {}, None
|
||||
|
||||
# Objekte nach (adv_name, bezeichnung) gruppieren
|
||||
kombinationen: dict[tuple[str, str], list] = {}
|
||||
@@ -133,16 +137,32 @@ def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
kombinationen[key] = []
|
||||
kombinationen[key].append(feat)
|
||||
|
||||
return None, status, kombinationen, temp_layer.crs().authid()
|
||||
|
||||
|
||||
def _baue_layer_aus_kombinationen(tab_widget, kombinationen: dict, crs: str, status: "LoadStatus"):
|
||||
"""Phase 2 ("Auswählen + Laden"): bei mehreren Kombinationen Auswahldialog
|
||||
zeigen, Geometrien der gewählten Kombination(en) verschmelzen, Memory-Layer bauen.
|
||||
|
||||
Args:
|
||||
tab_widget: Parent-Widget für Dialoge und Meldungen
|
||||
kombinationen: Ergebnis aus :func:`_suche_verfahrensgebiet_features`
|
||||
crs: CRS-Authid aus :func:`_suche_verfahrensgebiet_features`
|
||||
status: Lade-Status aus Phase 1 (FIRST/RELOAD), wird durchgereicht
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, List[QgsFeature]]
|
||||
"""
|
||||
if len(kombinationen) == 1:
|
||||
selected_key = next(iter(kombinationen))
|
||||
selected_feats = kombinationen[selected_key]
|
||||
selected_feats = next(iter(kombinationen.values()))
|
||||
else:
|
||||
# Mehrere Kombinationen → Nutzerauswahl
|
||||
# Mehrere Kombinationen → Nutzerauswahl, ein oder mehrere Objekte
|
||||
# können gemeinsam ausgewählt und geladen (verschmolzen) werden.
|
||||
key_list = sorted(kombinationen.keys())
|
||||
selected_key = ask_bror_kombination(tab_widget, key_list)
|
||||
if selected_key is None:
|
||||
selected_keys = ask_bror_kombinationen(tab_widget, key_list)
|
||||
if not selected_keys:
|
||||
return None, LoadStatus.NONE, []
|
||||
selected_feats = kombinationen[selected_key]
|
||||
selected_feats = [feat for key in selected_keys for feat in kombinationen[key]]
|
||||
|
||||
union_geom = QgsGeometry.unaryUnion([f.geometry() for f in selected_feats])
|
||||
if union_geom is None or union_geom.isEmpty():
|
||||
@@ -150,7 +170,6 @@ def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
return None, LoadStatus.NONE, []
|
||||
|
||||
# Memory-Layer erzeugen
|
||||
crs = temp_layer.crs().authid()
|
||||
memory_layer = QgsVectorLayer(f"Polygon?crs={crs}", "Verfahrensgebiet", "memory")
|
||||
pr = memory_layer.dataProvider()
|
||||
pr.addAttributes([QgsField(attr, QVariant.String) for attr in BROR_ATTRIBUTE_FELDER])
|
||||
@@ -180,3 +199,155 @@ def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
|
||||
# Status: FIRST oder RELOAD; selected_feats für Gemarkungsermittlung weitergeben
|
||||
return memory_layer, status, selected_feats
|
||||
|
||||
|
||||
def _suche_landkreis_kombinationen(tab_widget, landkreis_kennzahl: str, skip_existing_check: bool = False):
|
||||
"""Fallback-Suche über einen ganzen Landkreis (statt einer Verfahrensnummer).
|
||||
|
||||
Wie :func:`_suche_verfahrensgebiet_features`, aber mit WFS-Filter über die
|
||||
Landkreiskennzahl (``bezeichnung LIKE '{landkreis_kennzahl}%'``) statt
|
||||
über den 5-stelligen VKZ-Präfix. Gruppierung der Rohfeatures identisch:
|
||||
nach exaktem (adv_name, bezeichnung).
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, dict, Optional[str]]:
|
||||
Gleiche Bedeutung wie bei :func:`_suche_verfahrensgebiet_features`.
|
||||
"""
|
||||
status = LoadStatus.FIRST
|
||||
if not skip_existing_check:
|
||||
existing, status = _check_existing_layer(tab_widget)
|
||||
if status == LoadStatus.KEEP:
|
||||
return existing, status, {}, None
|
||||
|
||||
wfs_uri_find = (
|
||||
f"url='{alkis_sf_url}' typename='{typename}' srsname='EPSG:25833' "
|
||||
f"sql=SELECT * FROM AX_BauRaumOderBodenordnungsrecht "
|
||||
f"WHERE bezeichnung LIKE '{landkreis_kennzahl}%'"
|
||||
)
|
||||
temp_layer = QgsVectorLayer(wfs_uri_find, "TempVerfahrensgebiet", "WFS")
|
||||
|
||||
if not temp_layer.isValid():
|
||||
QMessageBox.critical(tab_widget, "Fehler", "Layer konnte nicht geladen werden.")
|
||||
return None, LoadStatus.NONE, {}, None
|
||||
|
||||
features = list(temp_layer.getFeatures())
|
||||
if not features:
|
||||
QMessageBox.critical(tab_widget, "Fehler",
|
||||
f"Für Landkreis {landkreis_kennzahl} wurden keine Objekte im WFS gefunden.")
|
||||
return None, LoadStatus.NONE, {}, None
|
||||
|
||||
kombinationen: dict[tuple[str, str], list] = {}
|
||||
for feat in features:
|
||||
try:
|
||||
adv_name_val = str(feat.attribute("adv_name") or "")
|
||||
bezeichnung_val = str(feat.attribute("bezeichnung") or "")
|
||||
except Exception:
|
||||
adv_name_val = ""
|
||||
bezeichnung_val = ""
|
||||
key = (adv_name_val, bezeichnung_val)
|
||||
if key not in kombinationen:
|
||||
kombinationen[key] = []
|
||||
kombinationen[key].append(feat)
|
||||
|
||||
return None, status, kombinationen, temp_layer.crs().authid()
|
||||
|
||||
|
||||
def _baue_layer_aus_landkreis_auswahl(tab_widget, kombinationen: dict, crs: str, status: "LoadStatus"):
|
||||
"""Auswahl + Bau für den Landkreis-Fallback.
|
||||
|
||||
Zeigt :func:`~sn_basis.functions.dialog_wrapper.ask_landkreis_objekte`
|
||||
(Mehrfachauswahl inkl. "Alle auswählen" und "Nur laden"-Checkbox). Die
|
||||
ausgewählten Kombinationen werden anhand der ersten 5 Zeichen von
|
||||
``bezeichnung`` (= die eigentliche VKZ) gruppiert; je Gruppe wird EIN
|
||||
Feature erzeugt (Geometrien der Gruppe verschmolzen). Damit entsteht bei
|
||||
Auswahl mehrerer/aller Objekte eines Landkreises ein Layer mit mehreren
|
||||
Features – einem je tatsächlichem Verfahren, nicht ein Gesamt-Blob.
|
||||
|
||||
Args:
|
||||
tab_widget: Parent-Widget für Dialoge und Meldungen
|
||||
kombinationen: Ergebnis aus :func:`_suche_landkreis_kombinationen`
|
||||
crs: CRS-Authid aus :func:`_suche_landkreis_kombinationen`
|
||||
status: Lade-Status aus Phase 1 (FIRST/RELOAD), wird durchgereicht
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, bool]:
|
||||
``(layer, status, nur_laden)``.
|
||||
"""
|
||||
key_list = sorted(kombinationen.keys())
|
||||
selected_keys, nur_laden = ask_landkreis_objekte(tab_widget, key_list)
|
||||
if not selected_keys:
|
||||
return None, LoadStatus.NONE, False
|
||||
|
||||
# Nach VKZ (erste 5 Zeichen der bezeichnung) gruppieren
|
||||
vkz_gruppen: dict[str, list] = {}
|
||||
for key in selected_keys:
|
||||
_, bezeichnung = key
|
||||
vkz = bezeichnung[:5]
|
||||
vkz_gruppen.setdefault(vkz, []).extend(kombinationen[key])
|
||||
|
||||
memory_layer = QgsVectorLayer(f"Polygon?crs={crs}", "Verfahrensgebiet", "memory")
|
||||
pr = memory_layer.dataProvider()
|
||||
pr.addAttributes([QgsField(attr, QVariant.String) for attr in BROR_ATTRIBUTE_FELDER])
|
||||
memory_layer.updateFields()
|
||||
|
||||
neue_features = []
|
||||
for vkz, feats in sorted(vkz_gruppen.items()):
|
||||
union_geom = QgsGeometry.unaryUnion([f.geometry() for f in feats])
|
||||
if union_geom is None or union_geom.isEmpty():
|
||||
continue
|
||||
|
||||
first_feat = feats[0]
|
||||
attrs = []
|
||||
for attr in BROR_ATTRIBUTE_FELDER:
|
||||
try:
|
||||
val = first_feat.attribute(attr)
|
||||
if val is None:
|
||||
attrs.append("")
|
||||
elif type(val).__name__ == "QDate":
|
||||
attrs.append(val.toString("dd.MM.yyyy"))
|
||||
else:
|
||||
attrs.append(str(val))
|
||||
except Exception:
|
||||
attrs.append("")
|
||||
|
||||
feat_out = QgsFeature()
|
||||
feat_out.setGeometry(union_geom)
|
||||
feat_out.setAttributes(attrs)
|
||||
neue_features.append(feat_out)
|
||||
|
||||
if not neue_features:
|
||||
QMessageBox.critical(tab_widget, "Fehler", "Keine Geometrien zum Verschmelzen gefunden.")
|
||||
return None, LoadStatus.NONE, False
|
||||
|
||||
pr.addFeatures(neue_features)
|
||||
memory_layer.updateExtents()
|
||||
|
||||
return memory_layer, status, nur_laden
|
||||
|
||||
|
||||
def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
|
||||
"""Lädt Verfahrensgebiet aus ALKIS-WFS basierend auf Verfahrensnummer.
|
||||
|
||||
Args:
|
||||
tab_widget: Parent-Widget für Dialoge und Meldungen
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[QgsVectorLayer], LoadStatus, List[QgsFeature]]:
|
||||
Geladener Memory-Layer, Lade-Status und Liste der Roh-WFS-Features
|
||||
(für geometriebasierte Weiterverarbeitung, z.B. Gemarkungsschlüssel-
|
||||
Ermittlung via Spatial-Join). Bei Abbruch oder Fehler: ``(None,
|
||||
LoadStatus.NONE, [])``. Bei ``LoadStatus.KEEP`` werden keine
|
||||
WFS-Features neu geladen; die Feature-Liste ist in diesem Fall leer.
|
||||
|
||||
Ruft intern nacheinander :func:`_suche_verfahrensgebiet_features` (Phase 1)
|
||||
und ggf. :func:`_baue_layer_aus_kombinationen` (Phase 2) auf – aufgeteilt,
|
||||
damit ``verfahrensgebiet_alkis_komplett()`` dazwischen einen eigenen
|
||||
Fortschrittsschritt anzeigen kann. Verhalten/Signatur dieser Funktion
|
||||
selbst ist unverändert.
|
||||
"""
|
||||
layer, status, kombinationen, crs = _suche_verfahrensgebiet_features(
|
||||
tab_widget, skip_existing_check
|
||||
)
|
||||
if layer is not None or not kombinationen:
|
||||
return layer, status, []
|
||||
return _baue_layer_aus_kombinationen(tab_widget, kombinationen, crs, status)
|
||||
|
||||
@@ -54,7 +54,10 @@ from sn_basis.functions.dialog_wrapper import (
|
||||
)
|
||||
|
||||
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import (
|
||||
verfahrensgebiet_alkis,
|
||||
_suche_verfahrensgebiet_features,
|
||||
_baue_layer_aus_kombinationen,
|
||||
_suche_landkreis_kombinationen,
|
||||
_baue_layer_aus_landkreis_auswahl,
|
||||
LoadStatus,
|
||||
)
|
||||
from sn_verfahrensgebiet.functions.gemarkung_alkis import (
|
||||
@@ -258,15 +261,25 @@ def verfahrensgebiet_alkis_komplett(
|
||||
"""Vollständiger VG-Lade-Workflow: ALKIS-VG → Gemarkungen →
|
||||
Flurstücke → Verschneidung → Flächenvergleich → opt. Umringsprüfung.
|
||||
|
||||
Schrittfolge:
|
||||
Zwei Fortschrittsdialoge nacheinander:
|
||||
|
||||
1. ``verfahrensgebiet_alkis(tab_widget)`` – BROR-Objekt als Memory-Layer.
|
||||
2. ``gemarkung_schluessel_und_namen_von_vg_geometrie(geom)`` – Schlüssel.
|
||||
3. ``flurstueck_alkis(...)`` – Flurstücke laden.
|
||||
4. ``_select_by_location(...)`` – räumliche Selektion.
|
||||
5. ``erstelle_verfahrensgebiet_aus_auswahl(...)`` – enriched Layer.
|
||||
6. ``_vergleiche_flaechen(...)`` – Flächenvergleich als Gate.
|
||||
7. Ggf. ``umringspruefung(...)`` nach Nutzerbestätigung.
|
||||
- **Dialog 1** ("Verfahrensgebiet(e) in ALKIS ermitteln/suchen"): läuft
|
||||
während ``_suche_verfahrensgebiet_features(tab_widget)`` (ALKIS-WFS
|
||||
durchsuchen) und wird danach immer geschlossen – auch wenn anschließend
|
||||
der BROR-Auswahldialog erscheint (kein Fortschrittsdialog läuft
|
||||
währenddessen parallel zum Auswahldialog).
|
||||
- **Dialog 2** (eigentlicher Workflow, 6 Schritte): startet erst, nachdem
|
||||
das Verfahrensgebiet feststeht (direkt nach Dialog 1 bei eindeutigem
|
||||
Treffer/KEEP, oder erst nach Bestätigung des Auswahldialogs mit OK).
|
||||
|
||||
Schrittfolge in Dialog 2:
|
||||
|
||||
1. ``gemarkung_schluessel_und_namen_von_vg_geometrie(geom)`` – Schlüssel.
|
||||
2. ``flurstueck_alkis(...)`` – Flurstücke laden.
|
||||
3. ``_select_by_location(...)`` – räumliche Selektion.
|
||||
4. ``erstelle_verfahrensgebiet_aus_auswahl(...)`` – enriched Layer.
|
||||
5. ``_vergleiche_flaechen(...)`` – Flächenvergleich als Gate.
|
||||
6. Ggf. ``umringspruefung(...)`` nach Nutzerbestätigung.
|
||||
|
||||
Der BROR-Layer (``BauRaumOderBodenordnungsrecht (ALKIS)``) bleibt bei Erfolg
|
||||
im Projekt. Bei Abbruch/Fehler wird er entfernt, falls er in diesem
|
||||
@@ -290,26 +303,74 @@ def verfahrensgebiet_alkis_komplett(
|
||||
alkis_vg_layer = None
|
||||
bror_was_added_by_us = False
|
||||
abgeschlossen = False
|
||||
progress = None
|
||||
|
||||
# Fortschrittsdialog für den Basisworkflow (7 Schritte)
|
||||
progress = create_progress_dialog(
|
||||
7, "Verfahrensgebiet laden", "Initialisierung …"
|
||||
)
|
||||
verfahrensnummer = get_variable("verfahrensnummer")
|
||||
landkreis_kennzahl = get_variable("landkreis_kennzahl")
|
||||
|
||||
if not verfahrensnummer and not landkreis_kennzahl:
|
||||
QMessageBox.critical(
|
||||
tab_widget,
|
||||
"Fehler",
|
||||
"Weder eine Verfahrensnummer noch ein Landkreis ist in den "
|
||||
"Projekteigenschaften eingetragen!",
|
||||
)
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
landkreis_modus = not verfahrensnummer
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Dialog 1: Verfahrensgebiet(e) in ALKIS ermitteln/suchen
|
||||
# ------------------------------------------------------------------
|
||||
if landkreis_modus:
|
||||
such_label = f"Objekte in Landkreis {landkreis_kennzahl} ermitteln/suchen …"
|
||||
else:
|
||||
such_label = "Verfahrensgebiet(e) in ALKIS ermitteln/suchen …"
|
||||
|
||||
progress_suche = create_progress_dialog(1, "Verfahrensgebiet laden", such_label)
|
||||
try:
|
||||
progress_suche.set_value(0)
|
||||
if progress_suche.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
if landkreis_modus:
|
||||
vorab_layer, vg_status, kombinationen, crs = _suche_landkreis_kombinationen(
|
||||
tab_widget, landkreis_kennzahl
|
||||
)
|
||||
else:
|
||||
vorab_layer, vg_status, kombinationen, crs = _suche_verfahrensgebiet_features(tab_widget)
|
||||
finally:
|
||||
# Dialog 1 schließt hier immer – egal ob eine Auswahl folgt oder nicht.
|
||||
progress_suche.close()
|
||||
|
||||
nur_laden = False
|
||||
|
||||
try:
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 1: VG aus ALKIS laden (BROR-Objekt → Memory-Layer)
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 1/6: Verfahrensgebiet aus ALKIS laden …")
|
||||
progress.set_value(0)
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
alkis_vg_layer, vg_status, _ = verfahrensgebiet_alkis(tab_widget)
|
||||
progress.raise_to_front()
|
||||
|
||||
if vg_status == LoadStatus.NONE or not alkis_vg_layer:
|
||||
return None, None, None, LoadStatus.NONE
|
||||
if vorab_layer is not None or not kombinationen:
|
||||
# KEEP oder Fehler/Abbruch bereits in Phase 1 entschieden
|
||||
if vg_status == LoadStatus.NONE or not vorab_layer:
|
||||
return None, None, None, LoadStatus.NONE
|
||||
alkis_vg_layer = vorab_layer
|
||||
elif landkreis_modus:
|
||||
# ------------------------------------------------------------------
|
||||
# Objekt(e) des Landkreises auswählen – kein eigener Fortschritts-
|
||||
# dialog währenddessen, nur der Auswahldialog selbst.
|
||||
# ------------------------------------------------------------------
|
||||
alkis_vg_layer, vg_status, nur_laden = _baue_layer_aus_landkreis_auswahl(
|
||||
tab_widget, kombinationen, crs, vg_status
|
||||
)
|
||||
if vg_status == LoadStatus.NONE or not alkis_vg_layer:
|
||||
return None, None, None, LoadStatus.NONE
|
||||
else:
|
||||
# ------------------------------------------------------------------
|
||||
# Verfahrensgebiet auswählen (bei mehreren Treffern) – kein eigener
|
||||
# Fortschrittsdialog währenddessen, nur der Auswahldialog selbst.
|
||||
# ------------------------------------------------------------------
|
||||
alkis_vg_layer, vg_status, _ = _baue_layer_aus_kombinationen(
|
||||
tab_widget, kombinationen, crs, vg_status
|
||||
)
|
||||
if vg_status == LoadStatus.NONE or not alkis_vg_layer:
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
# BROR-Layer umbenennen, um Namenskonflikt mit enriched VG-Layer zu vermeiden
|
||||
alkis_vg_layer.setName(BROR_LAYER_NAME)
|
||||
@@ -318,12 +379,25 @@ def verfahrensgebiet_alkis_komplett(
|
||||
project.addMapLayer(alkis_vg_layer)
|
||||
bror_was_added_by_us = True
|
||||
|
||||
progress.set_value(1)
|
||||
if nur_laden:
|
||||
# Nutzer hat "Nur laden" angehakt – kein Dialog 2, kein weiterer
|
||||
# Workflow (Gemarkung/Flurstücke/Vergleich).
|
||||
abgeschlossen = True
|
||||
return alkis_vg_layer, None, None, vg_status
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 2: Gemarkungsschlüssel per Geometrie-Spatial-Join ermitteln
|
||||
# Dialog 2: eigentlicher Fortschrittsdialog für die restlichen Schritte
|
||||
# (startet erst jetzt, nach erfolgreicher Auswahl/Übernahme des VG)
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 2/6: Gemarkungsschlüssel ermitteln …")
|
||||
progress = create_progress_dialog(
|
||||
6, "Verfahrensgebiet laden", "Initialisierung …"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 1: Gemarkungsschlüssel per Geometrie-Spatial-Join ermitteln
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 1/6: Gemarkungen ermitteln und laden …")
|
||||
progress.set_value(0)
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
@@ -344,12 +418,12 @@ def verfahrensgebiet_alkis_komplett(
|
||||
duration=5,
|
||||
)
|
||||
|
||||
progress.set_value(2)
|
||||
progress.set_value(1)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 3: Flurstücke laden (mit Nutzer-Dialog, Vorschlag befüllt)
|
||||
# Schritt 2: Flurstücke laden (mit Nutzer-Dialog, Vorschlag befüllt)
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 3/6: Flurstücke aus ALKIS laden …")
|
||||
progress.set_label("Schritt 2/6: Flurstücke aus ALKIS laden …")
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
@@ -363,12 +437,12 @@ def verfahrensgebiet_alkis_komplett(
|
||||
if flst_status == LoadStatus.NONE or not flst_layer:
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
progress.set_value(3)
|
||||
progress.set_value(2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 4: Räumliche Selektion
|
||||
# Schritt 3: Räumliche Selektion
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 4/6: Flurstücke räumlich selektieren …")
|
||||
progress.set_label("Schritt 3/6: Flurstücke räumlich selektieren …")
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
@@ -383,12 +457,12 @@ def verfahrensgebiet_alkis_komplett(
|
||||
)
|
||||
return None, flst_layer, None, LoadStatus.NONE
|
||||
|
||||
progress.set_value(4)
|
||||
progress.set_value(3)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 5: Enriched Memory-Layer aus Auswahl erzeugen
|
||||
# Schritt 4: Enriched Memory-Layer aus Auswahl erzeugen
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 5/6: Verfahrensgebiet-Layer erzeugen …")
|
||||
progress.set_label("Schritt 4/6: Verfahrensgebiet-Layer erzeugen …")
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
@@ -407,12 +481,12 @@ def verfahrensgebiet_alkis_komplett(
|
||||
vkz = get_variable("verfahrensnummer") or ""
|
||||
_fuege_vkz_feld_hinzu(enriched_layer, vkz)
|
||||
|
||||
progress.set_value(5)
|
||||
progress.set_value(4)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schritt 6: Flächenvergleich als Gate
|
||||
# Schritt 5: Flächenvergleich als Gate
|
||||
# ------------------------------------------------------------------
|
||||
progress.set_label("Schritt 6/6: Flächenvergleich …")
|
||||
progress.set_label("Schritt 5/6: Flächenvergleich …")
|
||||
if progress.is_canceled():
|
||||
return None, None, None, LoadStatus.NONE
|
||||
|
||||
@@ -421,11 +495,10 @@ def verfahrensgebiet_alkis_komplett(
|
||||
alkis_vg_layer, enriched_layer, toleranz
|
||||
)
|
||||
|
||||
progress.set_value(6)
|
||||
progress.set_value(5)
|
||||
|
||||
if stimmt_ueberein:
|
||||
progress.set_value(7)
|
||||
progress.close()
|
||||
progress.set_value(6)
|
||||
show_info_dialog(
|
||||
"Flächenvergleich",
|
||||
"Verfahrensgebiet mit erwarteter Geometrie erzeugt",
|
||||
@@ -437,7 +510,6 @@ def verfahrensgebiet_alkis_komplett(
|
||||
# ------------------------------------------------------------------
|
||||
# Flächenabweichung → 3-Optionen-Dialog
|
||||
# ------------------------------------------------------------------
|
||||
progress.close()
|
||||
diff = abs(bror_flaeche - vg_flaeche)
|
||||
entscheidung = ask_detailpruefung_oder_vg_laden(
|
||||
tab_widget,
|
||||
@@ -563,6 +635,9 @@ def verfahrensgebiet_alkis_komplett(
|
||||
return enriched_layer, flst_layer, alkis_vg_layer, vg_status
|
||||
|
||||
finally:
|
||||
# Dialog 2 (falls erstellt) bei jedem Ausgang (Erfolg, Abbruch, Fehler) schließen
|
||||
if progress is not None:
|
||||
progress.close()
|
||||
# WFS-Temp-Layer stets aus dem Projekt entfernen
|
||||
_entferne_hilfs_layer(project, "TempVerfahrensgebiet")
|
||||
# BROR-Layer bei Fehler/Abbruch entfernen (nur wenn in diesem Lauf hinzugefügt)
|
||||
|
||||
@@ -186,7 +186,7 @@ class TestVerfahrensgebietAlkis(unittest.TestCase):
|
||||
self.assertEqual(status, vg.LoadStatus.FIRST)
|
||||
self.assertEqual(len(feats), 1)
|
||||
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombination")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False))
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST))
|
||||
@@ -202,7 +202,7 @@ class TestVerfahrensgebietAlkis(unittest.TestCase):
|
||||
temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2])
|
||||
memory_layer = _DummyMemoryLayer()
|
||||
mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer]
|
||||
mock_dialog.return_value = ("ADV-A", "VKZ12/001")
|
||||
mock_dialog.return_value = [("ADV-A", "VKZ12/001")]
|
||||
|
||||
mock_feature = mock_feature_cls.return_value
|
||||
mock_feature.setGeometry.return_value = None
|
||||
@@ -216,7 +216,7 @@ class TestVerfahrensgebietAlkis(unittest.TestCase):
|
||||
# Nur feat1 gehört zur gewählten Kombination
|
||||
self.assertEqual(feats, [feat1])
|
||||
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombination")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST))
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer")
|
||||
@@ -229,7 +229,7 @@ class TestVerfahrensgebietAlkis(unittest.TestCase):
|
||||
feat2 = _DummyFeature(attrs={"adv_name": "ADV-B", "bezeichnung": "VKZ12/002"})
|
||||
temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2])
|
||||
mock_qgsvectorlayer.return_value = temp_layer
|
||||
mock_dialog.return_value = None # Nutzer bricht ab
|
||||
mock_dialog.return_value = [] # Nutzer bricht ab
|
||||
|
||||
layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None)
|
||||
|
||||
@@ -238,6 +238,35 @@ class TestVerfahrensgebietAlkis(unittest.TestCase):
|
||||
self.assertEqual(status, vg.LoadStatus.NONE)
|
||||
self.assertEqual(feats, [])
|
||||
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False))
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST))
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer")
|
||||
@patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox)
|
||||
def test_verfahrensgebiet_alkis_multi_kombination_mehrfachauswahl(
|
||||
self, mock_qgsvectorlayer, _mock_get_var, _mock_check, _mock_union, mock_feature_cls, mock_dialog
|
||||
):
|
||||
"""Zwei verschiedene Kombinationen → beide ausgewählt → beide Feature-Gruppen genutzt."""
|
||||
feat1 = _DummyFeature(attrs={"adv_name": "ADV-A", "bezeichnung": "VKZ12/001"})
|
||||
feat2 = _DummyFeature(attrs={"adv_name": "ADV-B", "bezeichnung": "VKZ12/002"})
|
||||
temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2])
|
||||
memory_layer = _DummyMemoryLayer()
|
||||
mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer]
|
||||
mock_dialog.return_value = [("ADV-A", "VKZ12/001"), ("ADV-B", "VKZ12/002")]
|
||||
|
||||
mock_feature = mock_feature_cls.return_value
|
||||
mock_feature.setGeometry.return_value = None
|
||||
mock_feature.setAttributes.return_value = None
|
||||
|
||||
layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None)
|
||||
|
||||
mock_dialog.assert_called_once()
|
||||
self.assertIs(layer, memory_layer)
|
||||
self.assertEqual(status, vg.LoadStatus.FIRST)
|
||||
self.assertEqual(feats, [feat1, feat2])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -129,7 +129,7 @@ def _make_progress_mock(canceled=False):
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_MOD = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett"
|
||||
_PATCH_VG_ALKIS = f"{_MOD}.verfahrensgebiet_alkis"
|
||||
_PATCH_VG_ALKIS = f"{_MOD}._suche_verfahrensgebiet_features"
|
||||
_PATCH_GEM = f"{_MOD}.gemarkung_schluessel_und_namen_von_vg_geometrie"
|
||||
_PATCH_FLST = f"{_MOD}.flurstueck_alkis"
|
||||
_PATCH_SELECT = f"{_MOD}._select_by_location"
|
||||
@@ -146,6 +146,8 @@ _PATCH_ASK_DIALOG = f"{_MOD}.ask_detailpruefung_oder_vg_laden"
|
||||
_PATCH_GP_ALKIS = f"{_MOD}.lade_punktort_ta"
|
||||
_PATCH_KNICK = f"{_MOD}.knickpunkt_pruefung"
|
||||
_PATCH_EXTRAHIERE = f"{_MOD}._extrahiere_knickpunkte"
|
||||
_PATCH_LANDKREIS_SUCHE = f"{_MOD}._suche_landkreis_kombinationen"
|
||||
_PATCH_LANDKREIS_BAU = f"{_MOD}._baue_layer_aus_landkreis_auswahl"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -265,9 +267,10 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_PROGRESS)
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
def test_vg_alkis_none_aborts(self, mock_vg, mock_proj, mock_prog):
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_vg_alkis_none_aborts(self, mock_get_var, mock_vg, mock_proj, mock_prog):
|
||||
mock_proj.instance.return_value = _DummyProject()
|
||||
mock_vg.return_value = (None, LoadStatus.NONE, [])
|
||||
mock_vg.return_value = (None, LoadStatus.NONE, {}, None)
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None)
|
||||
@@ -281,10 +284,11 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_GEM)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
def test_flst_none_aborts(self, mock_vg, mock_gem, mock_proj, mock_prog):
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_flst_none_aborts(self, mock_get_var, mock_vg, mock_gem, mock_proj, mock_prog):
|
||||
mock_proj.instance.return_value = _DummyProject()
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = [("1234", "Muster")]
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
@@ -302,13 +306,14 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_GEM)
|
||||
@patch(_PATCH_FLST)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_no_selection_returns_none_enriched(
|
||||
self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_msgbox, mock_prog
|
||||
self, mock_get_var, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_msgbox, mock_prog
|
||||
):
|
||||
mock_proj.instance.return_value = _DummyProject()
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 0
|
||||
@@ -330,15 +335,16 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_GEM)
|
||||
@patch(_PATCH_FLST)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_invalid_enriched_layer_returns_none(
|
||||
self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_msgbox, mock_prog
|
||||
self, mock_get_var, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_msgbox, mock_prog
|
||||
):
|
||||
mock_proj.instance.return_value = _DummyProject()
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
bad_enriched = _make_layer(valid=False)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 5
|
||||
@@ -356,11 +362,12 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_PROGRESS)
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
def test_progress_cancel_after_step1_returns_none(self, mock_vg, mock_proj, mock_prog):
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_progress_cancel_after_step1_returns_none(self, mock_get_var, mock_vg, mock_proj, mock_prog):
|
||||
"""Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus."""
|
||||
mock_proj.instance.return_value = _DummyProject()
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
|
||||
prog = MagicMock()
|
||||
prog.is_canceled.side_effect = [False, True] # 1. Aufruf False, 2. True
|
||||
@@ -398,7 +405,7 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
flst_layer = _make_layer(name="Flurstücke", lid="FLST1")
|
||||
enriched = _make_layer(name="Verfahrensgebiet", lid="VG_ENRICHED", valid=True)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = [("1234", "Muster")]
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 3
|
||||
@@ -437,18 +444,18 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
enriched = _make_layer(name="Verfahrensgebiet", valid=True)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 2
|
||||
mock_erstelle.return_value = (enriched, 2, 0)
|
||||
mock_get_var.return_value = ""
|
||||
mock_get_var.return_value = "VF-2024-001"
|
||||
mock_vergl.return_value = (1000.0, 1000.0, True)
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
verfahrensgebiet_alkis_komplett(None)
|
||||
|
||||
mock_vkz.assert_called_once_with(enriched, "")
|
||||
mock_vkz.assert_called_once_with(enriched, "VF-2024-001")
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Flächenvergleich – Abweichung + Abbrechen
|
||||
@@ -476,12 +483,12 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
enriched = _make_layer(name="Verfahrensgebiet", valid=True)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 3
|
||||
mock_erstelle.return_value = (enriched, 3, 0)
|
||||
mock_get_var.return_value = ""
|
||||
mock_get_var.return_value = "VF-2024-001"
|
||||
mock_vergl.return_value = (1000.0, 800.0, False)
|
||||
mock_ask.return_value = "abbrechen"
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
@@ -521,12 +528,12 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
enriched = _make_layer(name="Verfahrensgebiet", valid=True)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 3
|
||||
mock_erstelle.return_value = (enriched, 3, 0)
|
||||
mock_get_var.return_value = ""
|
||||
mock_get_var.return_value = "VF-2024-001"
|
||||
mock_vergl.return_value = (1000.0, 800.0, False)
|
||||
mock_ask.return_value = "als_vg_laden"
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
@@ -577,12 +584,12 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
punktort_layer = _make_layer(name="PunktortTA", lid="PTA_L")
|
||||
ergebnis_layer = _make_layer(name="Knickpunktprüfung", lid="KNICK_L")
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 3
|
||||
mock_erstelle.return_value = (enriched, 3, 0)
|
||||
mock_get_var.return_value = ""
|
||||
mock_get_var.return_value = "VF-2024-001"
|
||||
mock_vergl.return_value = (1000.0, 800.0, False)
|
||||
mock_ask.return_value = "detailpruefung"
|
||||
mock_extrahiere.return_value = [MagicMock(), MagicMock(), MagicMock()] # 3 Knickpunkte
|
||||
@@ -630,12 +637,12 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
flst_layer = _make_layer(name="Flurstücke")
|
||||
enriched = _make_layer(name="Verfahrensgebiet", valid=True)
|
||||
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_flst.return_value = (flst_layer, LoadStatus.FIRST)
|
||||
mock_select.return_value = 3
|
||||
mock_erstelle.return_value = (enriched, 3, 0)
|
||||
mock_get_var.return_value = ""
|
||||
mock_get_var.return_value = "VF-2024-001"
|
||||
mock_vergl.return_value = (1000.0, 1000.0, True)
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
@@ -647,15 +654,16 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_GEM)
|
||||
@patch(_PATCH_VG_ALKIS)
|
||||
@patch(_PATCH_GET_VAR, return_value="VF-2024-001")
|
||||
def test_bror_layer_wird_bei_fehler_entfernt(
|
||||
self, mock_vg, mock_gem, mock_proj, mock_prog
|
||||
self, mock_get_var, mock_vg, mock_gem, mock_proj, mock_prog
|
||||
):
|
||||
"""Bei Fehler (flst_alkis NONE) wird der BROR-Layer aus dem Projekt entfernt."""
|
||||
proj_inst = _DummyProject()
|
||||
mock_proj.instance.return_value = proj_inst
|
||||
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)", lid="BROR_ERR")
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, [])
|
||||
mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None)
|
||||
mock_gem.return_value = []
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
@@ -664,6 +672,78 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
|
||||
|
||||
self.assertIn("BROR_ERR", proj_inst.removed)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Landkreis-Fallback (keine Verfahrensnummer)
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@patch(_PATCH_PROGRESS)
|
||||
@patch(_PATCH_MSGBOX)
|
||||
@patch(_PATCH_GET_VAR, return_value="")
|
||||
def test_kein_vkz_kein_landkreis_zeigt_fehler(self, mock_get_var, mock_msgbox, mock_prog):
|
||||
"""Weder Verfahrensnummer noch Landkreis gesetzt → Fehlermeldung, kein Layer."""
|
||||
enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None)
|
||||
|
||||
self.assertIsNone(enriched)
|
||||
self.assertIsNone(flst)
|
||||
self.assertIsNone(bror)
|
||||
self.assertEqual(status, LoadStatus.NONE)
|
||||
mock_msgbox.critical.assert_called_once()
|
||||
|
||||
@patch(_PATCH_PROGRESS)
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_LANDKREIS_BAU)
|
||||
@patch(_PATCH_LANDKREIS_SUCHE)
|
||||
def test_landkreis_modus_wird_bei_fehlender_vkz_verwendet(
|
||||
self, mock_such, mock_bau, mock_proj, mock_prog
|
||||
):
|
||||
"""Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfad
|
||||
statt VKZ-Pfad wird verwendet."""
|
||||
|
||||
def _get_var_side_effect(key, scope="project"):
|
||||
return {"verfahrensnummer": "", "landkreis_kennzahl": "22"}.get(key, "")
|
||||
|
||||
proj_inst = _DummyProject()
|
||||
mock_proj.instance.return_value = proj_inst
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833")
|
||||
mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True) # nur_laden=True
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
with patch(_PATCH_GET_VAR, side_effect=_get_var_side_effect):
|
||||
enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None)
|
||||
|
||||
mock_such.assert_called_once()
|
||||
mock_bau.assert_called_once()
|
||||
self.assertIs(enriched, vg_layer)
|
||||
self.assertIsNone(flst)
|
||||
self.assertIsNone(bror)
|
||||
self.assertEqual(status, LoadStatus.FIRST)
|
||||
|
||||
@patch(_PATCH_PROGRESS)
|
||||
@patch(_PATCH_PROJECT)
|
||||
@patch(_PATCH_LANDKREIS_BAU)
|
||||
@patch(_PATCH_LANDKREIS_SUCHE)
|
||||
def test_landkreis_modus_nur_laden_ueberspringt_dialog2(
|
||||
self, mock_such, mock_bau, mock_proj, mock_prog
|
||||
):
|
||||
"""nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkung
|
||||
etc.) wird nicht gestartet."""
|
||||
|
||||
def _get_var_side_effect(key, scope="project"):
|
||||
return {"verfahrensnummer": "", "landkreis_kennzahl": "22"}.get(key, "")
|
||||
|
||||
proj_inst = _DummyProject()
|
||||
mock_proj.instance.return_value = proj_inst
|
||||
vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
|
||||
mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833")
|
||||
mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True)
|
||||
mock_prog.return_value = _make_progress_mock()
|
||||
|
||||
with patch(_PATCH_GET_VAR, side_effect=_get_var_side_effect), \
|
||||
patch(_PATCH_GEM) as mock_gem:
|
||||
verfahrensgebiet_alkis_komplett(None)
|
||||
mock_gem.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user