try2 und try3 laden das Verfahrensgebiet mit Detailprüfung anstelle der Flurstücke vom wfs
This commit is contained in:
+123
-121
@@ -7,7 +7,7 @@ Schwesterplugins für andere Bundesländer dieselbe Logik mit einer anderen
|
||||
Länderkennung wiederverwenden können.
|
||||
|
||||
Gleicht eine CSV-Exportliste des BI-Servers (Kopfzeile
|
||||
``FLA_VEF_VKZF,FLA_Beteiligung,FLURSTUECK``) mit dem Verfahrensgebiet-Bestand
|
||||
``VEF_VKZF,FLA_Beteiligung,FLURSTUECK``) mit dem Verfahrensgebiet-Bestand
|
||||
ab. Aus ``FLURSTUECK`` (Format ``<Gemarkung>/<Zähler>/<Nenner>/<Folge>``) wird
|
||||
je Zeile ein zu ALKIS kompatibles Flurstückskennzeichen ``Flurstueck_csv``
|
||||
gebildet.
|
||||
@@ -19,10 +19,12 @@ Automatische Kaskade je nach Projektzustand:
|
||||
:mod:`verfahrensgebiet_edit_service`) ist vorhanden → schneller
|
||||
Mengenvergleich ohne WFS-Zugriff.
|
||||
- **try2**: Ein Verfahrensgebiet-Layer ist vorhanden, aber ohne verwertbare
|
||||
Kennzeichen → je CSV-Zeile eine WFS-Einzelabfrage gegen ``AX_Flurstueck``.
|
||||
Kennzeichen → die Pipeline "Verfahrensgebiet aus ALKIS laden"
|
||||
(:func:`verfahrensgebiet_alkis_komplett`) wird silent (ohne
|
||||
Nutzerrückfragen, aber inkl. Detailprüfung) durchlaufen, um einen
|
||||
vollständigen, aktuellen Bestand zu erzeugen – danach wie try1.
|
||||
- **try3**: Kein Verfahrensgebiet-Layer im Projekt → Verfahrensnummer aus
|
||||
``VEF_VKZF`` als Projektvariable setzen, Verfahrensgebiet analog zur
|
||||
bestehenden ALKIS-Pipeline laden (ohne Detailprüfung), danach wie try2.
|
||||
``VEF_VKZF`` als Projektvariable setzen, danach wie try2.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -32,28 +34,29 @@ from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from sn_basis.functions import QgsVectorLayer, QgsProject, QgsFeature, QgsField
|
||||
from sn_basis.functions import QMessageBox
|
||||
from sn_basis.functions.qt_wrapper import QFileDialog, QVariant
|
||||
from sn_basis.functions.variable_wrapper import set_variable
|
||||
from sn_basis.functions.laenderkennung import get_laenderkennung
|
||||
from sn_basis.functions.dialog_wrapper import create_progress_dialog
|
||||
from sn_basis.functions.ly_style_wrapper import apply_style
|
||||
|
||||
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import verfahrensgebiet_alkis
|
||||
from sn_verfahrensgebiet.functions.flurstueck_alkis import ALKIS_SF_URL, FLST_TYPENAME, FLST_LAYER_NAME
|
||||
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import (
|
||||
verfahrensgebiet_alkis_komplett,
|
||||
)
|
||||
from sn_verfahrensgebiet.functions.flurstueck_alkis import FLST_LAYER_NAME
|
||||
from sn_verfahrensgebiet.functions.verfahrensgebiet_edit_service import (
|
||||
VERF_LAYER_NAME,
|
||||
get_kennzeichen_menge,
|
||||
)
|
||||
|
||||
CSV_SPALTE_FLURSTUECK = "FLURSTUECK"
|
||||
CSV_SPALTE_VEF_VKZF = "FLA_VEF_VKZF"
|
||||
CSV_SPALTE_VEF_VKZF = "VEF_VKZF"
|
||||
CSV_SPALTE_BETEILIGUNG = "FLA_Beteiligung"
|
||||
|
||||
FELD_FLURSTUECK_CSV = "Flurstueck_csv"
|
||||
FELD_FLURSTUECKSKENNZEICHEN_WFS = "flurstueckskennzeichen"
|
||||
|
||||
ERGEBNIS_LAYER_VERGLEICH = "Beteiligung – Vergleich"
|
||||
ERGEBNIS_LAYER_ERFOLGREICH = "Beteiligung – gefunden"
|
||||
ERGEBNIS_LAYER_NICHT_GEFUNDEN = "Beteiligung – nicht gefunden"
|
||||
|
||||
WERT_BEIDE = "beide"
|
||||
WERT_NUR_VERFAHRENSGEBIET = "nur_verfahrensgebiet"
|
||||
@@ -127,6 +130,38 @@ def lade_beteiligung_csv(parent_widget) -> Optional[Tuple[List[dict], List[str]]
|
||||
# Flurstueck_csv-Bildung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
#: Alte CSV-Exporte führen Buchstaben-Nenner (ALKIS: a, b, c, ...) teils als
|
||||
#: numerischen Platzhalter 9001, 9002, ... statt als Buchstabe. 9001 → "a",
|
||||
#: 9002 → "b" usw. (Basiswert, auf den 1..26 für a..z addiert wird).
|
||||
_NENNER_BUCHSTABEN_BASIS = 9000
|
||||
|
||||
|
||||
def _baue_nenner_kennzeichen(nenner: str) -> str:
|
||||
"""Bildet den 4-stelligen Nenner-Teil des Flurstückskennzeichens.
|
||||
|
||||
Normalfall: numerischer Nenner, 4-stellig mit führenden Nullen
|
||||
aufgefüllt; Nenner 0/leer → Platzhalter ``"____"``.
|
||||
|
||||
Format-Inkonsistenz alter CSV-Exporte: ein Buchstaben-Nenner (z.B. "a")
|
||||
wird dort als ``9001`` (a), ``9002`` (b), usw. geführt. Wird ein solcher
|
||||
Platzhalterwert erkannt, wird stattdessen der Buchstabe selbst
|
||||
(4-stellig mit führenden Nullen aufgefüllt, z.B. ``"000a"``) verwendet,
|
||||
damit das Kennzeichen mit dem ALKIS-Bestand übereinstimmt.
|
||||
"""
|
||||
nenner = (nenner or "").strip()
|
||||
nenner_ohne_null = nenner.lstrip("0")
|
||||
if not nenner_ohne_null:
|
||||
return "____"
|
||||
|
||||
if nenner_ohne_null.isdigit():
|
||||
zahl = int(nenner_ohne_null)
|
||||
if _NENNER_BUCHSTABEN_BASIS < zahl <= _NENNER_BUCHSTABEN_BASIS + 26:
|
||||
buchstabe = chr(ord("a") + (zahl - _NENNER_BUCHSTABEN_BASIS - 1))
|
||||
return buchstabe.zfill(4)
|
||||
|
||||
return nenner.zfill(4)
|
||||
|
||||
|
||||
def baue_flurstueck_csv(flurstueck_wert: str, laenderkennung: str) -> Optional[str]:
|
||||
"""Bildet aus dem CSV-Attribut ``FLURSTUECK`` ein ALKIS-Flurstückskennzeichen.
|
||||
|
||||
@@ -154,11 +189,10 @@ def baue_flurstueck_csv(flurstueck_wert: str, laenderkennung: str) -> Optional[s
|
||||
gemarkung_kz = gemarkung.zfill(4)
|
||||
flur_kz = "___"
|
||||
zaehler_kz = zaehler.zfill(5)
|
||||
nenner_kz = _baue_nenner_kennzeichen(nenner)
|
||||
folge_kz = folge.zfill(2)
|
||||
|
||||
nenner_ohne_null = nenner.lstrip("0")
|
||||
nenner_kz = "____" if not nenner_ohne_null else nenner.zfill(4)
|
||||
|
||||
return f"{laenderkennung}{gemarkung_kz}{flur_kz}{zaehler_kz}{nenner_kz}{folge}"
|
||||
return f"{laenderkennung}{gemarkung_kz}{flur_kz}{zaehler_kz}{nenner_kz}{folge_kz}"
|
||||
|
||||
|
||||
def baue_flurstueck_csv_liste(zeilen: List[dict], laenderkennung: str) -> List[str]:
|
||||
@@ -231,14 +265,25 @@ def _flurstueckskennzeichen_geometrien(flst_layer) -> Dict[str, object]:
|
||||
# try1: schneller Listenabgleich
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _try1_listenabgleich(verf_layer, flurstueck_csv_liste: List[str]) -> Tuple[object, Dict[str, int]]:
|
||||
def _try1_listenabgleich(
|
||||
tab_widget, verf_layer, flurstueck_csv_liste: List[str]
|
||||
) -> Tuple[object, Dict[str, int]]:
|
||||
progress = create_progress_dialog(
|
||||
3, "Beteiligung prüfen", "Verfahrensgebiet-Kennzeichen einlesen …"
|
||||
)
|
||||
progress.set_value(0)
|
||||
|
||||
verf_menge = get_kennzeichen_menge(verf_layer)
|
||||
csv_menge = set(flurstueck_csv_liste)
|
||||
progress.set_value(1)
|
||||
|
||||
progress.set_label("CSV-Liste abgleichen …")
|
||||
beide = sorted(verf_menge & csv_menge)
|
||||
nur_verfahrensgebiet = sorted(verf_menge - csv_menge)
|
||||
nur_aglb = sorted(csv_menge - verf_menge)
|
||||
progress.set_value(2)
|
||||
|
||||
progress.set_label("Ergebnislayer erstellen …")
|
||||
flst_layer = _hole_flurstuecke_layer()
|
||||
geom_map = _flurstueckskennzeichen_geometrien(flst_layer)
|
||||
if flst_layer is not None and flst_layer.isValid():
|
||||
@@ -274,6 +319,9 @@ def _try1_listenabgleich(verf_layer, flurstueck_csv_liste: List[str]) -> Tuple[o
|
||||
pr.addFeatures(neue_features)
|
||||
layer.updateExtents()
|
||||
|
||||
progress.set_value(3)
|
||||
progress.close()
|
||||
|
||||
statistik = {
|
||||
WERT_BEIDE: len(beide),
|
||||
WERT_NUR_VERFAHRENSGEBIET: len(nur_verfahrensgebiet),
|
||||
@@ -283,87 +331,68 @@ def _try1_listenabgleich(verf_layer, flurstueck_csv_liste: List[str]) -> Tuple[o
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# try2: WFS-Einzelabgleich
|
||||
# try2/try3: ALKIS-Pipeline (silent) + Listenabgleich wie try1
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _try2_wfs_einzelabgleich(
|
||||
flurstueck_csv_liste: List[str],
|
||||
) -> Tuple[Optional[object], object, Dict[str, int]]:
|
||||
erfolgreich: List[tuple] = []
|
||||
nicht_gefunden: List[str] = []
|
||||
schema_fields = None
|
||||
crs = "EPSG:25833"
|
||||
dienst_erreichbar = False
|
||||
def _lade_via_alkis_pipeline_und_vergleiche(
|
||||
tab_widget, flurstueck_csv_liste: List[str]
|
||||
) -> BeteiligungErgebnis:
|
||||
"""Lädt das Verfahrensgebiet silent per ALKIS-Pipeline (inkl. Detailprüfung)
|
||||
und gleicht den Bestand danach wie in try1 gegen die CSV-Liste ab.
|
||||
|
||||
for kz in flurstueck_csv_liste:
|
||||
uri = (
|
||||
f"url='{ALKIS_SF_URL}' typename='{FLST_TYPENAME}' srsname='EPSG:25833' "
|
||||
f"sql=SELECT * FROM AX_Flurstueck "
|
||||
f"WHERE {FELD_FLURSTUECKSKENNZEICHEN_WFS} = '{kz}'"
|
||||
)
|
||||
wfs = QgsVectorLayer(uri, f"_TmpBet{kz}", "WFS")
|
||||
if not wfs.isValid():
|
||||
continue
|
||||
dienst_erreichbar = True
|
||||
|
||||
feats = list(wfs.getFeatures())
|
||||
if not feats:
|
||||
nicht_gefunden.append(kz)
|
||||
continue
|
||||
|
||||
if schema_fields is None:
|
||||
schema_fields = wfs.fields()
|
||||
crs = wfs.crs().authid()
|
||||
erfolgreich.append((wfs.fields(), feats[0]))
|
||||
|
||||
if not dienst_erreichbar:
|
||||
raise BeteiligungPruefungFehler(
|
||||
f"Dienst nicht erreichbar – ALKIS-WFS ({ALKIS_SF_URL}) konnte nicht "
|
||||
f"abgefragt werden. Bitte Netzwerkverbindung prüfen."
|
||||
)
|
||||
|
||||
erfolg_layer = None
|
||||
if schema_fields is not None:
|
||||
erfolg_layer = QgsVectorLayer(f"Polygon?crs={crs}", ERGEBNIS_LAYER_ERFOLGREICH, "memory")
|
||||
pr = erfolg_layer.dataProvider()
|
||||
pr.addAttributes(list(schema_fields))
|
||||
erfolg_layer.updateFields()
|
||||
mem_fields = erfolg_layer.fields()
|
||||
|
||||
neue_features = []
|
||||
for wfs_fields, feat in erfolgreich:
|
||||
new_f = QgsFeature(mem_fields)
|
||||
new_f.setGeometry(feat.geometry())
|
||||
for i in range(wfs_fields.count()):
|
||||
fname = wfs_fields.field(i).name()
|
||||
idx = mem_fields.indexFromName(fname)
|
||||
if idx >= 0:
|
||||
try:
|
||||
new_f.setAttribute(idx, feat[fname])
|
||||
except Exception:
|
||||
pass
|
||||
neue_features.append(new_f)
|
||||
pr.addFeatures(neue_features)
|
||||
erfolg_layer.updateExtents()
|
||||
|
||||
ng_layer = QgsVectorLayer(
|
||||
f"None?field={FELD_FLURSTUECK_CSV}:string", ERGEBNIS_LAYER_NICHT_GEFUNDEN, "memory"
|
||||
Raises
|
||||
------
|
||||
BeteiligungPruefungFehler
|
||||
Wenn die ALKIS-Pipeline kein Verfahrensgebiet liefert (Pipeline hat
|
||||
bereits eine erklärende Messagebox gezeigt).
|
||||
"""
|
||||
enriched_layer, flst_layer, bror_layer, status = verfahrensgebiet_alkis_komplett(
|
||||
tab_widget, silent=True
|
||||
)
|
||||
pr = ng_layer.dataProvider()
|
||||
ng_fields = ng_layer.fields()
|
||||
neue_ng = []
|
||||
for kz in nicht_gefunden:
|
||||
f = QgsFeature(ng_fields)
|
||||
f.setAttribute(0, kz)
|
||||
neue_ng.append(f)
|
||||
pr.addFeatures(neue_ng)
|
||||
if enriched_layer is None:
|
||||
raise BeteiligungPruefungFehler(
|
||||
"Verfahrensgebiet konnte über die ALKIS-Pipeline nicht geladen werden."
|
||||
)
|
||||
|
||||
statistik = {"erfolgreich": len(erfolgreich), "nicht_gefunden": len(nicht_gefunden)}
|
||||
return erfolg_layer, ng_layer, statistik
|
||||
project = QgsProject.instance()
|
||||
|
||||
if flst_layer is not None and flst_layer.isValid():
|
||||
if not project.mapLayersByName(flst_layer.name()):
|
||||
project.addMapLayer(flst_layer)
|
||||
apply_style(flst_layer, "GIS_Flst_Beschriftung_ALKIS_NAS.qml")
|
||||
|
||||
if bror_layer is not None and bror_layer.isValid():
|
||||
if not project.mapLayer(bror_layer.id()):
|
||||
project.addMapLayer(bror_layer)
|
||||
apply_style(bror_layer, "BauRaumOderBodenordnungsrecht.qml")
|
||||
|
||||
if not project.mapLayer(enriched_layer.id()):
|
||||
project.addMapLayer(enriched_layer)
|
||||
apply_style(enriched_layer, "verfahrensgebiet.qml")
|
||||
|
||||
vergleich_layer, statistik = _try1_listenabgleich(
|
||||
tab_widget, enriched_layer, flurstueck_csv_liste
|
||||
)
|
||||
|
||||
layers: List[Tuple[object, Optional[str]]] = [
|
||||
(enriched_layer, None),
|
||||
(vergleich_layer, "beteiligung_vergleich.qml"),
|
||||
]
|
||||
if flst_layer is not None:
|
||||
layers.append((flst_layer, None))
|
||||
if bror_layer is not None:
|
||||
layers.append((bror_layer, None))
|
||||
|
||||
text = (
|
||||
f"Übereinstimmend: {statistik[WERT_BEIDE]}\n"
|
||||
f"Nur im Verfahrensgebiet: {statistik[WERT_NUR_VERFAHRENSGEBIET]}\n"
|
||||
f"Nur in der CSV-Liste (AGLB): {statistik[WERT_NUR_AGLB]}"
|
||||
)
|
||||
return BeteiligungErgebnis("try2", layers, statistik, text)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# try3: Bootstrap (Verfahrensnummer aus CSV, VG laden) + try2
|
||||
# try3: Bootstrap (Verfahrensnummer aus CSV) + wie try2
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _lese_vef_vkzf(zeilen: List[dict], spaltennamen: List[str]) -> str:
|
||||
@@ -386,28 +415,10 @@ def _try3_bootstrap_und_try2(
|
||||
vef_vkzf = _lese_vef_vkzf(zeilen, spaltennamen)
|
||||
set_variable("verfahrensnummer", vef_vkzf, scope="project")
|
||||
|
||||
# Identische BROR-Ladung wie die bestehende ALKIS-Pipeline, ohne die
|
||||
# nachgelagerte Detailprüfung/Flächenvergleich aus verfahrensgebiet_alkis_komplett.
|
||||
neuer_vg_layer, status, _feats = verfahrensgebiet_alkis(tab_widget, skip_existing_check=True)
|
||||
if neuer_vg_layer is None:
|
||||
# verfahrensgebiet_alkis() hat bereits eine erklärende Messagebox gezeigt.
|
||||
raise BeteiligungPruefungFehler(
|
||||
f"Verfahrensgebiet konnte für Verfahrensnummer '{vef_vkzf}' nicht geladen werden."
|
||||
)
|
||||
|
||||
erfolg_layer, ng_layer, statistik = _try2_wfs_einzelabgleich(flurstueck_csv_liste)
|
||||
|
||||
layers: List[Tuple[object, Optional[str]]] = [(neuer_vg_layer, "verfahrensgebiet.qml")]
|
||||
if erfolg_layer is not None:
|
||||
layers.append((erfolg_layer, "beteiligung_erfolgreich.qml"))
|
||||
layers.append((ng_layer, None))
|
||||
|
||||
text = (
|
||||
f"Verfahrensnummer aus CSV: {vef_vkzf}\n"
|
||||
f"Erfolgreich geladen: {statistik['erfolgreich']}\n"
|
||||
f"Nicht gefunden: {statistik['nicht_gefunden']}"
|
||||
)
|
||||
return BeteiligungErgebnis("try3", layers, statistik, text)
|
||||
ergebnis = _lade_via_alkis_pipeline_und_vergleiche(tab_widget, flurstueck_csv_liste)
|
||||
ergebnis.modus = "try3"
|
||||
ergebnis.statistik_text = f"Verfahrensnummer aus CSV: {vef_vkzf}\n" + ergebnis.statistik_text
|
||||
return ergebnis
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -446,7 +457,7 @@ def beteiligung_pruefen(tab_widget) -> Optional[BeteiligungErgebnis]:
|
||||
verf_layer = _hole_verfahrensgebiet_layer(tab_widget)
|
||||
|
||||
if verf_layer is not None and _hat_verwendbare_kennzeichen(verf_layer):
|
||||
layer, statistik = _try1_listenabgleich(verf_layer, flurstueck_csv_liste)
|
||||
layer, statistik = _try1_listenabgleich(tab_widget, verf_layer, flurstueck_csv_liste)
|
||||
text = (
|
||||
f"Übereinstimmend: {statistik[WERT_BEIDE]}\n"
|
||||
f"Nur im Verfahrensgebiet: {statistik[WERT_NUR_VERFAHRENSGEBIET]}\n"
|
||||
@@ -455,15 +466,6 @@ def beteiligung_pruefen(tab_widget) -> Optional[BeteiligungErgebnis]:
|
||||
return BeteiligungErgebnis("try1", [(layer, "beteiligung_vergleich.qml")], statistik, text)
|
||||
|
||||
if verf_layer is not None:
|
||||
erfolg_layer, ng_layer, statistik = _try2_wfs_einzelabgleich(flurstueck_csv_liste)
|
||||
layers: List[Tuple[object, Optional[str]]] = []
|
||||
if erfolg_layer is not None:
|
||||
layers.append((erfolg_layer, "beteiligung_erfolgreich.qml"))
|
||||
layers.append((ng_layer, None))
|
||||
text = (
|
||||
f"Erfolgreich geladen: {statistik['erfolgreich']}\n"
|
||||
f"Nicht gefunden: {statistik['nicht_gefunden']}"
|
||||
)
|
||||
return BeteiligungErgebnis("try2", layers, statistik, text)
|
||||
return _lade_via_alkis_pipeline_und_vergleiche(tab_widget, flurstueck_csv_liste)
|
||||
|
||||
return _try3_bootstrap_und_try2(tab_widget, zeilen, spaltennamen, flurstueck_csv_liste)
|
||||
|
||||
Reference in New Issue
Block a user