"""sn_verfahrensgebiet/ui/tabs/working_tab.py – Bearbeitungs-Tab. Verwaltet die Verfahrensgebiet-Abgrenzung und ALKIS-Integration. """ from sn_basis.functions.qt_wrapper import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox from sn_basis.functions.qt_wrapper import Qt from sn_basis.functions import Qgis, QgsProject #QgsMessageLog from sn_basis.functions.qgisui_wrapper import QgsMapLayerComboBox, zoom_auf_layer from sn_basis.functions.qgiscore_wrapper import QgsMapLayerProxyModel from sn_basis.functions.verfahrensgebiet_manager import get_verfahrensgebiet_layer_id, set_verfahrensgebiet_layer_id from sn_basis.functions.dialog_wrapper import show_info_dialog, create_progress_dialog, ask_yes_no from sn_basis.functions.variable_wrapper import get_variable from sn_basis.modules.Pruefmanager import Pruefmanager from sn_basis.modules.pruef_ergebnis import pruef_ergebnis from qgis.utils import iface from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import LoadStatus from sn_verfahrensgebiet.functions.flurstueck_alkis import flurstueck_alkis from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import ( verfahrensgebiet_alkis_komplett, BROR_LAYER_NAME, ) from sn_verfahrensgebiet.functions.verfahrensgebiet_shape_komplett import verfahrensgebiet_shape_komplett from sn_verfahrensgebiet.functions.beteiligung_pruefung import ( beteiligung_pruefen, BeteiligungPruefungFehler, ) from sn_verfahrensgebiet.functions.grenzpunkt_alkis import grenzpunkte_gemarkungsweise, JOINED_LAYER_NAME from sn_verfahrensgebiet.functions.gemarkung_alkis import ( gemarkung_schluessel_und_namen_von_vg_geometrie, lade_gemarkungen_layer, ALKIS_VEREINF_URL, ) from sn_verfahrensgebiet.functions.umringstatistik_grenzpunkte import umringstatistik_grenzpunkte from sn_verfahrensgebiet.functions.verfahrensgebiet_edit_service import ( pruefe_flurstueck_layer, pruefe_auswahl, erstelle_verfahrensgebiet_aus_auswahl, ergaenze_verfahrensgebiet, entferne_aus_verfahrensgebiet, ersetze_verfahrensgebiet, ) from sn_basis.functions.message_wrapper import success, info, warning, error from sn_basis.functions.ly_style_wrapper import apply_style class WorkingTab(QWidget): """Tab für die Verfahrensgebiet-Bearbeitung und Verwaltung. Attributes: tab_title: Anzeigetitel des Tabs ("Bearbeitung") """ tab_title = "Bearbeitung" def __init__(self, parent=None): super().__init__(parent) layout = QVBoxLayout() layout.setAlignment(Qt.AlignmentFlag.AlignTop) # Abschnitt 1: Verfahrensgebiet laden row_verf_header = QHBoxLayout() lbl_verf = QLabel("Verfahrensgebiet") self.haken_verf = self._haken_label() row_verf_header.addWidget(lbl_verf) row_verf_header.addWidget(self.haken_verf) row_verf_header.addStretch() layout.addLayout(row_verf_header) row_verf_buttons = QHBoxLayout() self.btn_verf_alkis = QPushButton("Aus ALKIS laden") self.btn_verf_shape = QPushButton("Aus Shape laden") row_verf_buttons.addWidget(self.btn_verf_alkis) row_verf_buttons.addWidget(self.btn_verf_shape) layout.addLayout(row_verf_buttons) row_beteiligung_buttons = QHBoxLayout() self.btn_beteiligung_pruefen = QPushButton("Beteiligung prüfen") self.btn_beteiligung_pruefen.setToolTip( "CSV-Export aus dem BI-Server verwenden. benötigte Spalten: " "FLA_Verfahren-VEF_VKZF, FLA-Flurstuecke-Flurstueck, FLA_Beteiligung" ) row_beteiligung_buttons.addWidget(self.btn_beteiligung_pruefen) layout.addLayout(row_beteiligung_buttons) # Abschnitt 1b: Verfahrensgebiet-Layer auswählen row_layer_label = QHBoxLayout() lbl_layer = QLabel("Oder: Verfahrensgebiet-Layer auswählen") lbl_layer.setStyleSheet("font-style: italic; color: gray; margin-top: 8px;") row_layer_label.addWidget(lbl_layer) row_layer_label.addStretch() layout.addLayout(row_layer_label) self.verf_layer_combo = QgsMapLayerComboBox(self) self.verf_layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer) self.verf_layer_combo.setToolTip("Wähle den Verfahrensgebiet-Layer, oder lasse es leer für Automatik") self.verf_layer_combo.layerChanged.connect(self._on_verf_layer_changed) layout.addWidget(self.verf_layer_combo) # Abschnitt 2: Gemarkungen row_gem_header = QHBoxLayout() lbl_gem = QLabel("Gemarkungen") self.haken_gem = self._haken_label() row_gem_header.addWidget(lbl_gem) row_gem_header.addWidget(self.haken_gem) row_gem_header.addStretch() layout.addLayout(row_gem_header) row_gem_buttons = QHBoxLayout() self.btn_gem_alkis = QPushButton("Aus ALKIS laden") row_gem_buttons.addWidget(self.btn_gem_alkis) layout.addLayout(row_gem_buttons) # Abschnitt 2b: Flurstücke row_flurst_header = QHBoxLayout() lbl_flurst = QLabel("Flurstücke") self.haken_flurst = self._haken_label() row_flurst_header.addWidget(lbl_flurst) row_flurst_header.addWidget(self.haken_flurst) row_flurst_header.addStretch() layout.addLayout(row_flurst_header) row_flurst_buttons = QHBoxLayout() self.btn_flurst_alkis = QPushButton("Aus ALKIS laden") self.btn_flurst_shape = QPushButton("Aus Shape laden") row_flurst_buttons.addWidget(self.btn_flurst_alkis) row_flurst_buttons.addWidget(self.btn_flurst_shape) layout.addLayout(row_flurst_buttons) row_flurst_gem_buttons = QHBoxLayout() self.btn_flurst_aus_gemarkungen = QPushButton("aus gewählten Gemarkungen laden") row_flurst_gem_buttons.addWidget(self.btn_flurst_aus_gemarkungen) layout.addLayout(row_flurst_gem_buttons) # Abschnitt 2c: Grenzpunkte row_grenz_header = QHBoxLayout() lbl_grenz = QLabel("Grenzpunkte") self.haken_grenz = self._haken_label() row_grenz_header.addWidget(lbl_grenz) row_grenz_header.addWidget(self.haken_grenz) row_grenz_header.addStretch() layout.addLayout(row_grenz_header) row_grenz_buttons = QHBoxLayout() self.btn_grenz_alkis = QPushButton("Grenzpunkte aus ALKIS laden") row_grenz_buttons.addWidget(self.btn_grenz_alkis) layout.addLayout(row_grenz_buttons) row_umring_buttons = QHBoxLayout() self.btn_umring_statistik = QPushButton("Umringstatistik Grenzpunkte") row_umring_buttons.addWidget(self.btn_umring_statistik) layout.addLayout(row_umring_buttons) # Abschnitt 3: Verfahrensgebiet erstellen row_verf_erstellen_header = QHBoxLayout() lbl_verf_erstellen = QLabel("Verfahrensgebiet erstellen") row_verf_erstellen_header.addWidget(lbl_verf_erstellen) row_verf_erstellen_header.addStretch() layout.addLayout(row_verf_erstellen_header) row_verf_erstellen_buttons = QHBoxLayout() self.btn_verf_aus_auswahl = QPushButton("aus Auswahl") self.btn_verf_aus_liste = QPushButton("Aus Liste") row_verf_erstellen_buttons.addWidget(self.btn_verf_aus_auswahl) row_verf_erstellen_buttons.addWidget(self.btn_verf_aus_liste) layout.addLayout(row_verf_erstellen_buttons) layout.addStretch() self.setLayout(layout) # Pruefmanager für interaktive Entscheidungen (Ergänzen/Entfernen/Ersetzen) self.pruefmanager = Pruefmanager(ui_modus="qgis", parent=self) # Signale verbinden self.btn_verf_alkis.clicked.connect(self.handle_verf_alkis) self.btn_flurst_alkis.clicked.connect(self.handle_flurst_alkis) self.btn_flurst_aus_gemarkungen.clicked.connect(self.handle_flurst_aus_gemarkungen) self.btn_gem_alkis.clicked.connect(self.handle_gem_alkis) self.btn_grenz_alkis.clicked.connect(self.handle_grenz_alkis) self.btn_umring_statistik.clicked.connect(self.handle_umringstatistik) self.btn_verf_aus_auswahl.clicked.connect(self.handle_verf_aus_auswahl) self.btn_verf_aus_liste.clicked.connect(self.handle_verf_aus_liste) self.btn_verf_shape.clicked.connect(self.handle_verf_shape) # self.btn_flurst_shape.clicked.connect(self.handle_flurst_shape) self.btn_beteiligung_pruefen.clicked.connect(self.handle_beteiligung_pruefen) # Beim Init: aktuelle Verfahrensgebiet-Layer-ID laden und im Dropdown anzeigen self._restore_verf_layer_selection() def handle_verf_alkis(self) -> None: """Startet den vollständigen VG-Lade-Workflow (ALKIS → Gemarkungen → Flurstücke → Verschneidung → Flächenvergleich → opt. Umringsprüfung). Delegiert an :func:`~sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.verfahrensgebiet_alkis_komplett`. Nach Erfolg werden enriched VG-Layer, Flurstücks-Layer und BROR-Layer ins Projekt eingefügt, Styles angewendet und die UI-Elemente aktualisiert. """ self._set_busy(self.btn_verf_alkis, True, "Aus ALKIS laden") try: enriched_layer, flst_layer, bror_layer, status = verfahrensgebiet_alkis_komplett(self) if status == LoadStatus.NONE or not enriched_layer: set_verfahrensgebiet_layer_id("") return project = QgsProject.instance() # Flurstücks-Layer ins Projekt (sofern neu geladen) if flst_layer and flst_layer.isValid(): existing_flst = project.mapLayersByName(flst_layer.name()) if not existing_flst: project.addMapLayer(flst_layer) apply_style(flst_layer, "GIS_Flst_Beschriftung_ALKIS_NAS.qml") self.setze_haken(self.haken_flurst, True) # BROR-Layer bekommt seinen Style bereits direkt beim Laden in # verfahrensgebiet_alkis_komplett() (auch im "Nur laden"-Fall). # Bei "Nur Objekte laden" (kein weiterer Ablauf) ist enriched_layer # tatsächlich nur der rohe BROR-Layer (erkennbar am Namen) – hier # fand keine Flurstücksverschneidung statt, es gibt also keinen # echten VG-Layer: VG-Style/-Kombobox/-Haken/-Meldung überspringen. if enriched_layer.name() != BROR_LAYER_NAME: # Enriched VG-Layer ins Projekt (ggf. bereits registriert, z.B. bei "als VG laden") if not project.mapLayer(enriched_layer.id()): project.addMapLayer(enriched_layer) apply_style(enriched_layer, "verfahrensgebiet.qml") # Ausdehnung ins KBS der Kartenansicht umrechnen (Basis #99) zoom_auf_layer(enriched_layer, iface.mapCanvas()) self.setze_haken(self.haken_verf, True) self.verf_layer_combo.setLayer(enriched_layer) self._on_verf_layer_changed(enriched_layer) msg = "erstmalig geladen" if status == LoadStatus.FIRST else "neu geladen" success("Verfahrensgebiet", f"Layer wurde {msg} (inkl. Flurstücksverschneidung).", duration=5) finally: self._set_busy(self.btn_verf_alkis, False, "Aus ALKIS laden") def handle_verf_shape(self) -> None: """Lädt ein Verfahrensgebiet aus einem ZIP-Archiv mit Shapefile-Set. Delegiert an :func:`~sn_verfahrensgebiet.functions.verfahrensgebiet_shape_komplett.verfahrensgebiet_shape_komplett`. Nach Erfolg werden Shape-VG-Layer und Flurstücks-Layer ins Projekt eingefügt, Styles angewendet und die UI-Elemente aktualisiert. """ # Vorprüfung: Verfahrensnummer muss gesetzt sein # vkz = get_variable("verfahrensnummer") or "" # if not str(vkz).strip(): # if not ask_yes_no( # "Verfahrensnummer nicht gesetzt", # "Verfahrensnummer nicht gesetzt. Trotzdem fortfahren?", # default=False, # parent=self, # ): # return self._set_busy(self.btn_verf_shape, True, "Aus Shape laden") try: enriched_layer, flst_layer, bror_layer, status = verfahrensgebiet_shape_komplett(self) if status == LoadStatus.NONE or not enriched_layer: set_verfahrensgebiet_layer_id("") return project = QgsProject.instance() # Flurstücks-Layer ins Projekt (sofern neu geladen) if flst_layer and flst_layer.isValid(): existing_flst = project.mapLayersByName(flst_layer.name()) if not existing_flst: project.addMapLayer(flst_layer) apply_style(flst_layer, "GIS_Flst_Beschriftung_ALKIS_NAS.qml") self.setze_haken(self.haken_flurst, True) # BROR-Layer: QML-Stil anwenden (sofern geladen) if bror_layer and bror_layer.isValid(): apply_style(bror_layer, "BauRaumOderBodenordnungsrecht.qml") # Shape-VG-Layer ins Projekt if not project.mapLayer(enriched_layer.id()): project.addMapLayer(enriched_layer) apply_style(enriched_layer, "verfahrensgebiet.qml") # Ausdehnung ins KBS der Kartenansicht umrechnen (Basis #99) zoom_auf_layer(enriched_layer, iface.mapCanvas()) self.setze_haken(self.haken_verf, True) self.verf_layer_combo.setLayer(enriched_layer) self._on_verf_layer_changed(enriched_layer) success("Verfahrensgebiet", "Layer aus Shape geladen.", duration=5) finally: self._set_busy(self.btn_verf_shape, False, "Aus Shape laden") def handle_beteiligung_pruefen(self) -> None: """Gleicht eine BI-Server-CSV-Beteiligungsliste mit dem Verfahrensgebiet ab. Delegiert an :func:`~sn_verfahrensgebiet.functions.beteiligung_pruefung.beteiligung_pruefen`. Ist noch kein (verwertbares) Verfahrensgebiet geladen, fordert die Funktion den Nutzer per Meldung auf, es zunächst über "Aus ALKIS laden" zu laden – es gibt kein automatisches Laden im Hintergrund. Sonst werden ein oder mehrere Ergebnis-Layer ins Projekt eingefügt, gestylt und eine Statistik als Messagebox angezeigt. """ self._set_busy(self.btn_beteiligung_pruefen, True, "Beteiligung prüfen") try: try: ergebnis = beteiligung_pruefen(self) except BeteiligungPruefungFehler as exc: QMessageBox.critical(self, "Beteiligung prüfen", str(exc)) return if ergebnis is None: return # Nutzerabbruch des Dateidialogs project = QgsProject.instance() for layer, style_name in ergebnis.layers: if layer is None or not layer.isValid(): continue if not project.mapLayer(layer.id()): project.addMapLayer(layer) if style_name: apply_style(layer, style_name) iface.mapCanvas().refresh() QMessageBox.information(self, "Beteiligung prüfen", ergebnis.statistik_text) finally: self._set_busy(self.btn_beteiligung_pruefen, False, "Beteiligung prüfen") def _set_busy(self, button: QPushButton, busy: bool, original_text: str = "Aus ALKIS laden") -> None: """Setzt den Busy-State eines Buttons. Parameters ---------- button : Der zu sperrende/entsperrende Button. busy : bool True zum Sperren (Text → "Lade ..."), False zum Freigeben. original_text : str Text, der nach dem Freigeben wiederhergestellt wird. Jeder Button gibt seinen eigenen Originaltext explizit an. """ button.setEnabled(not busy) button.setText("Lade ..." if busy else original_text) def _haken_label(self) -> QLabel: label = QLabel() label.setFixedSize(20, 20) label.setAlignment(Qt.AlignmentFlag.AlignCenter) label.setText("") # Start ohne Haken return label def setze_haken(self, label: QLabel, aktiv: bool): """Zeigt oder entfernt den grünen Haken (Unicode), ohne Theme-Icons.""" if aktiv: label.setText("✓") label.setStyleSheet("color: #2ea043; font-weight: bold;") else: label.setText("") label.setStyleSheet("") def _on_verf_layer_changed(self, layer) -> None: """Callback: Nutzer hat einen anderen Layer im Dropdown gewählt.""" if layer and layer.isValid(): # Speichere die Layer-ID in der zentralen Variable set_verfahrensgebiet_layer_id(layer.id()) else: # Layer is None oder invalid; leere die Variable set_verfahrensgebiet_layer_id("") def _restore_verf_layer_selection(self) -> None: """Beim Tab-Init: Lade die zuletzt gespeicherte Verfahrensgebiet-Layer-ID.""" layer_id = get_verfahrensgebiet_layer_id() if layer_id: try: project = QgsProject.instance() layer = project.mapLayer(layer_id) if layer and layer.isValid(): self.verf_layer_combo.setLayer(layer) except Exception: pass # ------------------------------------------------------------------ # Flurstücke-Handler # ------------------------------------------------------------------ def _uebernimm_flurstuecke_ergebnis(self, layer, status) -> None: """Übernimmt das Ergebnis von flurstueck_alkis() ins Projekt (FIRST/RELOAD/APPEND/KEEP).""" if status in (LoadStatus.FIRST, LoadStatus.RELOAD): QgsProject.instance().addMapLayer(layer) apply_style(layer, "GIS_Flst_Beschriftung_ALKIS_NAS.qml") iface.mapCanvas().refresh() self.setze_haken(self.haken_flurst, True) msg = "erstmalig geladen" if status == LoadStatus.FIRST else "neu geladen" success("Flurstücke", f"Layer wurde {msg}.", duration=5) elif status == LoadStatus.KEEP: self.setze_haken(self.haken_flurst, True) info("Flurstücke", "Vorhandener Layer wurde behalten.", duration=4) elif status == LoadStatus.APPEND: self.setze_haken(self.haken_flurst, True) iface.mapCanvas().refresh() success("Flurstücke", "Features wurden an den vorhandenen Layer angehängt.", duration=5) def handle_flurst_alkis(self) -> None: """Lädt Flurstücke aus ALKIS-WFS anhand von Gemarkungsschlüsseln.""" self._set_busy(self.btn_flurst_alkis, True, "Aus ALKIS laden") try: layer, status = flurstueck_alkis(self) if not layer or not layer.isValid(): return self._uebernimm_flurstuecke_ergebnis(layer, status) finally: self._set_busy(self.btn_flurst_alkis, False, "Aus ALKIS laden") def handle_flurst_aus_gemarkungen(self) -> None: """Lädt Flurstücke aus ALKIS anhand der Auswahl im Layer 'Gemarkungen'. - Kein Layer 'Gemarkungen': Layer wird geladen, Nutzer aufgefordert Auswahl zu treffen. - Keine Auswahl: Warnmeldung. - Auswahl vorhanden: Gemarkungsschlüssel extrahieren und Flurstücke direkt laden. """ self._set_busy(self.btn_flurst_aus_gemarkungen, True, "aus gewählten Gemarkungen laden") try: project = QgsProject.instance() # Fall A: Layer 'Gemarkungen' nicht vorhanden → laden gem_layers = project.mapLayersByName("Gemarkungen") if not gem_layers: gem_layer = lade_gemarkungen_layer() if not gem_layer: error( "Gemarkungen", f"Gemarkungslayer konnte nicht geladen werden – " f"Dienst nicht erreichbar ({ALKIS_VEREINF_URL}). " f"Bitte Netzwerkverbindung prüfen.", duration=0, ) return project.addMapLayer(gem_layer) apply_style(gem_layer, "gemarkungen_beschriftet.qml") self.setze_haken(self.haken_gem, True) QMessageBox.information( self, "Gemarkungen", "Gemarkungslayer wurde geladen – bitte Gemarkungen auswählen.", ) return gemarkungen_layer = gem_layers[0] # Fall B: Keine Auswahl if not gemarkungen_layer.selectedFeatureCount(): QMessageBox.warning( self, "Gemarkungen", "Bitte mindestens eine Gemarkung im Layer Gemarkungen auswählen.", ) return # Fall C: Auswahl vorhanden → Schlüssel extrahieren vorgabe_schluessel = [] vorgabe_namen = [] for feat in gemarkungen_layer.selectedFeatures(): try: schluessel = str(feat["schluessel"]).strip() nummer = schluessel[-4:] if len(schluessel) >= 4 else schluessel except Exception: nummer = "" try: name = str(feat["ave_name"]).strip() except Exception: name = "" if nummer: vorgabe_schluessel.append(nummer) vorgabe_namen.append(name) if not vorgabe_schluessel: error( "Flurstücke", "Aus der Gemarkungsauswahl konnten keine gültigen Gemarkungsschlüssel extrahiert werden.", duration=8, ) return # Flurstücke laden – Schlüssel bereits bekannt, aber bei bestehendem Layer # trotzdem interaktiv nachfragen (Überschreiben/Anhängen/Abbrechen, inkl. Dedup) layer, status = flurstueck_alkis( self, vorgabe_schluessel=vorgabe_schluessel, vorgabe_namen=vorgabe_namen, ohne_schluessel_abfrage=True, ) if not layer or not layer.isValid(): # flurstueck_alkis() hat bei Abbruch/Fehler bereits selbst eine Meldung gezeigt. return self._uebernimm_flurstuecke_ergebnis(layer, status) finally: self._set_busy(self.btn_flurst_aus_gemarkungen, False, "aus gewählten Gemarkungen laden") # ------------------------------------------------------------------ # Gemarkungen-Handler # ------------------------------------------------------------------ def handle_gem_alkis(self) -> None: """Lädt den KatasterBezirk-Layer aus dem vereinfachten ALKIS-WFS als Layer „Gemarkungen" ins Projekt. Ist ein Layer namens „Gemarkungen" bereits vorhanden, wird er behalten und eine Info-Meldung angezeigt (kein doppeltes Laden). """ self._set_busy(self.btn_gem_alkis, True, "Aus ALKIS laden") try: existing = QgsProject.instance().mapLayersByName("Gemarkungen") if existing: self.setze_haken(self.haken_gem, True) info("Gemarkungen", "Layer 'Gemarkungen' ist bereits im Projekt vorhanden.", duration=4) return layer = lade_gemarkungen_layer() if not layer: warning( "Gemarkungen", "Layer konnte nicht geladen werden – Dienst nicht erreichbar oder ungültig.", duration=6, ) return QgsProject.instance().addMapLayer(layer) apply_style(layer, "gemarkungen_beschriftet.qml") self.setze_haken(self.haken_gem, True) success("Gemarkungen", "Layer 'Gemarkungen' wurde geladen.", duration=4) finally: self._set_busy(self.btn_gem_alkis, False, "Aus ALKIS laden") # ------------------------------------------------------------------ # Verfahrensgebiet-erstellen-Handler # ------------------------------------------------------------------ def handle_verf_aus_auswahl(self) -> None: """Erstellt oder aktualisiert den Verfahrensgebiet-Layer aus der Flurstück-Auswahl.""" self._set_busy(self.btn_verf_aus_auswahl, True, "aus Auswahl") try: # 1. Aktiven Layer ermitteln active_layer = iface.activeLayer() if not active_layer: warning( "Verfahrensgebiet erstellen", "Kein aktiver Layer. Bitte den Flurstück-Layer aktivieren.", duration=None, ) return # 2. Vorbedingungen via Layerpruefer ergebnis = pruefe_flurstueck_layer(active_layer) if not ergebnis.ok: warning("Verfahrensgebiet erstellen", ergebnis.meldung, duration=None) return # 3. Auswahl prüfen ergebnis_auswahl = pruefe_auswahl(active_layer) if not ergebnis_auswahl.ok: warning("Verfahrensgebiet erstellen", ergebnis_auswahl.meldung, duration=None) return # 4. Bestehenden Verfahrensgebiet-Layer prüfen existing_layers = QgsProject.instance().mapLayersByName("Verfahrensgebiet") if not existing_layers: # Erstlauf: neuen Memory-Layer erzeugen layer, erfolgreich, fehlgeschlagen = erstelle_verfahrensgebiet_aus_auswahl( active_layer ) if not layer or not layer.isValid(): error("Verfahrensgebiet erstellen", "Layer konnte nicht erstellt werden.") return QgsProject.instance().addMapLayer(layer) apply_style(layer, "verfahrensgebiet.qml") iface.mapCanvas().refresh() self.verf_layer_combo.setLayer(layer) self._on_verf_layer_changed(layer) self.setze_haken(self.haken_verf, True) QMessageBox.information( self, "Verfahrensgebiet erstellt", f"Verfahrensgebiet aus Auswahl erstellt.\n" f"Erfolgreich: {erfolgreich}\n" f"Fehlgeschlagen: {fehlgeschlagen}", ) return # 5. Bestehender Layer: Nutzerentscheidung via Pruefmanager entscheidung_ergebnis = self.pruefmanager.verarbeite( pruef_ergebnis( ok=False, aktion="verfahrensgebiet_layer_existiert", meldung=( "Ein Verfahrensgebiet-Layer existiert bereits.\n" "Was soll mit der Auswahl geschehen?" ), kontext=existing_layers[0], ) ) aktion = entscheidung_ergebnis.aktion verf_layer = existing_layers[0] if aktion == "verfahrensgebiet_abbrechen" or not entscheidung_ergebnis.ok: return elif aktion == "verfahrensgebiet_ergaenzen": _, hinzugefuegt, bereits_vorhanden, fehlgeschlagen = ergaenze_verfahrensgebiet( verf_layer, active_layer ) apply_style(verf_layer, "verfahrensgebiet.qml") iface.mapCanvas().refresh() self.setze_haken(self.haken_verf, True) QMessageBox.information( self, "Flurstücke ergänzt", f"Hinzugefügt: {hinzugefuegt}\n" f"Bereits vorhanden (übersprungen): {bereits_vorhanden}\n" f"Fehlgeschlagen: {fehlgeschlagen}", ) elif aktion == "verfahrensgebiet_entfernen": _, entfernt, nicht_enthalten, fehlgeschlagen = entferne_aus_verfahrensgebiet( verf_layer, active_layer ) apply_style(verf_layer, "verfahrensgebiet.qml") iface.mapCanvas().refresh() QMessageBox.information( self, "Flurstücke entfernt", f"Entfernt: {entfernt}\n" f"Nicht im Verfahrensgebiet (übersprungen): {nicht_enthalten}\n" f"Fehlgeschlagen: {fehlgeschlagen}", ) elif aktion == "verfahrensgebiet_ersetzen": for lyr in existing_layers: QgsProject.instance().removeMapLayer(lyr.id()) layer, erfolgreich, fehlgeschlagen = ersetze_verfahrensgebiet(active_layer) if not layer or not layer.isValid(): error("Verfahrensgebiet erstellen", "Layer konnte nicht erstellt werden.") return QgsProject.instance().addMapLayer(layer) apply_style(layer, "verfahrensgebiet.qml") iface.mapCanvas().refresh() self.verf_layer_combo.setLayer(layer) self._on_verf_layer_changed(layer) self.setze_haken(self.haken_verf, True) QMessageBox.information( self, "Verfahrensgebiet ersetzt", f"Verfahrensgebiet neu erstellt.\n" f"Erfolgreich: {erfolgreich}\n" f"Fehlgeschlagen: {fehlgeschlagen}", ) finally: self._set_busy(self.btn_verf_aus_auswahl, False, "aus Auswahl") def handle_verf_aus_liste(self) -> None: """Platzhalter – Verfahrensgebiet aus Liste erstellen (noch nicht implementiert).""" show_info_dialog( "Noch nicht implementiert", "Die Funktion 'Aus Liste' steht in einer zukünftigen Version zur Verfügung.", parent=self, ) # ------------------------------------------------------------------ # Grenzpunkt-Handler # ------------------------------------------------------------------ def handle_grenz_alkis(self) -> None: """Lädt Grenzpunkte gemarkungsweise aus ALKIS. Verwendet denselben Gemarkungsfilter wie "Verfahrensgebiet aus ALKIS laden": Die Gemarkungsschlüssel werden per Spatial-Join aus der Geometrie des aktuell gewählten Verfahrensgebiet-Layers abgeleitet. """ # ------------------------------------------------------------------ # # Schritt 0: Fortschrittsdialog sofort anzeigen # # ------------------------------------------------------------------ # # 4 Vorbereitungsschritte (Geometrie, Gemarkungen, Hits, Laden) – # die genaue Schrittzahl wird in grenzpunkte_gemarkungsweise verfeinert. prog = create_progress_dialog( 4, "Grenzpunkte aus ALKIS laden", "Verfahrensgebiet prüfen …" ) prog.set_value(0) # Verfahrensgebiet-Layer aus dem Dropdown holen vg_layer = self.verf_layer_combo.currentLayer() if not vg_layer or not vg_layer.isValid(): prog.close() warning( "Grenzpunkte", "Kein Verfahrensgebiet-Layer gewählt. Bitte zuerst ein Verfahrensgebiet laden.", duration=6, ) return # Geometrie des ersten Features als Suchgeometrie feats = list(vg_layer.getFeatures()) if not feats: prog.close() warning("Grenzpunkte", "Verfahrensgebiet-Layer enthält keine Features.", duration=5) return # Geometrie über alle Features vereinen from qgis.core import QgsGeometry # type: ignore[import] vg_geom = QgsGeometry.unaryUnion([f.geometry() for f in feats if f.hasGeometry()]) if not vg_geom or vg_geom.isEmpty(): prog.close() warning("Grenzpunkte", "Verfahrensgebiet-Layer hat keine Geometrie.", duration=5) return prog.set_label("Gemarkungsschlüssel per WFS ermitteln …") prog.set_value(1) if prog.is_canceled(): return # Gemarkungsschlüssel per Spatial-Join ermitteln (identisch zu VG-Workflow) schluessel_namen = gemarkung_schluessel_und_namen_von_vg_geometrie(vg_geom) schluessel_liste = [nr for nr, _ in schluessel_namen] if not schluessel_liste: prog.close() warning( "Grenzpunkte", "Keine Gemarkungen für das Verfahrensgebiet gefunden. " "Bitte Netzwerkverbindung prüfen.", duration=6, ) return prog.set_label(f"{len(schluessel_liste)} Gemarkung(en) gefunden – Grenzpunkte laden …") prog.set_value(2) if prog.is_canceled(): return self._set_busy(self.btn_grenz_alkis, True, "Grenzpunkte aus ALKIS laden") prog.close() # Vorbereitungs-Dialog schließen; grenzpunkte_gemarkungsweise öffnet eigenen try: joined_layer = grenzpunkte_gemarkungsweise(self, schluessel_liste, vg_geom=vg_geom) if joined_layer and joined_layer.isValid(): iface.mapCanvas().refresh() self.setze_haken(self.haken_grenz, True) QMessageBox.information( self, "Grenzpunkte geladen", f"Grenzpunkte erfolgreich geladen ({joined_layer.featureCount()} Objekte).", ) elif joined_layer is None: error( "Grenzpunkte", "Laden fehlgeschlagen – WFS- oder Netzwerkfehler. " "Details unter Ansicht -> Bedienfelder -> Protokollnachrichten " "(Kategorie 'sn_verfahrensgebiet'). " "Bitte prüfen Sie ggf. die Proxy-Einstellungen.", duration=0, ) finally: self._set_busy(self.btn_grenz_alkis, False, "Grenzpunkte aus ALKIS laden") def handle_umringstatistik(self) -> None: """Klassifiziert Grenzpunkte auf dem VG-Umring nach Lagegenauigkeit. Sucht den vorhandenen ``Grenzpunkte (ALKIS)``-Layer im Projekt und kombiniert ihn mit dem gewählten Verfahrensgebiet-Layer, um die Umringstatistik zu berechnen. """ # 1. Grenzpunkte-Layer aus dem Projekt holen project = QgsProject.instance() gp_layers = project.mapLayersByName(JOINED_LAYER_NAME) if not gp_layers: warning( "Umringstatistik", f"Kein Layer '{JOINED_LAYER_NAME}' im Projekt gefunden. " "Bitte zuerst 'Grenzpunkte aus ALKIS laden' ausführen.", duration=8, ) return grenzpunkte_layer = gp_layers[0] # 2. Verfahrensgebiet-Layer aus Dropdown vg_layer = self.verf_layer_combo.currentLayer() if not vg_layer or not vg_layer.isValid(): warning( "Umringstatistik", "Kein Verfahrensgebiet-Layer gewählt. Bitte zuerst ein Verfahrensgebiet laden.", duration=6, ) return self._set_busy(self.btn_umring_statistik, True, "Umringstatistik Grenzpunkte") try: result_layer = umringstatistik_grenzpunkte(self, grenzpunkte_layer, vg_layer) if result_layer and result_layer.isValid(): iface.mapCanvas().refresh() finally: self._set_busy(self.btn_umring_statistik, False, "Umringstatistik Grenzpunkte") # --------------------------------------------------------------------------- # Modul-Hilfsfunktionen # ---------------------------------------------------------------------------