2026-04-20 14:33:13 +02:00
|
|
|
|
"""sn_verfahrensgebiet/ui/tabs/working_tab.py – Bearbeitungs-Tab.
|
|
|
|
|
|
Verwaltet die Verfahrensgebiet-Abgrenzung und ALKIS-Integration.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-03-06 10:11:17 +01:00
|
|
|
|
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
|
2026-03-27 11:19:07 +01:00
|
|
|
|
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
|
2026-05-05 19:57:35 +02:00
|
|
|
|
from sn_basis.functions.dialog_wrapper import show_info_dialog, create_progress_dialog
|
2026-04-21 21:50:26 +02:00
|
|
|
|
from sn_basis.modules.Pruefmanager import Pruefmanager
|
|
|
|
|
|
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
2025-11-18 16:10:58 +01:00
|
|
|
|
from qgis.utils import iface
|
|
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import LoadStatus
|
2026-04-21 21:50:26 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.flurstueck_alkis import flurstueck_alkis
|
2026-04-22 14:53:45 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import verfahrensgebiet_alkis_komplett
|
2026-05-05 21:17:19 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.grenzpunkt_alkis import grenzpunkte_gemarkungsweise, JOINED_LAYER_NAME
|
2026-05-01 23:32:28 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.gemarkung_alkis import gemarkung_schluessel_und_namen_von_vg_geometrie
|
2026-05-05 21:17:19 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.umringstatistik_grenzpunkte import umringstatistik_grenzpunkte
|
2026-04-21 21:50:26 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.verfahrensgebiet_edit_service import (
|
|
|
|
|
|
pruefe_flurstueck_layer,
|
|
|
|
|
|
pruefe_auswahl,
|
|
|
|
|
|
erstelle_verfahrensgebiet_aus_auswahl,
|
|
|
|
|
|
ergaenze_verfahrensgebiet,
|
|
|
|
|
|
entferne_aus_verfahrensgebiet,
|
|
|
|
|
|
ersetze_verfahrensgebiet,
|
|
|
|
|
|
)
|
2026-03-06 10:11:17 +01:00
|
|
|
|
from sn_basis.functions.message_wrapper import success, info, warning, error
|
|
|
|
|
|
from sn_basis.functions.ly_style_wrapper import apply_style
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
|
|
|
|
|
class WorkingTab(QWidget):
|
2026-04-20 14:33:13 +02:00
|
|
|
|
"""Tab für die Verfahrensgebiet-Bearbeitung und Verwaltung.
|
|
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
|
tab_title: Anzeigetitel des Tabs ("Bearbeitung")
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2025-11-18 16:10:58 +01:00
|
|
|
|
tab_title = "Bearbeitung"
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
|
|
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
|
|
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# Abschnitt 1: Verfahrensgebiet laden
|
2025-11-18 16:10:58 +01:00
|
|
|
|
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)
|
|
|
|
|
|
|
2026-03-27 11:19:07 +01:00
|
|
|
|
# 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)
|
|
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# Abschnitt 2: Flurstücke
|
2025-11-18 16:10:58 +01:00
|
|
|
|
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)
|
|
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
# Abschnitt 2b: 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)
|
|
|
|
|
|
|
2026-05-05 21:17:19 +02:00
|
|
|
|
row_umring_buttons = QHBoxLayout()
|
|
|
|
|
|
self.btn_umring_statistik = QPushButton("Umringstatistik Grenzpunkte")
|
|
|
|
|
|
row_umring_buttons.addWidget(self.btn_umring_statistik)
|
|
|
|
|
|
layout.addLayout(row_umring_buttons)
|
|
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# 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)
|
|
|
|
|
|
|
2025-11-18 16:10:58 +01:00
|
|
|
|
layout.addStretch()
|
|
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# Pruefmanager für interaktive Entscheidungen (Ergänzen/Entfernen/Ersetzen)
|
|
|
|
|
|
self.pruefmanager = Pruefmanager(ui_modus="qgis", parent=self)
|
|
|
|
|
|
|
2025-11-18 16:10:58 +01:00
|
|
|
|
# Signale verbinden
|
|
|
|
|
|
self.btn_verf_alkis.clicked.connect(self.handle_verf_alkis)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self.btn_flurst_alkis.clicked.connect(self.handle_flurst_alkis)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
self.btn_grenz_alkis.clicked.connect(self.handle_grenz_alkis)
|
2026-05-05 21:17:19 +02:00
|
|
|
|
self.btn_umring_statistik.clicked.connect(self.handle_umringstatistik)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self.btn_verf_aus_auswahl.clicked.connect(self.handle_verf_aus_auswahl)
|
|
|
|
|
|
self.btn_verf_aus_liste.clicked.connect(self.handle_verf_aus_liste)
|
2025-11-18 16:10:58 +01:00
|
|
|
|
# self.btn_verf_shape.clicked.connect(self.handle_verf_shape)
|
|
|
|
|
|
# self.btn_flurst_shape.clicked.connect(self.handle_flurst_shape)
|
|
|
|
|
|
|
2026-03-27 11:19:07 +01:00
|
|
|
|
# Beim Init: aktuelle Verfahrensgebiet-Layer-ID laden und im Dropdown anzeigen
|
|
|
|
|
|
self._restore_verf_layer_selection()
|
|
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
def handle_verf_alkis(self) -> None:
|
2026-04-23 22:24:14 +02:00
|
|
|
|
"""Startet den vollständigen VG-Lade-Workflow (ALKIS → Gemarkungen → Flurstücke →
|
|
|
|
|
|
Verschneidung → Flächenvergleich → opt. Umringsprüfung).
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
Delegiert an :func:`~sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.verfahrensgebiet_alkis_komplett`.
|
2026-04-23 22:24:14 +02:00
|
|
|
|
Nach Erfolg werden enriched VG-Layer, Flurstücks-Layer und BROR-Layer
|
|
|
|
|
|
ins Projekt eingefügt, Styles angewendet und die UI-Elemente aktualisiert.
|
2026-04-22 14:53:45 +02:00
|
|
|
|
"""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self._set_busy(self.btn_verf_alkis, True, "Aus ALKIS laden")
|
2025-11-18 16:10:58 +01:00
|
|
|
|
try:
|
2026-04-23 22:24:14 +02:00
|
|
|
|
enriched_layer, flst_layer, bror_layer, status = verfahrensgebiet_alkis_komplett(self)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
if status == LoadStatus.NONE or not enriched_layer:
|
2026-04-08 14:36:39 +02:00
|
|
|
|
set_verfahrensgebiet_layer_id("")
|
2025-11-18 16:10:58 +01:00
|
|
|
|
return
|
|
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
project = QgsProject.instance()
|
2026-04-08 14:36:39 +02:00
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
# 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)
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
2026-05-22 14:14:04 +02:00
|
|
|
|
# BROR-Layer: QML-Stil anwenden
|
2026-04-23 22:24:14 +02:00
|
|
|
|
if bror_layer and bror_layer.isValid():
|
2026-05-22 14:14:04 +02:00
|
|
|
|
apply_style(bror_layer, "BauRaumOderBodenordnungsrecht.qml")
|
2026-04-23 22:24:14 +02:00
|
|
|
|
|
2026-05-05 19:46:08 +02:00
|
|
|
|
# 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)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
apply_style(enriched_layer, "verfahrensgebiet.qml")
|
|
|
|
|
|
iface.mapCanvas().setExtent(enriched_layer.extent())
|
|
|
|
|
|
iface.mapCanvas().refresh()
|
|
|
|
|
|
self.setze_haken(self.haken_verf, True)
|
2026-04-08 14:36:39 +02:00
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
self.verf_layer_combo.setLayer(enriched_layer)
|
|
|
|
|
|
self._on_verf_layer_changed(enriched_layer)
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
msg = "erstmalig geladen" if status == LoadStatus.FIRST else "neu geladen"
|
|
|
|
|
|
success("Verfahrensgebiet", f"Layer wurde {msg} (inkl. Flurstücksverschneidung).", duration=5)
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
|
|
|
|
|
finally:
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self._set_busy(self.btn_verf_alkis, False, "Aus ALKIS laden")
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
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.
|
|
|
|
|
|
"""
|
2025-11-18 16:10:58 +01:00
|
|
|
|
button.setEnabled(not busy)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
button.setText("Lade ..." if busy else original_text)
|
2025-11-18 16:10:58 +01:00
|
|
|
|
|
|
|
|
|
|
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("")
|
|
|
|
|
|
|
2026-03-27 11:19:07 +01:00
|
|
|
|
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
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# Flurstücke-Handler
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
if status in (LoadStatus.FIRST, LoadStatus.RELOAD):
|
|
|
|
|
|
QgsProject.instance().addMapLayer(layer)
|
|
|
|
|
|
# iface.mapCanvas().setExtent(layer.extent()) # Kartenausschnitt bleibt unverändert
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
self._set_busy(self.btn_flurst_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,
|
|
|
|
|
|
)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# Grenzpunkt-Handler
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def handle_grenz_alkis(self) -> None:
|
2026-05-01 23:32:28 +02:00
|
|
|
|
"""Lädt Grenzpunkte gemarkungsweise aus ALKIS.
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
2026-05-01 23:32:28 +02:00
|
|
|
|
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.
|
2026-04-22 14:53:45 +02:00
|
|
|
|
"""
|
2026-05-05 19:57:35 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
2026-05-01 23:32:28 +02:00
|
|
|
|
# Verfahrensgebiet-Layer aus dem Dropdown holen
|
|
|
|
|
|
vg_layer = self.verf_layer_combo.currentLayer()
|
|
|
|
|
|
if not vg_layer or not vg_layer.isValid():
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.close()
|
2026-05-01 23:32:28 +02:00
|
|
|
|
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:
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.close()
|
2026-05-01 23:32:28 +02:00
|
|
|
|
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():
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.close()
|
2026-05-01 23:32:28 +02:00
|
|
|
|
warning("Grenzpunkte", "Verfahrensgebiet-Layer hat keine Geometrie.", duration=5)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.set_label("Gemarkungsschlüssel per WFS ermitteln …")
|
|
|
|
|
|
prog.set_value(1)
|
|
|
|
|
|
if prog.is_canceled():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-05-01 23:32:28 +02:00
|
|
|
|
# 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:
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.close()
|
2026-05-01 23:32:28 +02:00
|
|
|
|
warning(
|
|
|
|
|
|
"Grenzpunkte",
|
|
|
|
|
|
"Keine Gemarkungen für das Verfahrensgebiet gefunden. "
|
|
|
|
|
|
"Bitte Netzwerkverbindung prüfen.",
|
|
|
|
|
|
duration=6,
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.set_label(f"{len(schluessel_liste)} Gemarkung(en) gefunden – Grenzpunkte laden …")
|
|
|
|
|
|
prog.set_value(2)
|
|
|
|
|
|
if prog.is_canceled():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-05-01 23:32:28 +02:00
|
|
|
|
self._set_busy(self.btn_grenz_alkis, True, "Grenzpunkte aus ALKIS laden")
|
2026-05-05 19:57:35 +02:00
|
|
|
|
prog.close() # Vorbereitungs-Dialog schließen; grenzpunkte_gemarkungsweise öffnet eigenen
|
2026-05-01 23:32:28 +02:00
|
|
|
|
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)
|
2026-05-06 11:43:44 +02:00
|
|
|
|
QMessageBox.information(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"Grenzpunkte geladen",
|
2026-05-01 23:32:28 +02:00
|
|
|
|
f"Grenzpunkte erfolgreich geladen ({joined_layer.featureCount()} Objekte).",
|
2026-05-06 11:43:44 +02:00
|
|
|
|
)
|
|
|
|
|
|
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,
|
2026-05-01 23:32:28 +02:00
|
|
|
|
)
|
|
|
|
|
|
finally:
|
|
|
|
|
|
self._set_busy(self.btn_grenz_alkis, False, "Grenzpunkte aus ALKIS laden")
|
2026-04-23 22:24:14 +02:00
|
|
|
|
|
2026-05-05 21:17:19 +02:00
|
|
|
|
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")
|
|
|
|
|
|
|
2026-04-23 22:24:14 +02:00
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Modul-Hilfsfunktionen
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|