Nach einem Wechsel von Verfahren oder Landkreis blieb die geholte Liste stehen; "Ausgewaehlte laden" lud sie dann unter dem Namen der neuen Auswahl. Jetzt merkt sich die Seitenleiste, fuer welche Auswahl die Liste geholt wurde, und leert sie bei jedem Wechsel (auch beim stillen Wiederherstellen aus der Projektdatei). Ein Filter, der die Auswahl behaelt, laesst sie stehen. Die Meldung nach "Liste holen" nennt beim Landkreis den Landkreis statt "dieses Verfahren". Gefunden am 07.10.2026 bei der Aufnahme des Erklaervideos Linkliste. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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:
- 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). - 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. - 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)
-
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 ausPOST /person/loginbzw. einvlnk_…-Schlüssel aus meinVLN → API-Keys)
- Name:
-
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. -
Layer hinzufügen → Bearbeitungsmodus → zeichnen/ändern → Layer-Änderungen speichern. QGIS schickt
Content-Crsmit; 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 alsvln:styleWritableund in der KopfzeileX-Stil-Schreibbarder 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
…-punktnur Punkte, in…-linienur Linien, in…-flaechenur 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 mitblatt_uuidhängt eine Geometrie an ein Blatt; mitblatt_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 mitWWW-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 (QSettingsvln_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 öffnet sich der Anmeldedialog erneut. Ein 403 (angemeldet, aber nicht berechtigt — etwa „nur lesen“) zeigt seit 0.7.0 nur eine Meldung und meldet nicht ab.
- Seitenleiste rechts (andockbar, seit 08.09.2026 anstelle der Werkzeugleiste):
Anmeldung, Auswahl aus
GET /tgen/auswahlmit Suchfeld (oben die eigenen Landkreise für die Linkliste, dann die Verfahren; als oFB die des Landkreises mit „nur lesen“), 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.pyund den beiden Client-Methoden dafür. - Ladezeit: jede Sammlung kostet beim Anlegen rund eine halbe Sekunde — der
OAPIF-Provider fragt Sammlung, Schema, Stil,
OPTIONSund 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 erstengetFeatures()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 |
Geodienste aus der Linkliste
Die Linkliste pflegt der VLN-Manager (/linkliste.py in Ministeriums-, oFB- und meinVLN-Portal,
Doku docs/linkliste.md im Repo Erik/VLNManager): landesweit und vom Landkreis (oFB). Team
und Teilnehmergemeinschaft sehen sie nur; welche Dienste geladen werden, entscheidet man hier
mit den Häkchen (Erik, 06.10.2026).
- Liste holen ruft
GET /linkliste?vkz=…— für ein Verfahren oder, seit 0.7.0, für einen ganzen Landkreis (Eintrag „… — Linkliste des Landkreises“ oben in der Auswahl). Die Liste kommt fertig aufgelöst; je Zeile stehen Name, Gruppe › Untergruppe und Herkunft. Wechselt die Auswahl, leert sich die Liste (seit 0.7.1) — „Liste holen“ holt sie für die neue Auswahl. - Ausgewählte laden hängt die angehakten Dienste schrittweise unter „Geodienste “
ein: Gruppe › Untergruppe › Layer (
linkliste.zielgruppe); ohne Untergruppe direkt unter die Gruppe. Ein hinterlegter Stil kommt überGET /linkliste/{id}/style.qml. - Für Karte (seit 0.7.0): über der Liste je Karte ein Häkchen — Wege- und Gewässerplan,
Neugestaltungsgrundsätze, Wertermittlung (Katalog vom Server,
GET /linkliste/karten). Eine oder mehrere Karten gewählt: angehakt sind die Dienste, die der Landkreis bzw. die landesweite Pflege dieser Karte zugeordnet hat (linkliste.vorhaken); nachjustieren von Hand bleibt möglich. - Anbieter: WMS (
QgsRasterLayer), WFS, ArcGIS REST (FeatureServer) und GeoJSON/OGR. - Nur lesen: eine oFB sieht die Verfahren ihres Landkreises; deren OGC-Sammlungen tragen
vln:schreibbar: falseund werden schreibgeschützt geladen (layer.setReadOnly(True)).
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
QgsBlockingNetworkRequeststattrequests/urllibverwenden — 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
QgsNetworkAccessManagermit Tasks wechseln. - Features ohne Geometrie landen beim Laden in einem Tabellen-Layer
„ohne Geometrie“; Geometrien werden beim Hochladen als Multi-Typen
gesendet (
ST_GeomFromGeoJSONakzeptiert beides).