Files
Plugin_SN_Verfahrensgebiet/functions/verfahrensgebiet_alkis_komplett.py
T
Michael Otto 148d4dfa15 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).
2026-08-27 15:14:07 +02:00

650 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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,
)
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)"
#: 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).
- **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
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:
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:
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)
if vg_status in (LoadStatus.FIRST, LoadStatus.RELOAD):
project.addMapLayer(alkis_vg_layer)
bror_was_added_by_us = True
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
# ------------------------------------------------------------------
# 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