Files
Plugin_SN_Verfahrensgebiet/functions/verfahrensgebiet_alkis.py
T

183 lines
6.8 KiB
Python
Raw Normal View History

2026-04-20 14:33:13 +02:00
"""sn_verfahrensgebiet/functions/verfahrensgebiet_alkis.py – ALKIS-Integration.
Lädt Verfahrensgebiete aus dem ALKIS-WFS-Dienst.
"""
2025-11-18 16:10:58 +01:00
from enum import Enum
from sn_basis.functions import (
2025-11-18 16:10:58 +01:00
QgsVectorLayer, QgsProject, QgsFeature, QgsField, QgsGeometry
)
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
2026-04-23 13:07:35 +02:00
alkis_sf_url = "https://geodienste.sachsen.de/aaa/public_alkis/sf/wfs"
typename = "adv:AX_BauRaumOderBodenordnungsrecht"
#: Attribute, die aus dem BROR-WFS-Dienst geladen und im Memory-Layer gespeichert werden.
BROR_ATTRIBUTE_FELDER = [
"artderfestlegung",
"ausfuehrendestelle_ax_dienststelle_schluessel_stelle",
"adv_name",
"bezeichnung",
"datumanordnung",
"datumbesitzeinweisung",
"datumrechtskraft",
"datumabgabe",
]
2025-11-18 16:10:58 +01:00
class LoadStatus(Enum):
2026-04-20 14:33:13 +02:00
"""Status eines Layer-Ladevorgangs.
Attributes:
NONE: Nichts geladen
FIRST: Erstmalig geladen
RELOAD: Neu geladen
KEEP: Vorhandener Layer behalten
"""
2025-11-18 16:10:58 +01:00
NONE = "none" # nichts geladen
FIRST = "first" # erstmalig geladen
RELOAD = "reload" # neu geladen
KEEP = "keep" # vorhandener Layer behalten
2026-06-23 13:54:08 +02:00
APPEND = "append" # Features an vorhandenen Layer angehängt
2025-11-18 16:10:58 +01:00
def _check_existing_layer(tab_widget):
2026-04-20 14:33:13 +02:00
"""Prüft, ob ein Verfahrensgebiet-Layer bereits existiert.
Args:
tab_widget: Parent-Widget für Dialoge
Returns:
Tuple[Optional[QgsVectorLayer], LoadStatus]: Layer und Status
"""
2025-11-18 16:10:58 +01:00
project = QgsProject.instance()
existing_layers = project.mapLayersByName("Verfahrensgebiet")
if not existing_layers:
return None, LoadStatus.FIRST
reply = QMessageBox.question(
tab_widget,
"Layer bereits vorhanden",
"Der Layer 'Verfahrensgebiet' existiert bereits.\n"
"Möchten Sie ihn löschen und neu laden?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
for lyr in existing_layers:
project.removeMapLayer(lyr.id())
return None, LoadStatus.RELOAD
else:
return existing_layers[0], LoadStatus.KEEP
2026-07-15 15:29:48 +02:00
def verfahrensgebiet_alkis(tab_widget, skip_existing_check: bool = False):
2026-04-20 14:33:13 +02:00
"""Lädt Verfahrensgebiet aus ALKIS-WFS basierend auf Verfahrensnummer.
2026-04-22 14:53:45 +02:00
2026-04-20 14:33:13 +02:00
Args:
tab_widget: Parent-Widget für Dialoge und Meldungen
2026-04-22 14:53:45 +02:00
2026-04-20 14:33:13 +02:00
Returns:
2026-04-22 14:53:45 +02:00
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.
2026-04-20 14:33:13 +02:00
"""
verfahrensnummer = get_variable("verfahrensnummer")
if not verfahrensnummer:
QMessageBox.critical(tab_widget, "Fehler",
"In den Projekteigenschaften ist noch keine Verfahrensnummer eingetragen!")
2026-04-22 14:53:45 +02:00
return None, LoadStatus.NONE, []
2026-07-15 15:29:48 +02:00
# 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, []
# 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.
vkz_prefix = verfahrensnummer[:5]
wfs_uri_find = (
2026-04-23 13:07:35 +02:00
f"url='{alkis_sf_url}' typename='{typename}' srsname='EPSG:25833' "
f"sql=SELECT * FROM AX_BauRaumOderBodenordnungsrecht "
f"WHERE bezeichnung LIKE '{vkz_prefix}%'"
)
temp_layer = QgsVectorLayer(wfs_uri_find, "TempVerfahrensgebiet", "WFS")
if not temp_layer.isValid():
QMessageBox.critical(tab_widget, "Fehler", "Layer konnte nicht geladen werden.")
2026-04-22 14:53:45 +02:00
return None, LoadStatus.NONE, []
features = list(temp_layer.getFeatures())
if not features:
QMessageBox.critical(tab_widget, "Fehler",
f"Verfahrenskennzeichen {verfahrensnummer} nicht im WFS gefunden.")
2026-04-22 14:53:45 +02:00
return None, LoadStatus.NONE, []
# Objekte nach (adv_name, bezeichnung) gruppieren
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)
if len(kombinationen) == 1:
selected_key = next(iter(kombinationen))
selected_feats = kombinationen[selected_key]
else:
# Mehrere Kombinationen → Nutzerauswahl
key_list = sorted(kombinationen.keys())
selected_key = ask_bror_kombination(tab_widget, key_list)
if selected_key is None:
return None, LoadStatus.NONE, []
selected_feats = kombinationen[selected_key]
union_geom = QgsGeometry.unaryUnion([f.geometry() for f in selected_feats])
if union_geom is None or union_geom.isEmpty():
QMessageBox.critical(tab_widget, "Fehler", "Keine Geometrien zum Verschmelzen gefunden.")
2026-04-22 14:53:45 +02:00
return None, LoadStatus.NONE, []
2025-11-18 16:10:58 +01:00
# 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])
memory_layer.updateFields()
# Attributwerte aus dem ersten Feature der gewählten Kombination übernehmen
first_feat = selected_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":
# QDate-Objekte als DD.MM.YYYY formatieren
attrs.append(val.toString("dd.MM.yyyy"))
else:
attrs.append(str(val))
except Exception:
attrs.append("")
feat_union = QgsFeature()
feat_union.setGeometry(union_geom)
feat_union.setAttributes(attrs)
pr.addFeatures([feat_union])
memory_layer.updateExtents()
# Status: FIRST oder RELOAD; selected_feats für Gemarkungsermittlung weitergeben
return memory_layer, status, selected_feats