"""sn_verfahrensgebiet/functions/verfahrensgebiet_alkis_komplett.py – Vollständiger VG-Lade-Workflow. Orchestriert den vollständigen Arbeitsablauf zum Laden eines Verfahrensgebiets aus ALKIS mit anschließender Flurstücks-Verschneidung, Flächenvergleich und optionaler Umringsprüfung: 1. Verfahrensgebiet aus ``AX_BauRaumOderBodenordnungsrecht`` per VKZ laden. 2. Gemarkungsschlüssel per Geometrie aus ``AX_Gemarkung`` vorschlagen. 3. Flurstücke per Dialog (mit Vorschlag) laden. 4. Flurstücke räumlich auf das VG einschränken (``selectByLocation``). 5. Aus der Auswahl den enriched Verfahrensgebiet-Memory-Layer erzeugen. 6. Flächenvergleich zwischen BROR-Objekt und abgeleitetem Verfahrensgebiet. - **Übereinstimmung** (≤ Toleranz): modale OK-Meldung, Ablauf beendet. - **Abweichung**: 3-Optionen-Dialog (Detailprüfung / Als VG laden / Abbrechen). 7. Bei Wahl „Als VG laden": - BROR-Objekt direkt mit VG-Namen ins Projekt übernehmen. 8. Bei Wahl „Detailprüfung" (knickpunktbasiert): - Grenzpunkte (AX_PunktortTA) per BBox laden. - Knickpunkte des BROR mit PunktortTA vergleichen (grün/rot/orange). - Korrigiertes Polygon aus identischen + gefundenen PunktortTA ableiten. - Flurstücke mit korrigiertem Polygon neu selektieren und enriched Layer erzeugen. - Ergebnislayer (Punkte), Verbindungslinien und Polygon ins Projekt einfügen. - Abschlussmeldung mit Zählung. Das BROR-Objekt (``AX_BauRaumOderBodenordnungsrecht``) bleibt bei Erfolg als temporärer Layer ``BauRaumOderBodenordnungsrecht (ALKIS)`` im Projekt erhalten. **Rückgabe:** ``Tuple[enriched_layer, flst_layer, bror_layer, LoadStatus]`` **Abbruchverhalten:** Jeder Schritt, der :attr:`LoadStatus.NONE` zurückgibt oder kein Ergebnis liefert, bricht den Workflow ab. Der BROR-Layer wird nur bei Fehler/Abbruch entfernt (wenn er in diesem Lauf hinzugefügt wurde). """ from __future__ import annotations from typing import Optional, Tuple from sn_basis.functions import QgsProject from sn_basis.functions import QMessageBox from sn_basis.functions.message_wrapper import warning from sn_basis.functions.variable_wrapper import get_variable from sn_basis.functions.dialog_wrapper import ( create_progress_dialog, show_info_dialog, ask_detailpruefung_oder_vg_laden, ask_yes_no, ) from sn_basis.functions.landkreise import LANDKREISE, ALLE_LANDKREISE from sn_basis.functions.ly_style_wrapper import apply_style from sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge import ask_landkreis_auswahl from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import ( _suche_verfahrensgebiet_features, _baue_layer_aus_kombinationen, _suche_landkreis_kombinationen, _baue_layer_aus_landkreis_auswahl, LoadStatus, ) from sn_verfahrensgebiet.functions.gemarkung_alkis import ( gemarkung_schluessel_und_namen_von_vg_geometrie, ) from sn_verfahrensgebiet.functions.flurstueck_alkis import flurstueck_alkis from sn_verfahrensgebiet.functions.verfahrensgebiet_edit_service import ( erstelle_verfahrensgebiet_aus_auswahl, VERF_LAYER_NAME, ) from sn_verfahrensgebiet.functions.grenzpunkt_alkis import lade_punktort_ta from sn_verfahrensgebiet.functions.knickpunkt_pruefung import ( knickpunkt_pruefung, _extrahiere_knickpunkte, erstelle_korrigiertes_polygon, ) # Feldname für das Verfahrenskennzeichen im enriched Layer FELD_VKZ = "verfahrensnummer" #: Anzeigename des BROR-Layers im Projekt BROR_LAYER_NAME = "BauRaumOderBodenordnungsrecht (ALKIS)" #: QML-Stildatei für den BROR-Layer (sn_verfahrensgebiet/styles/) BROR_STYLE_QML = "BauRaumOderBodenordnungsrecht.qml" #: Projektvariable für die Flächentoleranz (in m²); Default 0 FLAECHEN_TOLERANZ_VARIABLE = "flaechen_toleranz_m2" #: Minimale implizite Flächentoleranz – Abweichungen unter diesem Wert #: lösen keine Konfliktabfrage aus (Gleitkomma-Ungenauigkeiten vermeiden). MINDEST_FLAECHEN_TOLERANZ_M2 = 0.01 # --------------------------------------------------------------------------- # Interne Hilfsfunktionen # --------------------------------------------------------------------------- def _entferne_hilfs_layer(project, layer_name: str) -> None: """Entfernt alle Layer mit dem angegebenen Namen aus dem Projekt. Wird verwendet, um temporäre WFS-Hilfs-Layer nach der Verschneidung aus dem Projekt zu bereinigen. Parameters ---------- project : Aktuelles QGIS-Projekt. layer_name : Anzeigename des zu entfernenden Layers. """ for lyr in project.mapLayersByName(layer_name): project.removeMapLayer(lyr.id()) def _select_by_location(flst_layer, vg_layer) -> int: """Selektiert Flurstücke, die innerhalb des Verfahrensgebiets liegen. Verwendet ``processing.run("native:selectbylocation")`` mit Prädikat 6 (within). Flurstücke, die das VG nur berühren, werden ausgeschlossen. Parameters ---------- flst_layer : Flurstücks-Layer (Quelle der Selektion). vg_layer : Verfahrensgebiet-Layer (Schnittmaske). Returns ------- int Anzahl der selektierten Features; 0 bei Fehler oder leerem Ergebnis. """ try: import processing # type: ignore[import] processing.run( "native:selectbylocation", { "INPUT": flst_layer, "PREDICATE": [6], # 6 = within; schließt reines Berühren aus "INTERSECT": vg_layer, "METHOD": 0, # 0 = neue Selektion }, ) return flst_layer.selectedFeatureCount() except Exception: return 0 def _fuege_vkz_feld_hinzu(layer, vkz: str) -> None: """Fügt das Feld ``verfahrensnummer`` zum enriched Layer hinzu und befüllt es mit dem Verfahrenskennzeichen, sofern dieses nicht leer ist. Parameters ---------- layer : Enriched Verfahrensgebiet-Memory-Layer (in-place geändert). vkz : Verfahrenskennzeichen aus der Projektvariable ``verfahrensnummer``. """ if not vkz: return try: from sn_basis.functions import QgsField from sn_basis.functions.qt_wrapper import QVariant pr = layer.dataProvider() pr.addAttributes([QgsField(FELD_VKZ, QVariant.String)]) layer.updateFields() idx = layer.fields().indexFromName(FELD_VKZ) if idx < 0: return changes = {} for feat in layer.getFeatures(): changes[feat.id()] = {idx: vkz} pr.changeAttributeValues(changes) layer.updateFields() except Exception: pass def _lese_flaechen_toleranz() -> float: """Liest die Flächentoleranz aus der Projektvariable (Default: 0.0 m²). Die Projektvariable :data:`FLAECHEN_TOLERANZ_VARIABLE` kann in den Projekteigenschaften gesetzt werden. Fehlt sie oder ist sie ungültig, wird 0.0 zurückgegeben (exakter Vergleich). Returns ------- float Toleranz in m² (bei EPSG:25833 identisch mit den Flächen-Einheiten). """ try: raw = get_variable(FLAECHEN_TOLERANZ_VARIABLE) if raw is not None and str(raw).strip(): return float(str(raw).strip()) except Exception: pass return 0.0 def _flaeche_des_layers(layer) -> float: """Summiert die Flächen aller Features eines Layers (in CRS-Einheiten²). Parameters ---------- layer : Beliebiger Polygon-Layer. Returns ------- float Summe aller Feature-Flächen; 0.0 wenn keine Features vorhanden. """ total = 0.0 for feat in layer.getFeatures(): try: geom = feat.geometry() if geom and not geom.isEmpty(): total += geom.area() except Exception: pass return total def _vergleiche_flaechen( bror_layer, vg_layer, toleranz_m2: float = 0.0 ) -> Tuple[float, float, bool]: """Vergleicht Flächen von BROR- und Verfahrensgebiet-Layer. Parameters ---------- bror_layer : BauRaumOderBodenordnungsrecht-Layer. vg_layer : Enriched Verfahrensgebiet-Layer. toleranz_m2 : Maximale zulässige Flächendifferenz in m². Returns ------- Tuple[float, float, bool] ``(bror_flaeche, vg_flaeche, stimmt_ueberein)`` """ bror_flaeche = _flaeche_des_layers(bror_layer) vg_flaeche = _flaeche_des_layers(vg_layer) diff = abs(bror_flaeche - vg_flaeche) stimmt_ueberein = diff < MINDEST_FLAECHEN_TOLERANZ_M2 or diff <= toleranz_m2 return bror_flaeche, vg_flaeche, stimmt_ueberein # --------------------------------------------------------------------------- # Öffentliche API # --------------------------------------------------------------------------- def verfahrensgebiet_alkis_komplett( tab_widget, ) -> Tuple[Optional[object], Optional[object], Optional[object], LoadStatus]: """Vollständiger VG-Lade-Workflow: ALKIS-VG → Gemarkungen → Flurstücke → Verschneidung → Flächenvergleich → opt. Umringsprüfung. Zwei Fortschrittsdialoge nacheinander: - **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). - **STOP** (Zwischenabfrage "Weiter?"): erscheint nach dem Laden/Übernehmen des BROR-Objekts, bevor Dialog 2 startet. Bei "Nein" bricht der Ablauf ab, das BROR-Objekt bleibt aber im Projekt (ersetzt die frühere "Nur Objekte laden"-Option des Landkreis-Auswahldialogs). - **Dialog 2** (eigentlicher Workflow, 6 Schritte): startet erst, nachdem das Verfahrensgebiet feststeht und die Stop-Abfrage mit "Ja" bestätigt wurde. 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 Lauf hinzugefügt wurde. Parameters ---------- tab_widget : Eltern-Widget für alle Dialoge. Returns ------- Tuple[Optional[enriched_layer], Optional[flst_layer], Optional[bror_layer], LoadStatus] - ``enriched_layer``: Memory-Layer „Verfahrensgebiet". - ``flst_layer``: Flurstücke-Layer (bleibt im Projekt). - ``bror_layer``: BROR-Layer ``BauRaumOderBodenordnungsrecht (ALKIS)`` (bleibt bei Erfolg im Projekt). - :class:`LoadStatus`: ``FIRST``, ``RELOAD`` oder ``NONE``. """ project = QgsProject.instance() alkis_vg_layer = None bror_was_added_by_us = False abgeschlossen = False progress = None verfahrensnummer = get_variable("verfahrensnummer") landkreis_kennzahl = get_variable("landkreis_kennzahl") if not verfahrensnummer and not landkreis_kennzahl: # Keine Projekteigenschaften vorhanden – Landkreis per Dialog abfragen, # statt direkt abzubrechen. landkreis_kennzahl = ask_landkreis_auswahl(tab_widget, LANDKREISE) if not landkreis_kennzahl: # Nutzer hat den Landkreis-Dialog abgebrochen – stiller Abbruch, # keine Fehlermeldung (bewusster Abbruch, kein Fehlerzustand). return None, None, None, LoadStatus.NONE landkreis_modus = not verfahrensnummer # ------------------------------------------------------------------ # Dialog 1: Verfahrensgebiet(e) in ALKIS ermitteln/suchen # ------------------------------------------------------------------ if landkreis_modus: if landkreis_kennzahl == ALLE_LANDKREISE: such_label = "Objekte in allen Landkreisen ermitteln/suchen …" else: 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() try: 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 = _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) if vg_status in (LoadStatus.FIRST, LoadStatus.RELOAD): project.addMapLayer(alkis_vg_layer) bror_was_added_by_us = True # Style direkt nach dem Laden anwenden – unabhängig vom weiteren # Ablauf, damit auch bei "Nein" auf die Stop-Abfrage der Style # gesetzt ist. apply_style(alkis_vg_layer, BROR_STYLE_QML) # ------------------------------------------------------------------ # STOP: Zwischenabfrage nach dem Laden des BROR-Objekts, bevor mit # Gemarkung/Flurstücke/Flächenvergleich fortgefahren wird (ersetzt # die frühere "Nur Objekte laden"-Option). # ------------------------------------------------------------------ if not ask_yes_no( "Verfahrensgebiet laden", f"Layer '{alkis_vg_layer.name()}' wurde geladen. Weiter?", default=True, parent=tab_widget, ): abgeschlossen = True return alkis_vg_layer, None, None, vg_status # ------------------------------------------------------------------ # Dialog 2: eigentlicher Fortschrittsdialog für die restlichen Schritte # (startet erst jetzt, nach erfolgreicher Auswahl/Übernahme des VG) # ------------------------------------------------------------------ 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 bror_feats = list(alkis_vg_layer.getFeatures()) vg_geom = bror_feats[0].geometry() if bror_feats else None schluessel_namen = ( gemarkung_schluessel_und_namen_von_vg_geometrie(vg_geom) if vg_geom else [] ) vorgabe_schluessel = [nr for nr, _ in schluessel_namen] vorgabe_namen = [nm for _, nm in schluessel_namen if nm] if not vorgabe_schluessel: warning( "VG laden", "Keine Gemarkungen in der BBox gefunden. Bitte Schlüssel manuell eingeben.", duration=5, ) progress.set_value(1) # ------------------------------------------------------------------ # Schritt 2: Flurstücke laden (mit Nutzer-Dialog, Vorschlag befüllt) # ------------------------------------------------------------------ progress.set_label("Schritt 2/6: Flurstücke aus ALKIS laden …") if progress.is_canceled(): return None, None, None, LoadStatus.NONE flst_layer, flst_status = flurstueck_alkis( tab_widget, vorgabe_schluessel=vorgabe_schluessel, vorgabe_namen=vorgabe_namen if vorgabe_namen else None, ohne_dialog=True, ) if flst_status == LoadStatus.NONE or not flst_layer: return None, None, None, LoadStatus.NONE progress.set_value(2) # ------------------------------------------------------------------ # Schritt 3: Räumliche Selektion # ------------------------------------------------------------------ progress.set_label("Schritt 3/6: Flurstücke räumlich selektieren …") if progress.is_canceled(): return None, None, None, LoadStatus.NONE anzahl = _select_by_location(flst_layer, alkis_vg_layer) if anzahl == 0: QMessageBox.warning( tab_widget, "Keine Flurstücke im VG", "Es wurden keine Flurstücke gefunden, die das Verfahrensgebiet schneiden.\n" "Bitte Gemarkungsschlüssel und Ausdehnung prüfen.", ) return None, flst_layer, None, LoadStatus.NONE progress.set_value(3) # ------------------------------------------------------------------ # Schritt 4: Enriched Memory-Layer aus Auswahl erzeugen # ------------------------------------------------------------------ progress.set_label("Schritt 4/6: Verfahrensgebiet-Layer erzeugen …") if progress.is_canceled(): return None, None, None, LoadStatus.NONE enriched_layer, erfolgreich, fehlgeschlagen = erstelle_verfahrensgebiet_aus_auswahl( flst_layer ) if not enriched_layer or not enriched_layer.isValid(): QMessageBox.critical( tab_widget, "Fehler", "Verfahrensgebiet-Layer konnte nicht erstellt werden.", ) return None, flst_layer, None, LoadStatus.NONE vkz = get_variable("verfahrensnummer") or "" _fuege_vkz_feld_hinzu(enriched_layer, vkz) progress.set_value(4) # ------------------------------------------------------------------ # Schritt 5: Flächenvergleich als Gate # ------------------------------------------------------------------ progress.set_label("Schritt 5/6: Flächenvergleich …") if progress.is_canceled(): return None, None, None, LoadStatus.NONE toleranz = _lese_flaechen_toleranz() bror_flaeche, vg_flaeche, stimmt_ueberein = _vergleiche_flaechen( alkis_vg_layer, enriched_layer, toleranz ) progress.set_value(5) if stimmt_ueberein: progress.set_value(6) show_info_dialog( "Flächenvergleich", "Verfahrensgebiet mit erwarteter Geometrie erzeugt", parent=tab_widget, ) abgeschlossen = True return enriched_layer, flst_layer, alkis_vg_layer, vg_status # ------------------------------------------------------------------ # Flächenabweichung → 3-Optionen-Dialog # ------------------------------------------------------------------ diff = abs(bror_flaeche - vg_flaeche) entscheidung = ask_detailpruefung_oder_vg_laden( tab_widget, "Abweichung erkannt", f"Abweichung zwischen BauRaumOderBodenordnungsrecht-Objekt und abgeleitetem " f"Verfahrensgebiet erkannt.\n\n" f"As: {bror_flaeche:.2f} m²\n" f"Verfahrensgebiet: {vg_flaeche:.2f} m²\n" f"Differenz: {diff:.2f} m²\n\n" f"Wie möchten Sie fortfahren?", ) if entscheidung == "abbrechen": abgeschlossen = True return enriched_layer, flst_layer, alkis_vg_layer, vg_status # ------------------------------------------------------------------ # Option: Bodenordnungsobjekt direkt als Verfahrensgebiet laden # ------------------------------------------------------------------ if entscheidung == "als_vg_laden": # Layer bleibt im Projekt – nur umbenennen. working_tab überspringt # addMapLayer wenn der Layer bereits registriert ist. alkis_vg_layer.setName(VERF_LAYER_NAME) bror_was_added_by_us = False abgeschlossen = True return alkis_vg_layer, None, None, vg_status # ------------------------------------------------------------------ # Option: Detailprüfung (knickpunktbasiert) # ------------------------------------------------------------------ # Knickpunkte vorab extrahieren, um Schrittanzahl zu bestimmen knickpunkte = _extrahiere_knickpunkte(alkis_vg_layer) n_knick = len(knickpunkte) # Gesamtschritte: 0=Start, 1=PTA-Index, 2…n+1=Knickpunkte, n+2=Orange, n+3=Fertig # + 1 Schritt vorab für Grenzpunkte laden gesamt_schritte = n_knick + 5 detail_progress = create_progress_dialog( gesamt_schritte, "Detailprüfung (Knickpunkte)", "Schritt 1: Grenzpunkte laden …", ) detail_progress.set_value(0) # Schritt 1: PunktortTA gemarkungsweise laden (Hits + 5000er-Batches) punktort_layer = lade_punktort_ta( vorgabe_schluessel, vg_geom, progress=detail_progress ) if detail_progress.is_canceled(): detail_progress.close() abgeschlossen = True return enriched_layer, flst_layer, alkis_vg_layer, vg_status detail_progress.set_value(1) if punktort_layer is None: detail_progress.close() abgeschlossen = True if not detail_progress.is_canceled(): QMessageBox.critical( tab_widget, "WFS-Fehler", "Die Grenzpunktdaten (PunktortTA) konnten nicht geladen werden.\n\n" "Mögliche Ursache: Netzwerk- oder Proxy-Fehler.\n" "Bitte prüfen Sie die QGIS-Proxy-Einstellungen " "(Einstellungen -> Optionen -> Netzwerk) und den Log-Eintrag " "unter Ansicht -> Bedienfelder -> Protokollnachrichten " "(Kategorie 'sn_verfahrensgebiet').", ) return enriched_layer, flst_layer, alkis_vg_layer, vg_status # Schritte 2…Ende: Knickpunktprüfung ergebnis_layer, identisch, kein_pta, fehlender_knick = knickpunkt_pruefung( alkis_vg_layer, punktort_layer, flst_layer, progress=detail_progress, knickpunkte=knickpunkte, ) if detail_progress.is_canceled(): detail_progress.close() abgeschlossen = True return enriched_layer, flst_layer, alkis_vg_layer, vg_status detail_progress.set_value(gesamt_schritte) detail_progress.close() # ------------------------------------------------------------------ # Korrigiertes Polygon aus identischen + fehlenden Knickpunkten # ------------------------------------------------------------------ if ergebnis_layer and ergebnis_layer.isValid(): linien_layer_korr, polygon_layer_korr = erstelle_korrigiertes_polygon( alkis_vg_layer, ergebnis_layer, alkis_vg_layer.crs().authid() ) if polygon_layer_korr is not None and polygon_layer_korr.isValid(): anzahl_korr = _select_by_location(flst_layer, polygon_layer_korr) if anzahl_korr > 0: enriched_layer_neu, _, _ = erstelle_verfahrensgebiet_aus_auswahl( flst_layer ) if enriched_layer_neu and enriched_layer_neu.isValid(): _fuege_vkz_feld_hinzu(enriched_layer_neu, vkz) enriched_layer = enriched_layer_neu if linien_layer_korr is not None and linien_layer_korr.isValid(): project.addMapLayer(linien_layer_korr) project.addMapLayer(polygon_layer_korr) if ergebnis_layer and ergebnis_layer.isValid(): project.addMapLayer(ergebnis_layer) show_info_dialog( "Detailprüfung abgeschlossen", f"Knickpunkte geprüft: {n_knick}\n" f" Identisch (grün): {identisch}\n" f" Kein PunktortTA (rot): {kein_pta}\n" f" Fehlende Knickpunkte (orange): {fehlender_knick}", parent=tab_widget, ) abgeschlossen = True 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) if not abgeschlossen and bror_was_added_by_us and alkis_vg_layer is not None: try: project.removeMapLayer(alkis_vg_layer.id()) except Exception: pass