Geodienste aus der Linkliste laden — je Verfahren, aus der Seitenleiste
Erik, 11.09.2026: "als angemeldeter Nutzer TG-spezifisch die Dienste aus der Linkliste einbinden". - vln_karten/linkliste.py (neu): holt GET /v2/linkliste?vkz=…, baut je Anbieter den passenden Layer (wms, wfs, rest/ArcGIS, geojson/ogr), wendet den beim Eintrag hinterlegten QML-Stil an und haengt alles schrittweise in die Gruppe "Geodienste <VKZ> <Name>" mit Untergruppen je Listengruppe. - Seitenleiste: neuer Block "Geodienste (Linkliste)" mit Liste holen, Haken je Dienst (Name, Gruppe, Herkunft, Stil-Hinweis), alle an/aus und Ausgewaehlte laden. - Die Liste kommt fertig aufgeloest vom Server; das Plugin rechnet nichts nach. Zwei Dinge, die der Test gezeigt hat (tools/headless_dienste_test.py, QGIS 4.2 ohne Fenster gegen die Live-Schnittstelle): * Die Adressform muss schlicht "url=<Adresse>" sein, wie in Plugin_SN_Basis/modules/LayerLoader.py. Mit einem selbst gebauten QgsDataSourceUri (crs, format) kam der ArcGIS-Layer erst nach 60 s und ungueltig zurueck; jetzt 0,4 s und 4457 Objekte. * Ein 404 auf style.qml erreicht QgsBlockingNetworkRequest als Transportfehler. Erst den Statuscode ansehen, sonst meldet das Plugin einen Fehler, wo schlicht kein Stil hinterlegt ist. Testlauf 11.09.2026: 44 Dienste in 0,8 s, Layer gueltig, Stil angewendet, "kein Stil hinterlegt" sauber gemeldet — 0 Fehler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Die Linkliste im Plugin: holen, Layer bauen, Stil anwenden — gegen die echte Schnittstelle.
|
||||
|
||||
Aufruf (macOS, QGIS 4.2 im Bundle; VLN_TOKEN = API-Key aus dem Login):
|
||||
|
||||
APP=/Applications/QGIS-final-4_2_0.app; LIB="$APP/Contents/Resources/python3.12"
|
||||
QT_QPA_PLATFORM=offscreen PROJ_DATA="$APP/Contents/Resources/qgis/proj" \\
|
||||
PYTHONPATH="$LIB:$LIB/lib-dynload:$LIB/site-packages" \\
|
||||
"$APP/Contents/MacOS/python3.12" tools/headless_dienste_test.py
|
||||
"""
|
||||
import os, sys, tempfile, time
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from qgis.core import QgsApplication, QgsNetworkAccessManager, QgsProject
|
||||
|
||||
app = QgsApplication([], True)
|
||||
QgsApplication.setPrefixPath("/Applications/QGIS-final-4_2_0.app/Contents/MacOS", True)
|
||||
QgsApplication.setPluginPath("/Applications/QGIS-final-4_2_0.app/Contents/PlugIns/qgis")
|
||||
app.initQgis()
|
||||
from qgis.PyQt.QtNetwork import QNetworkProxyFactory, QNetworkDiskCache
|
||||
QNetworkProxyFactory.setUseSystemConfiguration(False)
|
||||
c = QNetworkDiskCache(); c.setCacheDirectory(tempfile.mkdtemp())
|
||||
QgsNetworkAccessManager.instance().setCache(c)
|
||||
|
||||
from vln_karten import linkliste as LL
|
||||
from vln_karten.api_client import KartenApiClient
|
||||
|
||||
BASE, KEY, VKZ = "https://api.flurneuordnung-sachsen.de/v2", os.environ["VLN_TOKEN"], "27027"
|
||||
FEHLER = []
|
||||
|
||||
|
||||
def pruefe(b, t):
|
||||
print((" OK " if b else " FEHL ") + t)
|
||||
if not b:
|
||||
FEHLER.append(t)
|
||||
|
||||
|
||||
client = KartenApiClient(base_url=BASE, api_key=KEY)
|
||||
print("== 1) Liste holen ==")
|
||||
t = time.time()
|
||||
dienste = LL.dienste_laden(client, VKZ)
|
||||
pruefe(len(dienste) > 0, "%d Dienste in %.2f s" % (len(dienste), time.time() - t))
|
||||
pruefe(all({"name", "provider", "url", "herkunft"} <= set(d) for d in dienste),
|
||||
"alle Felder vorhanden, die der Layerbau braucht")
|
||||
|
||||
print("== 2) Layer bauen, je Anbieter einer ==")
|
||||
gebaut = {}
|
||||
for d in dienste:
|
||||
p = d["provider"]
|
||||
if p in gebaut:
|
||||
continue
|
||||
t = time.time()
|
||||
lyr = LL.layer_bauen(d)
|
||||
ok = lyr is not None and lyr.isValid()
|
||||
gebaut[p] = ok
|
||||
pruefe(ok, "%-5s %-34s %s (%.1f s)" % (p, d["name"][:34],
|
||||
"gueltig" if ok else (lyr.error().message()[:60] if lyr else "kein Layer"),
|
||||
time.time() - t))
|
||||
if ok and hasattr(lyr, "featureCount"):
|
||||
print(" Features:", lyr.featureCount())
|
||||
|
||||
print("== 3) Stil aus der Datenbank anwenden ==")
|
||||
mit_stil = [d for d in dienste if d.get("stil")]
|
||||
if mit_stil:
|
||||
d = mit_stil[0]
|
||||
lyr = LL.layer_bauen(d)
|
||||
if lyr is not None and lyr.isValid():
|
||||
vorher = lyr.renderer().type() if hasattr(lyr, "renderer") and lyr.renderer() else "-"
|
||||
ok, meldung = LL.stil_anwenden(lyr, BASE, d["id"], KEY)
|
||||
nachher = lyr.renderer().type() if hasattr(lyr, "renderer") and lyr.renderer() else "-"
|
||||
pruefe(ok, "Stil fuer '%s': %s (Renderer %s -> %s)" % (d["name"], meldung or "angewendet",
|
||||
vorher, nachher))
|
||||
else:
|
||||
print(" (Layer nicht gueltig, Stil nicht pruefbar)")
|
||||
else:
|
||||
print(" (kein Eintrag mit Stil)")
|
||||
|
||||
print("== 4) ohne Stil meldet sauber ==")
|
||||
ohne = [d for d in dienste if not d.get("stil")]
|
||||
if ohne:
|
||||
lyr = LL.layer_bauen(ohne[0])
|
||||
if lyr is not None and lyr.isValid():
|
||||
ok, meldung = LL.stil_anwenden(lyr, BASE, ohne[0]["id"], KEY)
|
||||
pruefe(not ok and "kein Stil" in meldung, "Meldung statt Fehler: %s" % meldung)
|
||||
|
||||
app.exitQgis()
|
||||
print("== %d Fehler ==" % len(FEHLER))
|
||||
for f in FEHLER:
|
||||
print(" -", f)
|
||||
sys.exit(1 if FEHLER else 0)
|
||||
@@ -0,0 +1,205 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Linkliste der Geodienste — die Dienste eines Verfahrens als QGIS-Layer.
|
||||
|
||||
Die Liste kommt vom VLN-Manager (``GET /v2/linkliste?vkz=…``) und ist dort vierstufig gepflegt:
|
||||
landesweit, Landkreis, Team und Verfahren erben aufeinander. Das Plugin bekommt bereits die
|
||||
**aufgelöste** Liste — es muss nichts zusammenrechnen, sondern nur laden. Je Eintrag steht dabei,
|
||||
woher er stammt (``herkunft``), damit man in QGIS sieht, ob ein Dienst landesweit gilt oder nur
|
||||
für dieses eine Verfahren eingetragen wurde.
|
||||
|
||||
Vier Anbieter kommen vor, jeder braucht in QGIS eine andere Datenquelle:
|
||||
|
||||
========== ================================================================
|
||||
``wms`` ``QgsRasterLayer`` mit ``crs``/``format``/``layers``/``url``
|
||||
``wfs`` ``QgsVectorLayer`` mit ``typename`` (Anbieter ``WFS``)
|
||||
``rest`` ``QgsVectorLayer`` auf den ArcGIS-FeatureServer
|
||||
``geojson`` ``QgsVectorLayer`` über ``ogr`` (Datei oder Adresse)
|
||||
========== ================================================================
|
||||
|
||||
Der Stil liegt beim Eintrag in der Datenbank und kommt über
|
||||
``GET /v2/linkliste/{id}/style.qml``; ohne Stil bleibt die QGIS-Vorgabe.
|
||||
"""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from qgis.PyQt.QtCore import Qt, QTimer, QUrl
|
||||
from qgis.PyQt.QtNetwork import QNetworkRequest
|
||||
from qgis.PyQt.QtWidgets import QProgressBar
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsApplication,
|
||||
QgsBlockingNetworkRequest,
|
||||
QgsNetworkAccessManager,
|
||||
QgsDataSourceUri,
|
||||
QgsProject,
|
||||
QgsRasterLayer,
|
||||
QgsVectorLayer,
|
||||
)
|
||||
|
||||
from .api_client import ApiError
|
||||
|
||||
#: Gruppenname im Ebenenbaum — alle Dienste eines Verfahrens landen darunter.
|
||||
GRUPPE = "Geodienste"
|
||||
|
||||
#: Vorgaben für WMS-Anfragen; dieselben Werte nutzt das Web-GIS (karte/doku/hintergrund.md).
|
||||
WMS_FORMAT = "image/png"
|
||||
WMS_CRS = "EPSG:25833"
|
||||
|
||||
|
||||
def dienste_laden(client, vkz):
|
||||
"""``GET /linkliste?vkz=…`` -> Liste der aufgelösten Einträge.
|
||||
|
||||
:raises ApiError: wenn die Antwort nicht das erwartete Format hat.
|
||||
"""
|
||||
daten = client._request("GET", "/linkliste?vkz=%s" % vkz)
|
||||
zeilen = daten.get("dienste") if isinstance(daten, dict) else None
|
||||
if not isinstance(zeilen, list):
|
||||
raise ApiError("Antwort von /linkliste hat ein unerwartetes Format.")
|
||||
return zeilen
|
||||
|
||||
|
||||
def stil_url(base_url, eintrag_id):
|
||||
return "%s/linkliste/%s/style.qml" % (base_url.rstrip("/"), eintrag_id)
|
||||
|
||||
|
||||
def stil_anwenden(layer, base_url, eintrag_id, api_key=None):
|
||||
"""Den Stil des Eintrags holen und anwenden. Ohne Stil (404) bleibt alles, wie es ist.
|
||||
|
||||
:returns: ``(True, "")`` oder ``(False, Meldung)``.
|
||||
"""
|
||||
req = QNetworkRequest(QUrl(stil_url(base_url, eintrag_id)))
|
||||
if api_key:
|
||||
req.setRawHeader(b"X-API-Key", api_key.encode("utf-8"))
|
||||
blk = QgsBlockingNetworkRequest()
|
||||
fehler = blk.get(req)
|
||||
antwort = blk.reply()
|
||||
code = antwort.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute)
|
||||
# Ein 404 kommt beim blockierenden Aufruf als Transportfehler an — erst den Statuscode
|
||||
# ansehen, sonst meldet das Plugin einen Fehler, wo schlicht kein Stil hinterlegt ist.
|
||||
if code == 404:
|
||||
return False, "kein Stil hinterlegt"
|
||||
if fehler != QgsBlockingNetworkRequest.NoError and not code:
|
||||
return False, blk.errorMessage()
|
||||
if code != 200:
|
||||
return False, "HTTP %s" % code
|
||||
pfad = os.path.join(tempfile.gettempdir(), "vln_dienst_%s.qml" % eintrag_id)
|
||||
with open(pfad, "wb") as fh:
|
||||
fh.write(bytes(antwort.content()))
|
||||
meldung, ok = layer.loadNamedStyle(pfad)
|
||||
if ok:
|
||||
layer.triggerRepaint()
|
||||
return bool(ok), "" if ok else meldung
|
||||
|
||||
|
||||
def layer_bauen(eintrag, zeit_ms=20000):
|
||||
"""Einen Eintrag der Linkliste in einen QGIS-Layer verwandeln.
|
||||
|
||||
Die Adressform ist die, die sich in ``Plugin_SN_Basis/modules/LayerLoader.py`` bewährt hat:
|
||||
schlicht ``url=<Adresse>``, bei WFS zusätzlich ``typename=``. Ein selbst gebautes
|
||||
``QgsDataSourceUri`` mit ``crs``/``format`` führte beim ArcGIS-Anbieter dazu, dass der
|
||||
Layer nach einer Minute ungültig zurückkam (gemessen 11.09.2026).
|
||||
|
||||
:param zeit_ms: Netzwerk-Zeitlimit für die netzgebundenen Anbieter.
|
||||
:returns: Layer oder ``None``, wenn der Anbieter unbekannt ist.
|
||||
"""
|
||||
name = str(eintrag.get("name") or "Dienst")
|
||||
provider = str(eintrag.get("provider") or "").lower()
|
||||
url = str(eintrag.get("url") or "").strip()
|
||||
ebene = str(eintrag.get("layer") or "").strip()
|
||||
|
||||
if provider in ("wms", "wfs", "rest"):
|
||||
try:
|
||||
verwalter = QgsNetworkAccessManager.instance()
|
||||
if hasattr(verwalter, "setTimeout"):
|
||||
verwalter.setTimeout(zeit_ms)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
|
||||
if provider == "wms":
|
||||
uri = url if url.lower().startswith("url=") else "url=" + url
|
||||
if ebene and "layers=" not in uri:
|
||||
uri = "layers=%s format=%s crs=%s %s" % (ebene, WMS_FORMAT, WMS_CRS, uri)
|
||||
return QgsRasterLayer(uri, name, "wms")
|
||||
|
||||
if provider == "wfs":
|
||||
basis = url[4:] if url.lower().startswith("url=") else url
|
||||
uri = "url=" + basis
|
||||
if ebene:
|
||||
uri += " typename=" + ebene
|
||||
return QgsVectorLayer(uri, name, "WFS")
|
||||
|
||||
if provider == "rest":
|
||||
# Die Adresse zeigt meist schon auf den Layer (…/FeatureServer/3). Fehlt der Index,
|
||||
# wird der aus der Liste angehängt, sonst 0 — der Anbieter braucht einen.
|
||||
ziel = url.rstrip("/")
|
||||
if ziel.lower().endswith("/featureserver") or ziel.lower().endswith("/mapserver"):
|
||||
ziel = "%s/%s" % (ziel, ebene or "0")
|
||||
return QgsVectorLayer("url=" + ziel, name, "arcgisfeatureserver")
|
||||
|
||||
if provider in ("geojson", "ogr"):
|
||||
return QgsVectorLayer(url, name, "ogr")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def dienste_hinzufuegen(iface, base_url, vkz, vkz_name, dienste, api_key=None, nur_ids=None):
|
||||
"""Die Dienste als Layer in die Gruppe „Geodienste“ hängen — **schrittweise**.
|
||||
|
||||
Wie beim Laden der Verfahrenslayer wird je Ereignisschleifen-Durchlauf genau ein Layer
|
||||
angelegt: WMS- und WFS-Anbieter fragen beim Anlegen ihre Capabilities ab, und bei zwanzig
|
||||
Diensten friert die Oberfläche sonst sichtbar ein. Untergruppen entstehen je Gruppe der
|
||||
Liste (Hintergrund, Wasser, Natur …), damit der Ebenenbaum lesbar bleibt.
|
||||
|
||||
:param nur_ids: nur diese Einträge laden (``None`` = alle).
|
||||
:returns: die Meldung mit dem Fortschrittsbalken.
|
||||
"""
|
||||
auswahl = [d for d in dienste if nur_ids is None or int(d.get("id", 0)) in nur_ids]
|
||||
projekt = QgsProject.instance()
|
||||
wurzel = projekt.layerTreeRoot()
|
||||
oben_name = ("%s %s %s" % (GRUPPE, vkz, vkz_name or "")).strip()
|
||||
oben = wurzel.findGroup(oben_name) or wurzel.insertGroup(0, oben_name)
|
||||
untergruppen = {}
|
||||
|
||||
balken = QProgressBar()
|
||||
balken.setMaximum(max(1, len(auswahl)))
|
||||
balken.setValue(0)
|
||||
balken.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
||||
meldung = iface.messageBar().createMessage("VLN Karten", "Geodienste werden geladen …")
|
||||
meldung.layout().addWidget(balken)
|
||||
iface.messageBar().pushWidget(meldung, Qgis.Info)
|
||||
|
||||
zustand = {"i": 0, "geladen": 0, "fehler": []}
|
||||
|
||||
def schritt():
|
||||
if zustand["i"] >= len(auswahl):
|
||||
iface.messageBar().popWidget(meldung)
|
||||
text = "%d Geodienste geladen." % zustand["geladen"]
|
||||
if zustand["fehler"]:
|
||||
text += " %d nicht ladbar: %s" % (len(zustand["fehler"]),
|
||||
", ".join(zustand["fehler"][:3]))
|
||||
iface.messageBar().pushMessage(
|
||||
"VLN Karten", text,
|
||||
level=Qgis.Success if zustand["geladen"] else Qgis.Warning, duration=10)
|
||||
return
|
||||
eintrag = auswahl[zustand["i"]]
|
||||
zustand["i"] += 1
|
||||
gruppe_name = str(eintrag.get("gruppe_name") or "Sonstiges")
|
||||
if gruppe_name not in untergruppen:
|
||||
untergruppen[gruppe_name] = (oben.findGroup(gruppe_name)
|
||||
or oben.addGroup(gruppe_name))
|
||||
layer = layer_bauen(eintrag)
|
||||
if layer is not None and layer.isValid():
|
||||
if eintrag.get("stil"):
|
||||
stil_anwenden(layer, base_url, eintrag.get("id"), api_key)
|
||||
projekt.addMapLayer(layer, False)
|
||||
untergruppen[gruppe_name].addLayer(layer)
|
||||
zustand["geladen"] += 1
|
||||
else:
|
||||
zustand["fehler"].append(str(eintrag.get("name") or "?"))
|
||||
balken.setValue(zustand["i"])
|
||||
meldung.setText("Geodienste werden geladen … %d von %d (%s)"
|
||||
% (zustand["i"], len(auswahl), eintrag.get("name")))
|
||||
QTimer.singleShot(0, schritt)
|
||||
|
||||
QTimer.singleShot(0, schritt)
|
||||
return meldung
|
||||
+70
-4
@@ -11,8 +11,9 @@ Aufbau von oben nach unten:
|
||||
1. Anmeldung — Status + Knopf (Anmelden/Abmelden)
|
||||
2. Verfahren — Suchfeld (filtert) + Auswahlliste
|
||||
3. Layer — Auswahl der Gruppen, laden, Änderungen speichern
|
||||
4. Darstellung — Stil speichern (mit Häkchen „für alle Verfahren")
|
||||
5. Statuszeile
|
||||
4. Geodienste — die Linkliste des Verfahrens: auswählen und als Layer laden
|
||||
5. Darstellung — Stil speichern (mit Häkchen „für alle Verfahren")
|
||||
6. Statuszeile
|
||||
|
||||
Der frühere GeoJSON-Weg (Kopie laden, später als Ganzes hochladen) ist am 08.09.2026
|
||||
entfallen (Erik: „das Speichern via OGC einbauen, GeoJSON nicht mehr verwenden"). Über
|
||||
@@ -22,7 +23,8 @@ nur die offenen Bearbeitungssitzungen.
|
||||
|
||||
from qgis.PyQt.QtCore import Qt
|
||||
from qgis.PyQt.QtWidgets import (QCheckBox, QComboBox, QDockWidget, QFrame, QGroupBox,
|
||||
QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy,
|
||||
QHBoxLayout, QLabel, QLineEdit, QListWidget,
|
||||
QListWidgetItem, QPushButton, QSizePolicy,
|
||||
QVBoxLayout, QWidget)
|
||||
|
||||
PANEL_TITEL = "VLN Karten"
|
||||
@@ -64,6 +66,7 @@ class VlnPanel(QDockWidget):
|
||||
aussen.addWidget(self._bau_anmeldung())
|
||||
aussen.addWidget(self._bau_verfahren())
|
||||
aussen.addWidget(self._bau_layer())
|
||||
aussen.addWidget(self._bau_dienste())
|
||||
aussen.addWidget(self._bau_darstellung())
|
||||
aussen.addStretch(1)
|
||||
aussen.addWidget(self._bau_status())
|
||||
@@ -143,6 +146,66 @@ class VlnPanel(QDockWidget):
|
||||
"jede Änderung sofort zurück.")))
|
||||
return kasten
|
||||
|
||||
def _bau_dienste(self):
|
||||
"""Die Linkliste des gewählten Verfahrens — vierstufig gepflegt, hier nur geladen.
|
||||
|
||||
Die Liste kommt fertig aufgelöst vom Server (landesweit, Landkreis, Team, Verfahren);
|
||||
je Zeile steht die Herkunft dabei, damit man sieht, wo ein Dienst herkommt.
|
||||
"""
|
||||
kasten = QGroupBox("Geodienste (Linkliste)")
|
||||
lage = QVBoxLayout(kasten)
|
||||
lage.setSpacing(6)
|
||||
lage.addWidget(_klein(QLabel("WMS-, WFS- und ArcGIS-Dienste für dieses Verfahren.")))
|
||||
self.dienste_liste = QListWidget()
|
||||
self.dienste_liste.setMinimumHeight(140)
|
||||
self.dienste_liste.setToolTip("Angehakte Dienste werden geladen")
|
||||
lage.addWidget(self.dienste_liste)
|
||||
zeile = QHBoxLayout()
|
||||
self.dienste_holen_knopf = QPushButton("Liste holen")
|
||||
self.dienste_holen_knopf.clicked.connect(lambda: self._ruf("dienste_holen"))
|
||||
self.dienste_laden_knopf = QPushButton("Ausgewählte laden")
|
||||
self.dienste_laden_knopf.clicked.connect(lambda: self._ruf("dienste_laden"))
|
||||
zeile.addWidget(self.dienste_holen_knopf)
|
||||
zeile.addWidget(self.dienste_laden_knopf)
|
||||
lage.addLayout(zeile)
|
||||
self.dienste_alle = QPushButton("alle an- oder abwählen")
|
||||
self.dienste_alle.setFlat(True)
|
||||
self.dienste_alle.clicked.connect(self._dienste_umschalten)
|
||||
lage.addWidget(self.dienste_alle)
|
||||
return kasten
|
||||
|
||||
def _dienste_umschalten(self):
|
||||
"""Alles an, wenn noch etwas aus ist; sonst alles aus."""
|
||||
etwas_aus = any(self.dienste_liste.item(i).checkState() != Qt.CheckState.Checked
|
||||
for i in range(self.dienste_liste.count()))
|
||||
ziel = Qt.CheckState.Checked if etwas_aus else Qt.CheckState.Unchecked
|
||||
for i in range(self.dienste_liste.count()):
|
||||
self.dienste_liste.item(i).setCheckState(ziel)
|
||||
|
||||
def set_dienste(self, dienste):
|
||||
"""Die geholte Liste anzeigen: Name, Gruppe und Herkunft, alle zunächst angehakt."""
|
||||
self.dienste_liste.clear()
|
||||
for d in dienste or []:
|
||||
text = "%s · %s · %s" % (d.get("name"), d.get("gruppe_name") or "",
|
||||
d.get("herkunft_text") or "")
|
||||
if d.get("stil"):
|
||||
text += " · mit Stil"
|
||||
eintrag = QListWidgetItem(text)
|
||||
eintrag.setFlags(eintrag.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||
eintrag.setCheckState(Qt.CheckState.Checked)
|
||||
eintrag.setData(Qt.ItemDataRole.UserRole, d)
|
||||
eintrag.setToolTip("%s\n%s" % (d.get("url"), d.get("beschreibung") or ""))
|
||||
self.dienste_liste.addItem(eintrag)
|
||||
|
||||
def gewaehlte_dienste(self):
|
||||
"""Die angehakten Einträge als Liste der Server-Datensätze."""
|
||||
aus = []
|
||||
for i in range(self.dienste_liste.count()):
|
||||
e = self.dienste_liste.item(i)
|
||||
if e.checkState() == Qt.CheckState.Checked:
|
||||
aus.append(e.data(Qt.ItemDataRole.UserRole))
|
||||
return aus
|
||||
|
||||
def _bau_darstellung(self):
|
||||
kasten = QGroupBox("Darstellung")
|
||||
lage = QVBoxLayout(kasten)
|
||||
@@ -174,7 +237,9 @@ class VlnPanel(QDockWidget):
|
||||
self.login_knopf.setVisible(not angemeldet)
|
||||
self.logout_knopf.setVisible(angemeldet)
|
||||
for widget in (self.filter_feld, self.combo, self.ogc_knopf, self.speichern_knopf,
|
||||
self.stil_knopf, self.vorlage_haken, *self.gruppen_haken.values()):
|
||||
self.stil_knopf, self.vorlage_haken, self.dienste_holen_knopf,
|
||||
self.dienste_laden_knopf, self.dienste_alle, self.dienste_liste,
|
||||
*self.gruppen_haken.values()):
|
||||
widget.setEnabled(angemeldet)
|
||||
if not angemeldet:
|
||||
self._stumm = True
|
||||
@@ -182,6 +247,7 @@ class VlnPanel(QDockWidget):
|
||||
self.combo.clear()
|
||||
self.combo.addItem("— bitte anmelden —", None)
|
||||
self.filter_feld.clear()
|
||||
self.dienste_liste.clear()
|
||||
self._stumm = False
|
||||
|
||||
def set_verfahren(self, verfahren):
|
||||
|
||||
@@ -19,6 +19,7 @@ from qgis.core import Qgis, QgsDataSourceUri, QgsProject
|
||||
from .api_client import ApiError, AuthError, KartenApiClient
|
||||
from .panel import VlnPanel
|
||||
from .login_dialog import LoginDialog, SETTINGS_GROUP
|
||||
from . import linkliste
|
||||
from . import ogc_layers
|
||||
|
||||
MENU_TITLE = "&VLN Karten"
|
||||
@@ -50,6 +51,8 @@ class VlnKartenPlugin:
|
||||
"verfahren_gewaehlt": self.on_verfahren_changed,
|
||||
"load_ogc": self.run_load_ogc,
|
||||
"save_edits": self.run_save_edits,
|
||||
"dienste_holen": self.run_dienste_holen,
|
||||
"dienste_laden": self.run_dienste_laden,
|
||||
"save_style": self.run_save_style,
|
||||
},
|
||||
)
|
||||
@@ -288,6 +291,56 @@ class VlnKartenPlugin:
|
||||
if self.panel is not None:
|
||||
self.panel.set_status("%d von %d Layern gespeichert." % (len(gespeichert), len(offen)))
|
||||
|
||||
def run_dienste_holen(self):
|
||||
"""Die Linkliste des gewählten Verfahrens holen und in der Seitenleiste anzeigen.
|
||||
|
||||
Der Server liefert sie bereits aufgelöst: landesweit, Landkreis, Team und Verfahren
|
||||
sind zusammengeführt, geerbte Einträge tragen ihre Herkunft. Das Plugin rechnet nichts
|
||||
nach, es zeigt und lädt.
|
||||
"""
|
||||
if not self._ensure_logged_in():
|
||||
return
|
||||
vkz = self._current_vkz()
|
||||
if not vkz:
|
||||
self._show_error("Kein Verfahren", "Bitte zuerst ein Verfahren auswählen.")
|
||||
return
|
||||
try:
|
||||
dienste = linkliste.dienste_laden(self.client, vkz)
|
||||
except AuthError:
|
||||
self._handle_session_expired()
|
||||
return
|
||||
except ApiError as exc:
|
||||
self._show_error("Linkliste", str(exc))
|
||||
return
|
||||
self.panel.set_dienste(dienste)
|
||||
herkunft = {}
|
||||
for d in dienste:
|
||||
herkunft[d.get("herkunft_text") or "?"] = herkunft.get(d.get("herkunft_text") or "?", 0) + 1
|
||||
text = ", ".join("%d %s" % (n, k) for k, n in herkunft.items()) or "nichts hinterlegt"
|
||||
self.panel.set_status("%d Geodienste: %s" % (len(dienste), text))
|
||||
self.iface.messageBar().pushMessage(
|
||||
"VLN Karten", "%d Geodienste für dieses Verfahren (%s)." % (len(dienste), text),
|
||||
level=Qgis.Info, duration=6)
|
||||
|
||||
def run_dienste_laden(self):
|
||||
"""Die angehakten Dienste als Layer in die Gruppe „Geodienste" hängen."""
|
||||
if not self._ensure_logged_in():
|
||||
return
|
||||
vkz = self._current_vkz()
|
||||
if not vkz:
|
||||
self._show_error("Kein Verfahren", "Bitte zuerst ein Verfahren auswählen.")
|
||||
return
|
||||
gewaehlt = self.panel.gewaehlte_dienste()
|
||||
if not gewaehlt:
|
||||
self._show_error("Nichts ausgewählt",
|
||||
"Bitte zuerst die Liste holen und dann die gewünschten Dienste "
|
||||
"anhaken.")
|
||||
return
|
||||
linkliste.dienste_hinzufuegen(
|
||||
self.iface, self.client.base_url, vkz, self.panel.aktueller_text(), gewaehlt,
|
||||
api_key=self.client.api_key)
|
||||
self.panel.set_status("%d Geodienste werden geladen …" % len(gewaehlt))
|
||||
|
||||
def run_save_style(self):
|
||||
"""Den Stil des aktiven OGC-Layers auf den Server zurückschreiben.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user