diff --git a/changelog.txt b/changelog.txt
index df8d39d..6d17ae6 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -2,6 +2,11 @@
- Fortschrittsanzeige beim Laden aus ALKIS in zwei Dialoge aufgeteilt (Suche / eigentlicher Ladevorgang), inkl. eigenem Suchschritt
- Mehrfachauswahl bei mehreren BauRaumOderBodenordnungsrecht-Treffern zur Verfahrensnummer (gemeinsames Laden/Verschmelzen mehrerer Objekte)
- Neu: Laden aller/mehrerer Objekte eines ganzen Landkreises als Fallback, wenn keine Verfahrensnummer gesetzt ist (Gruppierung je Verfahren, optional "Nur laden" ohne weiteren Ablauf)
+- Neu: Ist weder Verfahrensnummer noch Landkreis in den Projekteigenschaften gesetzt, fragt ein Dialog den Landkreis ab (inkl. Option "Alle Landkreise"), statt direkt abzubrechen
+- Landkreisweiter Objekt-Auswahldialog überarbeitet: Tabelle mit sortierbaren Spalten "Nummer"/"Name" (Standard: aufsteigend nach Nummer), Suchfeld, "Alle auswählen" jetzt korrekt (vorheriger Qt-Bug bei programmatischer Mehrfachauswahl behoben)
+- Bugfix: BROR-Layer bekommt seinen ALKIS-Style jetzt direkt beim Laden (auch bei "Nur Objekte laden") statt später fälschlich den Verfahrensgebiet-Style zu erhalten – **Detailprüfung/Verifikation im echten QGIS-Test steht noch aus**
+- Bugfix: Erfolgsmeldung "Layer wurde ... geladen (inkl. Flurstücksverschneidung)" erscheint nicht mehr fälschlich bei "Nur Objekte laden" (da dort keine Verschneidung stattfindet)
+- Verfahrensgebiet-spezifische ALKIS-Dialoge aus sn_basis nach sn_verfahrensgebiet verschoben (functions/verfahrensgebiet_dialoge.py)
---
Version 26.7.1-unstable:
- Verfahrensgebiet als shape laden, inkl. Abgleich mit BROBOR-Objekt
diff --git a/functions/verfahrensgebiet_alkis.py b/functions/verfahrensgebiet_alkis.py
index a58332c..1ba8d9d 100644
--- a/functions/verfahrensgebiet_alkis.py
+++ b/functions/verfahrensgebiet_alkis.py
@@ -9,7 +9,8 @@ from sn_basis.functions import (
from sn_basis.functions import QMessageBox
from sn_basis.functions.qt_wrapper import QVariant
from sn_basis.functions.variable_wrapper import get_variable
-from sn_basis.functions.dialog_wrapper import ask_bror_kombinationen, ask_landkreis_objekte
+from sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge import ask_bror_kombinationen, ask_landkreis_objekte
+from sn_basis.functions.landkreise import ALLE_LANDKREISE
alkis_sf_url = "https://geodienste.sachsen.de/aaa/public_alkis/sf/wfs"
typename = "adv:AX_BauRaumOderBodenordnungsrecht"
@@ -219,10 +220,14 @@ def _suche_landkreis_kombinationen(tab_widget, landkreis_kennzahl: str, skip_exi
if status == LoadStatus.KEEP:
return existing, status, {}, None
+ # "Alle Landkreise" (Sentinel) -> kein Kennzahl-Präfix, LIKE '%' matcht alles.
+ praefix = "" if landkreis_kennzahl == ALLE_LANDKREISE else landkreis_kennzahl
+ landkreis_anzeige = "allen Landkreisen" if landkreis_kennzahl == ALLE_LANDKREISE else f"Landkreis {landkreis_kennzahl}"
+
wfs_uri_find = (
f"url='{alkis_sf_url}' typename='{typename}' srsname='EPSG:25833' "
f"sql=SELECT * FROM AX_BauRaumOderBodenordnungsrecht "
- f"WHERE bezeichnung LIKE '{landkreis_kennzahl}%'"
+ f"WHERE bezeichnung LIKE '{praefix}%'"
)
temp_layer = QgsVectorLayer(wfs_uri_find, "TempVerfahrensgebiet", "WFS")
@@ -233,7 +238,7 @@ def _suche_landkreis_kombinationen(tab_widget, landkreis_kennzahl: str, skip_exi
features = list(temp_layer.getFeatures())
if not features:
QMessageBox.critical(tab_widget, "Fehler",
- f"Für Landkreis {landkreis_kennzahl} wurden keine Objekte im WFS gefunden.")
+ f"Für {landkreis_anzeige} wurden keine Objekte im WFS gefunden.")
return None, LoadStatus.NONE, {}, None
kombinationen: dict[tuple[str, str], list] = {}
@@ -255,7 +260,7 @@ def _suche_landkreis_kombinationen(tab_widget, landkreis_kennzahl: str, skip_exi
def _baue_layer_aus_landkreis_auswahl(tab_widget, kombinationen: dict, crs: str, status: "LoadStatus"):
"""Auswahl + Bau für den Landkreis-Fallback.
- Zeigt :func:`~sn_basis.functions.dialog_wrapper.ask_landkreis_objekte`
+ Zeigt :func:`~sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge.ask_landkreis_objekte`
(Mehrfachauswahl inkl. "Alle auswählen" und "Nur laden"-Checkbox). Die
ausgewählten Kombinationen werden anhand der ersten 5 Zeichen von
``bezeichnung`` (= die eigentliche VKZ) gruppiert; je Gruppe wird EIN
diff --git a/functions/verfahrensgebiet_alkis_komplett.py b/functions/verfahrensgebiet_alkis_komplett.py
index 8e329c8..880dbfb 100644
--- a/functions/verfahrensgebiet_alkis_komplett.py
+++ b/functions/verfahrensgebiet_alkis_komplett.py
@@ -52,6 +52,9 @@ from sn_basis.functions.dialog_wrapper import (
show_info_dialog,
ask_detailpruefung_oder_vg_laden,
)
+from sn_basis.functions.landkreise import LANDKREISE, ALLE_LANDKREISE
+from sn_basis.functions.ly_style_wrapper import apply_style
+from sn_verfahrensgebiet.functions.verfahrensgebiet_dialoge import ask_landkreis_auswahl
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import (
_suche_verfahrensgebiet_features,
@@ -79,6 +82,8 @@ from sn_verfahrensgebiet.functions.knickpunkt_pruefung import (
FELD_VKZ = "verfahrensnummer"
#: Anzeigename des BROR-Layers im Projekt
BROR_LAYER_NAME = "BauRaumOderBodenordnungsrecht (ALKIS)"
+#: QML-Stildatei für den BROR-Layer (sn_verfahrensgebiet/styles/)
+BROR_STYLE_QML = "BauRaumOderBodenordnungsrecht.qml"
#: Projektvariable für die Flächentoleranz (in m²); Default 0
FLAECHEN_TOLERANZ_VARIABLE = "flaechen_toleranz_m2"
#: Minimale implizite Flächentoleranz – Abweichungen unter diesem Wert
@@ -309,13 +314,17 @@ def verfahrensgebiet_alkis_komplett(
landkreis_kennzahl = get_variable("landkreis_kennzahl")
if not verfahrensnummer and not landkreis_kennzahl:
- QMessageBox.critical(
- tab_widget,
- "Fehler",
- "Weder eine Verfahrensnummer noch ein Landkreis ist in den "
- "Projekteigenschaften eingetragen!",
- )
- return None, None, None, LoadStatus.NONE
+ # Keine Projekteigenschaften vorhanden – Landkreis per Dialog abfragen,
+ # statt direkt abzubrechen.
+ landkreis_kennzahl = ask_landkreis_auswahl(tab_widget, LANDKREISE)
+ if not landkreis_kennzahl:
+ QMessageBox.critical(
+ tab_widget,
+ "Fehler",
+ "Weder eine Verfahrensnummer noch ein Landkreis ist in den "
+ "Projekteigenschaften eingetragen!",
+ )
+ return None, None, None, LoadStatus.NONE
landkreis_modus = not verfahrensnummer
@@ -323,7 +332,10 @@ def verfahrensgebiet_alkis_komplett(
# Dialog 1: Verfahrensgebiet(e) in ALKIS ermitteln/suchen
# ------------------------------------------------------------------
if landkreis_modus:
- such_label = f"Objekte in Landkreis {landkreis_kennzahl} ermitteln/suchen …"
+ if landkreis_kennzahl == ALLE_LANDKREISE:
+ such_label = "Objekte in allen Landkreisen ermitteln/suchen …"
+ else:
+ such_label = f"Objekte in Landkreis {landkreis_kennzahl} ermitteln/suchen …"
else:
such_label = "Verfahrensgebiet(e) in ALKIS ermitteln/suchen …"
@@ -378,6 +390,9 @@ def verfahrensgebiet_alkis_komplett(
if vg_status in (LoadStatus.FIRST, LoadStatus.RELOAD):
project.addMapLayer(alkis_vg_layer)
bror_was_added_by_us = True
+ # Style direkt nach dem Laden anwenden – unabhängig vom weiteren
+ # Ablauf, damit auch "Nur Objekte laden" den Style erhält.
+ apply_style(alkis_vg_layer, BROR_STYLE_QML)
if nur_laden:
# Nutzer hat "Nur laden" angehakt – kein Dialog 2, kein weiterer
diff --git a/functions/verfahrensgebiet_dialoge.py b/functions/verfahrensgebiet_dialoge.py
new file mode 100644
index 0000000..d4c6eef
--- /dev/null
+++ b/functions/verfahrensgebiet_dialoge.py
@@ -0,0 +1,299 @@
+"""sn_verfahrensgebiet/functions/verfahrensgebiet_dialoge.py – Dialoge zum
+Laden eines Verfahrensgebiets aus ALKIS (Qt5/6/Mock-kompatibel).
+"""
+from typing import Any, Optional
+from sn_basis.functions.qt_wrapper import (
+ QT_VERSION, exec_dialog, QInputDialog, QLineEdit,
+ QDialog, QDialogButtonBox, QListWidget, QListWidgetItem, QAbstractItemView,
+ QTableWidget, QTableWidgetItem, QTableWidgetSelectionRange,
+ QIcon, QVBoxLayout, QHBoxLayout, QLabel, QCheckBox,
+ DialogAccepted, ExtendedSelection, SelectRows, NoEditTriggers, AscendingOrder,
+)
+from sn_basis.functions.landkreise import ALLE_LANDKREISE
+import os
+
+# .../python/plugins (drei Ebenen über dieser Datei)
+_PLUGINS_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+_WAPPEN_ICON = os.path.join(_PLUGINS_DIR, "sn_basis", "favicon.ico")
+
+
+def ask_bror_kombinationen(
+ parent: Any,
+ kombinationen: list[tuple[str, str]],
+) -> list[tuple[str, str]]:
+ """Zeigt Dialog zur Auswahl einer oder mehrerer BROR-Kombinationen
+ (adv_name + bezeichnung).
+
+ Wird aufgerufen, wenn der WFS mehrere verschiedene Kombinationen aus
+ ``adv_name`` und ``bezeichnung`` zurückliefert. Es können ein oder
+ mehrere Objekte gemeinsam ausgewählt und geladen (verschmolzen) werden.
+
+ Parameters
+ ----------
+ parent :
+ Eltern-Widget oder None.
+ kombinationen :
+ Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
+
+ Returns
+ -------
+ list[tuple[str, str]]
+ Die gewählten ``(adv_name, bezeichnung)``-Tupel. Leere Liste bei
+ Abbruch oder wenn nichts ausgewählt wurde.
+ """
+ if not kombinationen:
+ return []
+
+ if QT_VERSION == 0: # Mock-Modus
+ print(f"Mock-Modus: ask_bror_kombinationen → {kombinationen[:1]}")
+ return kombinationen[:1]
+
+ dialog = QDialog(parent)
+ dialog.setWindowTitle("Verfahrensgebiet aus ALKIS laden")
+ if os.path.exists(_WAPPEN_ICON):
+ dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
+ layout = QVBoxLayout(dialog)
+
+ hinweis = QLabel(
+ f"Es wurden {len(kombinationen)} Objekte gefunden.\n\n"
+ "Bitte wählen Sie eines oder mehrere Objekte aus:"
+ )
+ hinweis.setWordWrap(True)
+ layout.addWidget(hinweis)
+
+ list_widget = QListWidget()
+ list_widget.setSelectionMode(ExtendedSelection)
+ for adv, bez in kombinationen:
+ item = QListWidgetItem(f"{adv} | {bez}")
+ list_widget.addItem(item)
+ list_widget.item(0).setSelected(True)
+ layout.addWidget(list_widget, 1) # Stretch-Faktor: wächst mit Fenstergröße
+
+ mehrfachauswahl_hinweis = QLabel("Mehrfachauswahl mit Strg + Umschalt")
+ layout.addWidget(mehrfachauswahl_hinweis)
+
+ buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
+ buttons.accepted.connect(dialog.accept)
+ buttons.rejected.connect(dialog.reject)
+ layout.addWidget(buttons)
+
+ dialog.adjustSize()
+ breite = dialog.sizeHint().width()
+
+ # Liste auf max. 5 sichtbare Zeilen begrenzen, Rest per Scrollbar
+ sichtbare_zeilen = min(len(kombinationen), 5)
+ zeilenhoehe = list_widget.sizeHintForRow(0) if list_widget.count() else 20
+ # + Puffer für eine ggf. eingeblendete horizontale Scrollbar (bei langen
+ # Einträgen), damit diese nicht die letzte Zeile abschneidet.
+ scrollbar_puffer = list_widget.horizontalScrollBar().sizeHint().height()
+ list_hoehe = zeilenhoehe * sichtbare_zeilen + 2 * list_widget.frameWidth() + 4 + scrollbar_puffer
+ list_widget.setMinimumHeight(list_hoehe)
+
+ dialog.resize(breite, dialog.sizeHint().height())
+
+ if exec_dialog(dialog) != DialogAccepted:
+ return []
+
+ ausgewaehlte_indizes = sorted(list_widget.row(item) for item in list_widget.selectedItems())
+ return [kombinationen[i] for i in ausgewaehlte_indizes]
+
+
+def ask_landkreis_objekte(
+ parent: Any,
+ kombinationen: list[tuple[str, str]],
+) -> tuple[list[tuple[str, str]], bool]:
+ """Zeigt Dialog zur Auswahl eines, mehrerer oder aller BROR-Objekte eines
+ Landkreises (Fallback, wenn keine Verfahrensnummer gesetzt ist).
+
+ Wie :func:`ask_bror_kombinationen`, zusätzlich mit einer "Alle
+ auswählen"-Checkbox (da hier potenziell deutlich mehr Treffer als bei der
+ VKZ-Suche auftreten) und einer "Nur laden"-Checkbox, mit der gesteuert
+ wird, ob nach dem Laden der weitere Workflow (Gemarkung/Flurstücke/
+ Flächenvergleich) fortgesetzt werden soll.
+
+ Parameters
+ ----------
+ parent :
+ Eltern-Widget oder None.
+ kombinationen :
+ Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
+
+ Returns
+ -------
+ tuple[list[tuple[str, str]], bool]
+ ``(ausgewaehlte_kombinationen, nur_laden)``. ``([], False)`` bei
+ Abbruch oder wenn nichts ausgewählt wurde.
+ """
+ if not kombinationen:
+ return [], False
+
+ if QT_VERSION == 0: # Mock-Modus
+ print(f"Mock-Modus: ask_landkreis_objekte → {kombinationen[:1]}")
+ return kombinationen[:1], False
+
+ dialog = QDialog(parent)
+ dialog.setWindowTitle("Verfahrensgebiete aus Landkreis laden")
+ if os.path.exists(_WAPPEN_ICON):
+ dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
+ layout = QVBoxLayout(dialog)
+
+ hinweis = QLabel(
+ f"Es wurden {len(kombinationen)} Objekte gefunden.\n\n"
+ "Bitte wählen Sie eines oder mehrere Objekte aus:"
+ )
+ hinweis.setWordWrap(True)
+ layout.addWidget(hinweis)
+
+ suche_zeile = QHBoxLayout()
+ suche_zeile.addWidget(QLabel("Suche:"))
+ suche_feld = QLineEdit()
+ suche_feld.setPlaceholderText("Nummer oder Name …")
+ suche_zeile.addWidget(suche_feld)
+ layout.addLayout(suche_zeile)
+
+ alle_auswaehlen = QCheckBox("Alle auswählen")
+ layout.addWidget(alle_auswaehlen)
+
+ # Standardsortierung nach Bezeichnung; per Klick auf Spaltenkopf änderbar
+ # (setSortingEnabled aktiviert die eingebaute Tabellensortierung).
+ sortierte_kombinationen = sorted(kombinationen, key=lambda kb: kb[1])
+
+ table = QTableWidget(len(sortierte_kombinationen), 2)
+ table.setHorizontalHeaderLabels(["Nummer", "Name"])
+ table.setSelectionMode(ExtendedSelection)
+ table.setSelectionBehavior(SelectRows)
+ table.setEditTriggers(NoEditTriggers)
+ table.verticalHeader().setVisible(False)
+ table.verticalHeader().setDefaultSectionSize(22) # flachere Zeilen
+ table.horizontalHeader().setStretchLastSection(True)
+ table.setSortingEnabled(False)
+ for row, (adv, bez) in enumerate(sortierte_kombinationen):
+ table.setItem(row, 0, QTableWidgetItem(bez))
+ table.setItem(row, 1, QTableWidgetItem(adv))
+ table.setSortingEnabled(True)
+ table.sortByColumn(0, AscendingOrder) # Standard: aufsteigend nach Nummer
+ table.resizeColumnToContents(0)
+ table.setColumnWidth(0, max(110, table.columnWidth(0)))
+ table.selectRow(0)
+ layout.addWidget(table, 1) # Stretch-Faktor: wächst mit Fenstergröße
+
+ def _suche_geaendert(text):
+ text_lower = text.strip().lower()
+ for row in range(table.rowCount()):
+ bez_text = table.item(row, 0).text().lower()
+ adv_text = table.item(row, 1).text().lower()
+ treffer = not text_lower or text_lower in bez_text or text_lower in adv_text
+ table.setRowHidden(row, not treffer)
+
+ suche_feld.textChanged.connect(_suche_geaendert)
+
+ def _alle_auswaehlen_umschalten(checked):
+ table.clearSelection()
+ if checked:
+ letzte_spalte = table.columnCount() - 1
+ for row in range(table.rowCount()):
+ if not table.isRowHidden(row):
+ # setRangeSelected (statt selectRow) fügt der Selektion
+ # hinzu, ohne vorherige Zeilen wieder abzuwählen.
+ table.setRangeSelected(
+ QTableWidgetSelectionRange(row, 0, row, letzte_spalte), True
+ )
+
+ alle_auswaehlen.stateChanged.connect(_alle_auswaehlen_umschalten)
+
+ mehrfachauswahl_hinweis = QLabel("Mehrfachauswahl mit Strg + Umschalt")
+ layout.addWidget(mehrfachauswahl_hinweis)
+
+ nur_laden_checkbox = QCheckBox("Nur Objekte laden (ohne weitere Prüfungen)")
+ layout.addWidget(nur_laden_checkbox)
+
+ buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
+ buttons.accepted.connect(dialog.accept)
+ buttons.rejected.connect(dialog.reject)
+ layout.addWidget(buttons)
+
+ dialog.adjustSize()
+ # Bezeichnungs-Spalte + großzügige Breite für den ADV-Namen (lange Namen
+ # sollen möglichst ohne Abschneiden sichtbar sein) + Rahmen/Scrollbar-Puffer.
+ breite = max(dialog.sizeHint().width(), table.columnWidth(0) + 480)
+
+ # Tabelle auf max. 15 sichtbare Zeilen begrenzen, Rest per Scrollbar – hier
+ # potenziell deutlich mehr Treffer als bei der VKZ-Suche, daher großzügiger
+ # bemessen als bei ask_bror_kombinationen (dort 5).
+ sichtbare_zeilen = min(len(sortierte_kombinationen), 15)
+ zeilenhoehe = table.verticalHeader().defaultSectionSize() if table.rowCount() else 20
+ kopfzeile_hoehe = table.horizontalHeader().height()
+ # + Puffer für eine ggf. eingeblendete horizontale Scrollbar (bei langen
+ # Einträgen), damit diese nicht die letzte Zeile abschneidet.
+ scrollbar_puffer = table.horizontalScrollBar().sizeHint().height()
+ table_hoehe = (
+ zeilenhoehe * sichtbare_zeilen + kopfzeile_hoehe
+ + 2 * table.frameWidth() + 4 + scrollbar_puffer
+ )
+ table.setMinimumHeight(table_hoehe)
+
+ dialog.resize(breite, dialog.sizeHint().height())
+
+ if exec_dialog(dialog) != DialogAccepted:
+ return [], False
+
+ ausgewaehlte_zeilen = sorted(set(item.row() for item in table.selectedItems()))
+ ausgewaehlt = [(table.item(row, 1).text(), table.item(row, 0).text()) for row in ausgewaehlte_zeilen]
+ return ausgewaehlt, nur_laden_checkbox.isChecked()
+
+
+def ask_landkreis_auswahl(
+ parent: Any,
+ landkreise: list[tuple[str, str]],
+) -> Optional[str]:
+ """Fragt per Dropdown-Dialog einen Landkreis ab (Fallback, wenn weder
+ Verfahrensnummer noch Landkreis in den Projekteigenschaften gesetzt ist).
+
+ Parameters
+ ----------
+ parent :
+ Eltern-Widget oder None.
+ landkreise :
+ Liste von ``(kennzahl, name)``-Tupeln, z.B. ``LANDKREISE`` aus
+ :mod:`sn_basis.functions.landkreise`.
+
+ Die Auswahlliste beginnt mit einem leeren Platzhalter (der Nutzer muss
+ aktiv einen Eintrag wählen) und enthält zusätzlich "Alle Landkreise"
+ (Sentinel :data:`~sn_basis.functions.landkreise.ALLE_LANDKREISE`).
+
+ Returns
+ -------
+ Optional[str]
+ Gewählte Landkreiskennzahl, :data:`ALLE_LANDKREISE` bei Auswahl aller
+ Landkreise, oder ``None`` bei Abbruch, leerem Platzhalter oder leerer
+ Landkreisliste.
+ """
+ if not landkreise:
+ return None
+
+ if QT_VERSION == 0: # Mock-Modus
+ print("Mock-Modus: ask_landkreis_auswahl → None (keine aktive Auswahl)")
+ return None
+
+ ALLE_LABEL = "Alle Landkreise"
+ namen = [""] + [ALLE_LABEL] + [name for _, name in landkreise]
+
+ dialog = QInputDialog(parent)
+ dialog.setWindowTitle("Landkreis auswählen")
+ if os.path.exists(_WAPPEN_ICON):
+ dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
+ dialog.setLabelText("Landkreis:")
+ dialog.setComboBoxItems(namen)
+ dialog.setComboBoxEditable(False)
+
+ if exec_dialog(dialog) != DialogAccepted:
+ return None
+
+ name = dialog.textValue()
+ if not name:
+ return None
+ if name == ALLE_LABEL:
+ return ALLE_LANDKREISE
+
+ name_zu_kennzahl = {n: k for k, n in landkreise}
+ return name_zu_kennzahl.get(str(name))
diff --git a/tests/test_verfahrensgebiet_alkis_komplett.py b/tests/test_verfahrensgebiet_alkis_komplett.py
index 04f4287..c6b11ce 100644
--- a/tests/test_verfahrensgebiet_alkis_komplett.py
+++ b/tests/test_verfahrensgebiet_alkis_komplett.py
@@ -148,6 +148,7 @@ _PATCH_KNICK = f"{_MOD}.knickpunkt_pruefung"
_PATCH_EXTRAHIERE = f"{_MOD}._extrahiere_knickpunkte"
_PATCH_LANDKREIS_SUCHE = f"{_MOD}._suche_landkreis_kombinationen"
_PATCH_LANDKREIS_BAU = f"{_MOD}._baue_layer_aus_landkreis_auswahl"
+_PATCH_ASK_LANDKREIS = f"{_MOD}.ask_landkreis_auswahl"
# ---------------------------------------------------------------------------
@@ -678,9 +679,13 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
@patch(_PATCH_PROGRESS)
@patch(_PATCH_MSGBOX)
+ @patch(_PATCH_ASK_LANDKREIS, return_value=None)
@patch(_PATCH_GET_VAR, return_value="")
- def test_kein_vkz_kein_landkreis_zeigt_fehler(self, mock_get_var, mock_msgbox, mock_prog):
- """Weder Verfahrensnummer noch Landkreis gesetzt → Fehlermeldung, kein Layer."""
+ def test_kein_vkz_kein_landkreis_zeigt_fehler(
+ self, mock_get_var, mock_ask_landkreis, mock_msgbox, mock_prog
+ ):
+ """Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer bricht den
+ Landkreis-Auswahldialog ab → Fehlermeldung, kein Layer."""
enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None)
self.assertIsNone(enriched)
@@ -689,6 +694,33 @@ class TestVerfahrensgebietAlkisKomplett(unittest.TestCase):
self.assertEqual(status, LoadStatus.NONE)
mock_msgbox.critical.assert_called_once()
+ @patch(_PATCH_PROGRESS)
+ @patch(_PATCH_PROJECT)
+ @patch(_PATCH_LANDKREIS_BAU)
+ @patch(_PATCH_LANDKREIS_SUCHE)
+ @patch(_PATCH_ASK_LANDKREIS, return_value="22")
+ @patch(_PATCH_GET_VAR, return_value="")
+ def test_kein_vkz_kein_landkreis_dialog_liefert_landkreis(
+ self, mock_get_var, mock_ask_landkreis, mock_such, mock_bau, mock_proj, mock_prog
+ ):
+ """Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer wählt einen
+ Landkreis im Dialog → Landkreis-Suchpfad wird mit dieser Kennzahl
+ verwendet (wie im Fall 'Landkreis aus Projekteigenschaften')."""
+ proj_inst = _DummyProject()
+ mock_proj.instance.return_value = proj_inst
+ vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)")
+ mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833")
+ mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True) # nur_laden=True
+ mock_prog.return_value = _make_progress_mock()
+
+ enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None)
+
+ mock_ask_landkreis.assert_called_once()
+ mock_such.assert_called_once_with(None, "22")
+ mock_bau.assert_called_once()
+ self.assertIs(enriched, vg_layer)
+ self.assertEqual(status, LoadStatus.FIRST)
+
@patch(_PATCH_PROGRESS)
@patch(_PATCH_PROJECT)
@patch(_PATCH_LANDKREIS_BAU)
diff --git a/ui/tabs/working_tab.py b/ui/tabs/working_tab.py
index 813a10f..5c090d8 100644
--- a/ui/tabs/working_tab.py
+++ b/ui/tabs/working_tab.py
@@ -21,7 +21,10 @@ from sn_verfahrensgebiet.functions.flurstueck_alkis import (
ALKIS_SF_URL,
FLST_LAYER_NAME,
)
-from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import verfahrensgebiet_alkis_komplett
+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.grenzpunkt_alkis import grenzpunkte_gemarkungsweise, JOINED_LAYER_NAME
from sn_verfahrensgebiet.functions.gemarkung_alkis import (
@@ -201,23 +204,27 @@ class WorkingTab(QWidget):
apply_style(flst_layer, "GIS_Flst_Beschriftung_ALKIS_NAS.qml")
self.setze_haken(self.haken_flurst, True)
- # BROR-Layer: QML-Stil anwenden
- if bror_layer and bror_layer.isValid():
- apply_style(bror_layer, "BauRaumOderBodenordnungsrecht.qml")
+ # BROR-Layer bekommt seinen Style bereits direkt beim Laden in
+ # verfahrensgebiet_alkis_komplett() (auch im "Nur laden"-Fall).
- # 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)
+ # 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)
+ 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)
+ 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")