KAIandClaude Opus 5.5 c84bd6e378 Sichtpruefung der Verkehrszeichen mit echten Daten, ohne Zugangsschluessel
tools/headless_render_live.py rendert ein Verfahren in QGIS 4.2 offscreen: Daten aus dem
oeffentlichen /v2/maps/widmung/<vkz>, zusatz als Text wie beim OAPIF-Provider, Stil aus dem
Stil-Erzeuger des Managers (derselbe Code wie auf dem Server), Grafiken und Umschreiben
ueber zeichen_katalog. Heraus kommen Bilder bei 1:2000, 1:500 je Schild, 1:5000 und 1:20000.

Entstanden am 23.09.2026: die Fragezeichen unter den Schildern und ein gedrehtes Schild,
das aufrecht stand, sah man erst im echten Bild, nicht im Quelltext des Stils. Wichtig
fuer solche Laeufe: @map_scale gibt es nur mit dem Ausdruckskontext der Karteneinstellungen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 06:44:48 +02:00

API_Karte_QGISDemo — Beispiel: Karten-API des VLN Managers aus QGIS ansteuern

Dieses Repository zeigt an einem lauffähigen QGIS-Plugin, wie die Karten-API des VLN Managers (https://api.flurneuordnung-sachsen.de/v2) aus QGIS bzw. PyQGIS angesteuert wird: Anmeldung, Laden der Verfahrens-Layer als editierbare QGIS-Layer und Zurückschreiben der Änderungen. Es liest und schreibt dieselben Daten, die auch das Web-GIS (karte.flurneuordnung-sachsen.de) verwendet.

Der Code ist bewusst als Referenz-Implementierung gehalten — wer eine eigene Integration (Plugin, Skript, Processing-Werkzeug) bauen will, findet hier alle Bausteine.

Die Ansteuerung in Kürze

So sieht der minimale Ablauf in PyQGIS aus (vollständige, robuste Fassung in vln_karten/api_client.py):

import json
from qgis.PyQt.QtCore import QUrl
from qgis.PyQt.QtNetwork import QNetworkRequest
from qgis.core import QgsBlockingNetworkRequest

BASE = "https://api.flurneuordnung-sachsen.de/v2"

def request(method, path, payload=None, api_key=None):
    req = QNetworkRequest(QUrl(BASE + path))
    req.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json")
    if api_key:
        req.setRawHeader(b"X-API-Key", api_key.encode())
    blk = QgsBlockingNetworkRequest()
    body = json.dumps(payload).encode() if payload is not None else b""
    getattr(blk, method)(req, *([body] if method != "get" else []))
    return json.loads(bytes(blk.reply().content()) or b"null")

# 1) Login: liefert das userauth-Token = API-Key
login = request("post", "/person/login",
                {"mail": "...", "password": "..."})
api_key = login["data"]["userauth"]

# 2) Layer lesen: GeoJSON FeatureCollection in EPSG:25833
fc = request("get", "/maps/p41?vkz=22017&limit=2000&offset=0",
             api_key=api_key)

# 3) Layer schreiben: PUT ersetzt den kompletten Bestand dieser VKZ!
request("put", "/maps/p41/22017", payload=fc, api_key=api_key)

Die drei Stolpersteine, die das Beispiel-Plugin jeweils sauber löst:

  1. CRS: Die API liefert/erwartet EPSG:25833 (ETRS89/UTM33), nicht WGS84 wie in RFC 7946 üblich. GeoJSON-Export aus QGIS daher mit QgsJsonExporter.setTransformGeometries(False).
  2. Gemischte Geometrietypen: Eine FeatureCollection kann Punkte, Linien und Flächen zugleich enthalten — QGIS-Layer können das nicht. Über die OGC API stellt sich die Frage nicht mehr: der Server bietet je Sammlung drei Familien an (-punkt, -linie, -flaeche), und jede wird ein eigener Layer.
  3. PUT ersetzt alles: Der Schreibendpunkt legt einen neuen Snapshot an und ersetzt den kompletten Layer-Bestand der VKZ. Immer den vollständigen Datensatz senden — Teilmengen löschen den Rest serverseitig.

API-Kommunikation

POST /person/login
  Body:     {"mail": "...", "password": "..."}
  Antwort:  {"data": {"userauth": "<url_token>", "id": ...}}
  userauth = API-Key, danach Header "X-API-Key: <userauth>"

GET  /tgen                              Verfahren/TGs (Auth nötig)

GET  /maps/<layer>?vkz=&limit=&offset=  Layer lesen, mit Paging
GET  /maps/<layer>/{vkz}                Layer lesen, ohne Paging
  layer:    umringe | p41 | st | kas | we
  Auth:     umringe/p41/st öffentlich, kas/we mit X-API-Key
  Antwort:  GeoJSON FeatureCollection in EPSG:25833

PUT  /maps/<layer>/{vkz}                Layer schreiben (Auth nötig)
  Body:     GeoJSON (ersetzt den Layer dieser VKZ, neuer Snapshot)
  Antwort:  {"data": {"vkz","layer","art","speicher_id"}, "status":"ok"}

Fehlerformat: RFC 7807 (application/problem+json)
Leerer vkz-Parameter (?vkz=) wird mit HTTP 400 abgelehnt.

OGC API Features — laden, bearbeiten, zurückspeichern (empfohlen)

Seit dem 07.09.2026 spricht die API OGC API Features (Part 1 Core, Part 2 CRS, Part 3 Queryables, Part 4 CRUD) unter https://api.flurneuordnung-sachsen.de/v2/ogc. Das ist der Weg für alle, die nur einen Manager-Login haben: QGIS lädt die Layer nativ, und jede Änderung im Bearbeitungsmodus geht beim Speichern einzeln an die API (POST/PUT/PATCH/DELETE je Feature) — kein Vollersetzen, kein Memory-Layer.

Ohne Plugin (QGIS 3.28+ / QGIS 4)

  1. Layer → Datenquellenverwaltung → WFS / OGC API - Features → Neu

    • Name: VLN Manager, URL: https://api.flurneuordnung-sachsen.de/v2/ogc
    • Version: OGC API - Features
    • Authentifizierung: Basic (E-Mail + Passwort des Managers) — oder Methode API-Header mit X-API-Key (Wert = userauth aus POST /person/login bzw. ein vlnk_…-Schlüssel aus meinVLN → API-Keys)
  2. Verbinden → die Sammlungen des eigenen Verfahrens erscheinen, z. B.

    Sammlung Inhalt Speicher-CRS
    umringe-27998-flaeche, p41-…, st-…, kas-…, we-…, ngg-…, biotop-… jüngster Stand des Layers (wie im Web-GIS) EPSG:25833
    `vorerhebung-27998--punkt -linie -flaeche`
    `massnahmeblatt-27998-linie -flaeche -punkt`

    Jede Sammlung gibt es je Geometriefamilie (-punkt, -linie, -flaeche): QGIS-Layer sind eintypig, die Stände des Web-GIS mischen Punkte, Linien und Flächen. Die Queryables nennen je Familie den Geometrietyp und die Felder — so lassen sich auch leere Layer direkt bearbeiten. Die Basis ohne Suffix (st-27998) ist nicht gelistet, aber lesbar.

  3. Layer hinzufügen → Bearbeitungsmodus → zeichnen/ändern → Layer-Änderungen speichern. QGIS schickt Content-Crs mit; die API rechnet in PostGIS um (EPSG:25833 ↔ CRS84).

Wer nur seine Verfahren sieht: Vorsitz, Kassenbetreuer und Stellvertreter der TG; Verbandsmitarbeiter alle (?vkz= in /ogc/collections grenzt ein).

Mit dem Plugin

Knopf „Layer laden" in der Seitenleiste: legt nach dem Login eine QGIS-Authentifizierungskonfiguration API-Header (X-API-Key) an — das Passwort bleibt draußen —, holt GET /ogc/collections?vkz=… und hängt jede Sammlung als OAPIF-Layer in eine Gruppe „VLN " (Untergruppen Vorerhebung / Maßnahmenblätter / Verfahren). Code: vln_karten/ogc_layers.py.

from qgis.core import QgsDataSourceUri, QgsVectorLayer
uri = QgsDataSourceUri()
uri.setParam("url", "https://api.flurneuordnung-sachsen.de/v2/ogc")
uri.setParam("typename", "p41-27998-flaeche")
uri.setParam("pagingEnabled", "true")
uri.setParam("srsname", "EPSG:25833")
uri.setAuthConfigId("<authcfg-id>")          # APIHeader (X-API-Key) oder Basic
layer = QgsVectorLayer(uri.uri(False), "Plan 41 · 27998 · Flächen", "OAPIF")

Wenn das Laden quälend langsam ist

Nicht die API: ein System-Proxy (PAC-Datei), den macOS nicht ausführen kann, lässt jede Qt-Anfrage in einen Timeout laufen (~10 s statt 0,3 s) — QGIS wirkt dann hängend, obwohl der Server jede Anfrage mit 200 beantwortet. Prüfen und abschalten:

networksetup -getautoproxyurl Wi-Fi          # Enabled: Yes + unerreichbare .pac-Datei?
networksetup -setautoproxystate Wi-Fi off    # nur wenn im Netz kein Proxy nötig ist

In der QGIS-Meldungsleiste steht dann Execution of PAC script … failed (…-1002).

Stile (QML) — mitgeladen und zurückspeicherbar

Jede Sammlung hat einen Stil: GET …/collections/{cid}/style.qml. Der Server liefert den für die Sammlung gespeicherten Stil, sonst die Vorlage der Art (kas-*-flaeche gilt für die Karte alter Stand aller Verfahren), sonst einen erzeugten Standard — kategorisiert nach typ mit den Farben des Web-GIS (Wege dunkel, Wasser blau, Ökologie grün, Problemstellen rot).

  • Plugin: wendet den Stil beim Laden automatisch an. Der Knopf „Stil des aktiven Layers speichern“ in der Seitenleiste schreibt den in QGIS eingestellten Stil zurück; mit dem Häkchen „als Vorlage für alle Verfahren“ (oder gedrückter Umschalttaste) gilt er für die ganze Art.
  • Ohne Plugin: Layer → Eigenschaften → Stil → Stil laden … bzw. die QML-Datei von der URL holen. Zurückschreiben geht per PUT (z. B. curl -T stil.qml -H "X-API-Key: …").
  • Berechtigung: Lesen darf jeder, der die Sammlung sieht. Speichern und Löschen nur Verbandsmitarbeiter oder Mitglieder der Gruppe MODUL_KARTENSTIL; sonst HTTP 403. Ob der eigene Zugang darf, steht in der Sammlung als vln:styleWritable und in der Kopfzeile X-Stil-Schreibbar der QML-Antwort.
GET    /ogc/collections/{cid}/style.qml             Stil (Kopfzeile X-Stil-Quelle: sammlung|vorlage|standard)
PUT    /ogc/collections/{cid}/style.qml[?vorlage=1] Stil speichern (Gruppe MODUL_KARTENSTIL)
DELETE /ogc/collections/{cid}/style.qml[?vorlage=1] gespeicherten Stil entfernen

Regeln beim Schreiben

  • Geometriefamilie: in …-punkt nur Punkte, in …-linie nur Linien, in …-flaeche nur Flächen (sonst HTTP 400).
  • Verfahrens-Layer: Änderungen landen in place im jüngsten Stand; fehlt ein Stand, legt der erste Schreibzugriff ihn an. Das Web-GIS speichert weiterhin als neuer Stand — beide treffen sich am jüngsten.
  • Vorerhebung: nur Status „bearbeitung" ist schreibbar (sonst HTTP 423).
  • Maßnahmenblatt: fid = <Blatt-UUID>:<n>. Neues Feature mit blatt_uuid hängt eine Geometrie an ein Blatt; mit blatt_typ (pflanzung|wasserbau|wegebau|entsiegelung) + blatt_name (+ blatt_mkz) entsteht ein neues Blatt. blatt_*-Attribute ändern die Blatt-Spalten, alles andere die Geometrie-Eigenschaften.
  • Fehler kommen als RFC 7807 (application/problem+json); ohne Anmeldung 401 mit WWW-Authenticate: Basic — QGIS fragt dann selbst nach dem Login.
GET  /ogc                          Landing Page (öffentlich)
GET  /ogc/conformance              Konformitätsklassen (öffentlich)
GET  /ogc/collections[?vkz=]       Sammlungen des Nutzers
GET  /ogc/collections/{cid}        Sammlung: crs, storageCrs, extent
GET  /ogc/collections/{cid}/queryables
GET  /ogc/collections/{cid}/items  ?limit&offset&bbox&bbox-crs&crs   → FeatureCollection
GET  /ogc/collections/{cid}/items/{fid}
POST /ogc/collections/{cid}/items          Feature | FeatureCollection → 201 {ids}
PUT  /ogc/collections/{cid}/items/{fid}    → 204
PATCH /ogc/collections/{cid}/items/{fid}   Eigenschaften mergen, Geometrie optional → 204
DELETE /ogc/collections/{cid}/items/{fid}  → 204
CRS-URIs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 (Vorgabe),
          http://www.opengis.net/def/crs/EPSG/0/25833

Verifiziert mit QGIS 4.2 (headless)

tools/headless_ogc_test.py treibt das installierte QGIS ohne Oberfläche (PyQGIS, QT_QPA_PLATFORM=offscreen) gegen die API: Layer laden, Feature anlegen, Attribut und Geometrie ändern, löschen — für einen Verfahrens-Layer und ein Maßnahmenblatt, jeder Schritt per API gegengeprüft. Stand 07.09.2026: 0 Fehler mit QGIS 4.2.0. Was QGIS dafür vom Server braucht (und die API liefert): service-desc-Link auf ein OpenAPI-Dokument, Part-5-Schema für Felder/Geometrietyp, OPTIONS …/items mit Allow, PATCH als JSON Merge Patch.

Das Beispiel-Plugin

Das Plugin macht den kompletten Arbeitsablauf in der QGIS-Oberfläche erlebbar:

  • Login beim Start: ohne gespeicherten API-Key zeigt das Plugin nach dem QGIS-Start gleich den Anmeldedialog (E-Mail/Passwort). Der Login liefert das userauth-Token; das Plugin speichert nur dieses Token (QSettings vln_karten/api_key), nie das Passwort, und nutzt es in jeder weiteren Sitzung. „Abmelden“ löscht es.
  • Einmaliger Login (E-Mail/Passwort) — der API-Key wird in den QGIS-Einstellungen gespeichert und über QGIS-Neustarts hinweg wiederverwendet; bei 401/403 öffnet sich der Anmeldedialog erneut.
  • Seitenleiste rechts (andockbar, seit 08.09.2026 anstelle der Werkzeugleiste): Anmeldung, Verfahrensliste aus GET /tgen mit Suchfeld, Lade- und Speicherknöpfe, Statuszeile. Wieder einblenden über Web → VLN Karten → Seitenleiste anzeigen.
  • Verfahrens-Auswahl gemerkt je QGIS-Projektdatei.
  • Speichern: über die OGC API schreibt QGIS jede Änderung einzeln zurück (Part 4); der Knopf „Änderungen speichern" schließt die offenen Bearbeitungssitzungen aller geladenen VLN-Layer auf einmal. Der frühere GeoJSON-Weg (ganze Layer laden und als Ganzes hochladen) ist am 08.09.2026 entfallen — samt layer_manager.py und den beiden Client-Methoden dafür.
  • Ladezeit: jede Sammlung kostet beim Anlegen rund eine halbe Sekunde — der OAPIF-Provider fragt Sammlung, Schema, Stil, OPTIONS und die erste Seite ab, nacheinander und blockierend. Zwei Gegenmittel: vorwaermen() holt vorab alle diese Antworten gleichzeitig (unter 1,5 s), und die Häkchen in der Seitenleiste laden nur die gewünschten Gruppen. Gemessen am 08.09.2026 (Berbisdorf, 27 Sammlungen): 20,5 s ohne, 14,2 s mit Vorwärmen; nur Verfahren 11,1 s (21 Layer), nur Vorerhebung oder nur Maßnahmenblätter je 1,6 s (3 Layer). Nicht parallel: ein in einem Python-Thread gebauter OAPIF-Layer stürzt beim ersten getFeatures() ab (Segfault, gemessen) — der Provider gehört dem Thread, der ihn erzeugt hat.
Datei Zweck
vln_karten/plugin.py Ablaufsteuerung: Anmeldung, Laden, Speichern, Hochladen, DATASETS-Registry
vln_karten/panel.py Seitenleiste (Anmeldung, Verfahrensfilter, Knöpfe, Statuszeile)
vln_karten/api_client.py HTTP-Client: Login, Paging-Loader, PUT, RFC-7807-Fehler
vln_karten/ogc_layers.py OGC API Features: Auth-Konfiguration (API-Header), Sammlungen → OAPIF-Layer
vln_karten/login_dialog.py Anmeldedialog
vln_karten/metadata.txt QGIS-Plugin-Metadaten

Installation

Voraussetzung: QGIS 3.22+ oder QGIS 4 (Qt6/PyQt6 — der Code nutzt scoped Enums und exec()), plus ein VLN-Manager-Konto.

Für Anwender: über das VLN-Plugin-Verzeichnis

Erweiterungen → Erweiterungen verwalten → Einstellungen → Hinzufügen, Name „VLN“, Adresse:

https://api.flurneuordnung-sachsen.de/v2/qgis/plugins.xml

Danach unter Alle nach „VLN Karten“ suchen und installieren (experimentelle Erweiterungen zulassen). Neue Fassungen bietet QGIS von sich aus an. Das Paket baut python3 tools/baue_paket.py; ZIP und plugins.xml gehören danach in den Manager nach api/qgis/.

Zum Entwickeln: Ordner verlinken

git clone https://entwicklung.flurneuordnung-sachsen.de/VLN_SN/API_Karte_QGISDemo.git
cd API_Karte_QGISDemo

# macOS, QGIS 4 (bei QGIS 3: "QGIS4" durch "QGIS3" ersetzen):
ln -s "$PWD/vln_karten" \
  ~/Library/Application\ Support/QGIS/QGIS4/profiles/default/python/plugins/vln_karten

# Linux:   ~/.local/share/QGIS/QGIS4/profiles/default/python/plugins/
# Windows: %APPDATA%\QGIS\QGIS4\profiles\default\python\plugins\

Dann QGIS starten und unter Erweiterungen → Erweiterungen verwalten „VLN Karten“ aktivieren (experimentelle Erweiterungen zulassen). Beim Entwickeln hilft das Plugin Plugin Reloader — Codeänderungen wirken dann ohne QGIS-Neustart.

Verkehrszeichen und Widmungen

Die Sammlungen widmung-<vkz>-punkt (Schilder) und -linie (Widmungen) führen den Bestand des Verfahrens; blattzeichen-<vkz> führt die neuen Zeichen am Weg eines Maßnahmenblatts (sie liegen im Blatt, ein neues Zeichen braucht darum blatt_uuid). Beides ist über die OGC API voll bearbeitbar.

Den Stil liefert der Server fertig: Schildgrafik statt Kreis, gedreht nach drehung, Größe nach groesse, bis zu zwei Zusatzzeichen darunter; Widmungslinien nach Straßenklasse eingefärbt, Strichmuster nach Zustand, Änderungen seitlich versetzt. Ab 1:2500 wird aus dem Schild eine Marke, ab 1:10000 ein Punkt — sonst liegt über einem großen Verfahren nur noch ein Haufen.

Die Grafiken kommen von der API (/v2/zeichen/svg/<code>.svg, Katalog unter /v2/zeichen/katalog.json, beides öffentlich). Das Plugin legt sie beim Laden im QGIS-Profil ab (<Profil>/svg/vln/) und schreibt den Stil auf diesen Ordner um; fehlt eine Datei, bleibt die Serveradresse stehen.

Gesetzt wird wie im Web: in der Seitenleiste unter Verkehrszeichen setzen ein Schild wählen (Suche nach Nummer oder Name), dann Schild setzen einschalten — erster Klick Standort, zweiter Klick Blickrichtung. Zusatzzeichen hängt das gewählte Zusatzzeichen an ein vorhandenes Schild (höchstens zwei). Für Zeichen am Weg eines Blattes oben das Blatt auswählen; in der Verfahrenskarte bleibt die Auswahl leer. Zum Drehen vorhandener Schilder taugt auch das QGIS-Bordwerkzeug Punktsymbole drehen, weil der Stil die Drehung datendefiniert führt.

Hinweise für eigene Integrationen

  • QgsBlockingNetworkRequest statt requests/urllib verwenden — damit greifen Proxy- und Zertifikatseinstellungen aus QGIS.
  • Der API-Key wird im Beispiel unverschlüsselt in QSettings abgelegt; für Produktivcode bietet sich der QGIS-Authentifizierungsmanager an (QgsApplication.authManager()).
  • Requests laufen im Beispiel synchron (blockierend); für große Datensätze auf QgsNetworkAccessManager mit Tasks wechseln.
  • Features ohne Geometrie landen beim Laden in einem Tabellen-Layer „ohne Geometrie“; Geometrien werden beim Hochladen als Multi-Typen gesendet (ST_GeomFromGeoJSON akzeptiert beides).
S
Description
No description provided
Readme
428 KiB
Languages
Python 100%