"""sn_verfahrensgebiet/functions/verfahrensgebiet_alkis.py – ALKIS-Integration. Lädt Verfahrensgebiete aus dem ALKIS-WFS-Dienst. """ from enum import Enum from sn_basis.functions import ( 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 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", ] class LoadStatus(Enum): """Status eines Layer-Ladevorgangs. Attributes: NONE: Nichts geladen FIRST: Erstmalig geladen RELOAD: Neu geladen KEEP: Vorhandener Layer behalten """ NONE = "none" # nichts geladen FIRST = "first" # erstmalig geladen RELOAD = "reload" # neu geladen KEEP = "keep" # vorhandener Layer behalten APPEND = "append" # Features an vorhandenen Layer angehängt 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 """ 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 def verfahrensgebiet_alkis(tab_widget): """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. """ verfahrensnummer = get_variable("verfahrensnummer") if not verfahrensnummer: QMessageBox.critical(tab_widget, "Fehler", "In den Projekteigenschaften ist noch keine Verfahrensnummer eingetragen!") return None, LoadStatus.NONE, [] # Vorhandenen Layer prüfen 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 = ( 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.") return None, LoadStatus.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, [] # 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.") 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]) 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