- Ist weder Verfahrensnummer noch Landkreis in den Projekteigenschaften gesetzt, wird jetzt per Dialog ein Landkreis abgefragt (inkl. "Alle Landkreise") statt direkt abzubrechen. - Verfahrensgebiet-spezifische ALKIS-Dialoge aus sn_basis hierher verschoben: neue Datei functions/verfahrensgebiet_dialoge.py (ask_bror_kombinationen, ask_landkreis_objekte, ask_landkreis_auswahl). - Bugfix: BROR-Layer bekommt seinen Style (BauRaumOderBodenordnungsrecht.qml) jetzt direkt beim Laden in verfahrensgebiet_alkis_komplett(), unabhängig vom weiteren Ablauf. working_tab.py überspringt bei "Nur Objekte laden" jetzt den gesamten VG-Nachbehandlungsblock (Style/Extent/Haken/Combobox/ Erfolgsmeldung), da dort kein echter VG-Layer entsteht. Detailprüfung/ Verifikation im echten QGIS (nicht nur per Unittest) steht noch aus. - Bugfix: Erfolgsmeldung "Layer wurde ... geladen (inkl. Flurstücksverschneidung)" erscheint nicht mehr bei "Nur Objekte laden". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
665 lines
26 KiB
Python
665 lines
26 KiB
Python
"""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_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).
|
||
- **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:
|
||
# Keine Projekteigenschaften vorhanden – Landkreis per Dialog abfragen,
|
||
# statt direkt abzubrechen.
|
||
landkreis_kennzahl = ask_landkreis_auswahl(tab_widget, LANDKREISE)
|
||
if 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:
|
||
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()
|
||
|
||
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
|
||
# Style direkt nach dem Laden anwenden – unabhängig vom weiteren
|
||
# Ablauf, damit auch "Nur Objekte laden" den Style erhält.
|
||
apply_style(alkis_vg_layer, BROR_STYLE_QML)
|
||
|
||
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
|
||
|