Files
Plugin_SN_Verfahrensgebiet/ui/tabs/working_tab.py
T
DanielandClaude Sonnet 5 0ac0757e42 Drei getrennte Flurstuecke-Ladelogiken auf eine konsolidiert (#47)
handle_flurst_aus_gemarkungen() hatte eine eigene Kopie des
"Layer bereits vorhanden"-Dialogs und rief lade_flurstuecke_per_gemarkung
direkt auf, statt die bereits vorhandene, getestete Logik in
flurstueck_alkis() wiederzuverwenden. Grund war, dass ohne_dialog=True
zwei unabhaengige Dinge zugleich abschaltete: die Schluessel-Abfrage per
ask_text UND den Existenz-Dialog samt Anhaengen-Dedup.

Neuer Parameter ohne_schluessel_abfrage trennt beides: er ueberspringt
nur die Schluessel-Abfrage (Schluessel kommen bereits aus der
Gemarkungen-Layer-Auswahl), der Existenz-Dialog mit Dedup bleibt aktiv.
ohne_dialog bleibt fuer die automatischen Pipelines unveraendert.

handle_flurst_alkis() und handle_flurst_aus_gemarkungen() teilen sich
jetzt zusaetzlich die Nachbehandlung des Ladeergebnisses
(_uebernimm_flurstuecke_ergebnis). lade_flurstuecke_per_gemarkung() wird
wieder nur noch von einer Stelle aus aufgerufen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 10:26:25 +02:00

821 lines
36 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/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
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("<b>Verfahrensgebiet</b>")
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("<b>Gemarkungen</b>")
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("<b>Flurstücke</b>")
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("<b>Grenzpunkte</b>")
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("<b>Verfahrensgebiet erstellen</b>")
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")
iface.mapCanvas().setExtent(enriched_layer.extent())
iface.mapCanvas().refresh()
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")
iface.mapCanvas().setExtent(enriched_layer.extent())
iface.mapCanvas().refresh()
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
# ---------------------------------------------------------------------------