README auf Demo-Charakter ausgerichtet
Das Repo ist als Beispiel für die Ansteuerung der Karten-API aus QGIS gedacht: README beginnt jetzt mit einem PyQGIS-Minimalbeispiel (Login -> GET -> PUT), benennt die drei Stolpersteine (EPSG:25833, gemischte Geometrietypen, PUT ersetzt alles) und führt das Plugin als Referenz-Implementierung ein. metadata.txt-Beschreibung entsprechend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,94 +1,69 @@
|
||||
# VLN Karten — QGIS-Plugin für die Karten-API des VLN Managers
|
||||
# API_Karte_QGISDemo — Beispiel: Karten-API des VLN Managers aus QGIS ansteuern
|
||||
|
||||
QGIS-Plugin, das die Karten-Layer des [VLN Managers](https://www.vlnsachsen.de)
|
||||
direkt in QGIS bearbeitbar macht. Es liest und schreibt dieselben
|
||||
Datenbank-Daten (`KARTE_OBJEKT`), die auch das Web-GIS
|
||||
(karte.flurneuordnung-sachsen.de) über die `/maps/*`-Endpunkte nutzt.
|
||||
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 Datenbank-Daten
|
||||
(`KARTE_OBJEKT`), die auch das Web-GIS
|
||||
(karte.flurneuordnung-sachsen.de) verwendet.
|
||||
|
||||
## Funktionen
|
||||
Der Code ist bewusst als **Referenz-Implementierung** gehalten — wer
|
||||
eine eigene Integration (Plugin, Skript, Processing-Werkzeug) bauen
|
||||
will, findet hier alle Bausteine.
|
||||
|
||||
- **Einmaliger Login** mit dem VLN-Manager-Konto (E-Mail/Passwort).
|
||||
Der zurückgelieferte API-Key wird in den QGIS-Einstellungen gespeichert
|
||||
und bei jedem QGIS-Start automatisch wiederverwendet; erst bei
|
||||
HTTP 401/403 erscheint der Anmeldedialog erneut.
|
||||
- **Verfahrens-Auswahl in der Toolbar** — alle Teilnehmergemeinschaften
|
||||
aus `GET /tgen` als „VKZ — Name“. Die Auswahl wird in der Projektdatei
|
||||
gespeichert und beim Öffnen des Projekts wiederhergestellt.
|
||||
- **Vier Layer je Verfahren laden:**
|
||||
## Die Ansteuerung in Kürze
|
||||
|
||||
| Button | API-Layer | DB-Art |
|
||||
|---|---|---|
|
||||
| Verfahrensgebiet | `umringe` | `UMRING` |
|
||||
| Plan 41 (Wege- und Gewässerplan) | `p41` | `P41` |
|
||||
| Karte alter Stand | `kas` | `KAS` |
|
||||
| Wertermittlung | `we` | `WE` |
|
||||
So sieht der minimale Ablauf in PyQGIS aus (vollständige, robuste
|
||||
Fassung in [vln_karten/api_client.py](vln_karten/api_client.py)):
|
||||
|
||||
Geladen wird **vollständig** über den Listen-Endpunkt mit
|
||||
limit/offset-Paging — auch bei mehr als 2000 Objekten.
|
||||
- **Gemischte Geometrietypen:** Die API liefert Punkte, Linien und
|
||||
Flächen in einer FeatureCollection. Beim Laden wird nach
|
||||
Geometrie-Familie in bis zu drei Memory-Layer gesplittet
|
||||
(Single-Typen werden zu Multi-Typen befördert); bei nur einem Typ
|
||||
entsteht ein einzelner Layer.
|
||||
- **Hochladen:** „Aktiven Layer hochladen“ vereint alle Teil-Layer
|
||||
desselben Datensatzes (Punkte + Linien + Flächen derselben VKZ) wieder
|
||||
zu einer FeatureCollection und schreibt sie per `PUT` zurück.
|
||||
Vorher: Commit offener Bearbeitungssitzungen und Sicherheitsabfrage
|
||||
mit Auflistung der beteiligten Layer.
|
||||
- **Schutz vor Duplikaten:** Erneutes Laden eines bereits geladenen
|
||||
Datensatzes ersetzt die vorhandenen Layer nach Rückfrage, statt sie zu
|
||||
stapeln. Liefert der Server 0 Objekte, wird das deutlich gemeldet und
|
||||
ein leerer Layer zum Digitalisieren angelegt.
|
||||
```python
|
||||
import json
|
||||
from qgis.PyQt.QtCore import QUrl
|
||||
from qgis.PyQt.QtNetwork import QNetworkRequest
|
||||
from qgis.core import QgsBlockingNetworkRequest
|
||||
|
||||
## Voraussetzungen
|
||||
BASE = "https://api.flurneuordnung-sachsen.de/v2"
|
||||
|
||||
- QGIS **3.22 oder neuer**, einschließlich **QGIS 4** (Qt6/PyQt6 —
|
||||
der Code verwendet durchgehend scoped Enums und `exec()`).
|
||||
- Ein VLN-Manager-Konto mit Zugriff auf die Karten-API.
|
||||
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")
|
||||
|
||||
## Installation
|
||||
# 1) Login: liefert das userauth-Token = API-Key
|
||||
login = request("post", "/person/login",
|
||||
{"mail": "...", "password": "..."})
|
||||
api_key = login["data"]["userauth"]
|
||||
|
||||
Repository klonen und den Plugin-Ordner in das QGIS-Profil verlinken
|
||||
(alternativ kopieren), dann QGIS neu starten:
|
||||
# 2) Layer lesen: GeoJSON FeatureCollection in EPSG:25833
|
||||
fc = request("get", "/maps/p41?vkz=22017&limit=2000&offset=0",
|
||||
api_key=api_key)
|
||||
|
||||
```bash
|
||||
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\
|
||||
# 3) Layer schreiben: PUT ersetzt den kompletten Bestand dieser VKZ!
|
||||
request("put", "/maps/p41/22017", payload=fc, api_key=api_key)
|
||||
```
|
||||
|
||||
Anschließend in QGIS unter *Erweiterungen → Erweiterungen verwalten*
|
||||
„VLN Karten“ aktivieren (experimentelle Erweiterungen zulassen).
|
||||
Die drei Stolpersteine, die das Beispiel-Plugin jeweils sauber löst:
|
||||
|
||||
## Bedienung
|
||||
|
||||
1. **Anmelden …** in der Toolbar „VLN Karten“ (auch unter
|
||||
*Web → VLN Karten*): E-Mail und Passwort des VLN-Manager-Kontos.
|
||||
Das Passwort wird nicht gespeichert, nur der API-Key.
|
||||
2. **Verfahren wählen** in der Auswahlliste.
|
||||
3. **Layer laden**, in QGIS normal editieren (Memory-Layer,
|
||||
EPSG:25833), **hochladen**.
|
||||
|
||||
> ⚠️ Der `PUT` der API ersetzt den kompletten Layer-Bestand der
|
||||
> jeweiligen VKZ (versionierter Snapshot in `KARTE_SPEICHERSTAND`).
|
||||
> Deshalb lädt das Plugin immer alle Teil-Layer gemeinsam hoch — nur
|
||||
> einen Teil zu senden, würde die übrigen Geometrietypen serverseitig
|
||||
> löschen.
|
||||
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-Memory-Layer können
|
||||
das nicht. Das Plugin splittet beim Laden nach Geometrie-Familie
|
||||
und vereint beim Hochladen wieder ([vln_karten/layer_manager.py](vln_karten/layer_manager.py)).
|
||||
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-Vertrag
|
||||
|
||||
Basis-URL: `https://api.flurneuordnung-sachsen.de/v2`
|
||||
(fest hinterlegt als `DEFAULT_BASE_URL` in
|
||||
[vln_karten/api_client.py](vln_karten/api_client.py))
|
||||
|
||||
```
|
||||
POST /person/login
|
||||
Body: {"mail": "...", "password": "..."}
|
||||
@@ -97,49 +72,79 @@ POST /person/login
|
||||
|
||||
GET /tgen Verfahren/TGs (Auth nötig)
|
||||
|
||||
GET /maps/<layer>?vkz=&limit=&offset= Layer lesen (kas/we: Auth nötig)
|
||||
Antwort: GeoJSON FeatureCollection in EPSG:25833 (ETRS89/UTM33)
|
||||
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.
|
||||
```
|
||||
|
||||
Ein weiterer Layer der API (`st` = Servicetermin) wäre ein zusätzlicher
|
||||
Eintrag im `DATASETS`-Dict in [vln_karten/plugin.py](vln_karten/plugin.py).
|
||||
## Das Beispiel-Plugin
|
||||
|
||||
## Aufbau
|
||||
Das Plugin macht den kompletten Arbeitsablauf in der QGIS-Oberfläche
|
||||
erlebbar:
|
||||
|
||||
- **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.
|
||||
- **Verfahrens-Auswahl in der Toolbar** (aus `GET /tgen`), gemerkt je
|
||||
QGIS-Projektdatei.
|
||||
- **Buttons je Layer:** Verfahrensgebiet (`umringe`), Plan 41 (`p41`),
|
||||
Karte alter Stand (`kas`), Wertermittlung (`we`) — vollständig
|
||||
geladen über den Listen-Endpunkt mit Paging. Weitere Layer (z. B.
|
||||
`st`) sind je ein Eintrag mehr im `DATASETS`-Dict in
|
||||
[vln_karten/plugin.py](vln_karten/plugin.py).
|
||||
- **„Aktiven Layer hochladen“** vereint alle Teil-Layer des Datensatzes
|
||||
und schreibt per PUT zurück (mit Sicherheitsabfrage).
|
||||
- Erneutes Laden **ersetzt** vorhandene Layer nach Rückfrage, statt sie
|
||||
zu stapeln — sonst würde der nächste Upload alles doppelt senden.
|
||||
|
||||
| Datei | Zweck |
|
||||
|---|---|
|
||||
| [vln_karten/plugin.py](vln_karten/plugin.py) | Toolbar, Aktionen, Verfahrens-Auswahl, `DATASETS`-Registry |
|
||||
| [vln_karten/api_client.py](vln_karten/api_client.py) | HTTP-Client (Login, Paging-Loader, PUT) über `QgsBlockingNetworkRequest` |
|
||||
| [vln_karten/layer_manager.py](vln_karten/layer_manager.py) | GeoJSON ↔ Memory-Layer, Geometrie-Splitting, Layer-Zusammenführung |
|
||||
| [vln_karten/api_client.py](vln_karten/api_client.py) | HTTP-Client: Login, Paging-Loader, PUT, RFC-7807-Fehler |
|
||||
| [vln_karten/layer_manager.py](vln_karten/layer_manager.py) | GeoJSON ↔ Memory-Layer, Geometrie-Splitting/-Vereinigung |
|
||||
| [vln_karten/login_dialog.py](vln_karten/login_dialog.py) | Anmeldedialog |
|
||||
| [vln_karten/metadata.txt](vln_karten/metadata.txt) | QGIS-Plugin-Metadaten |
|
||||
|
||||
## Technische Hinweise
|
||||
## Installation (zum Ausprobieren)
|
||||
|
||||
- **CRS:** Die API liefert und erwartet Koordinaten in **EPSG:25833**
|
||||
(abweichend von RFC 7946). Import/Export laufen ohne
|
||||
WGS84-Transformation (`GEOJSON_CRS` in `layer_manager.py`).
|
||||
- **Geometrien** gehen beim Hochladen als Multi-Typen an die API
|
||||
(`ST_GeomFromGeoJSON` akzeptiert beides). Features ohne Geometrie
|
||||
landen in einem Tabellen-Layer „ohne Geometrie“.
|
||||
- **API-Key-Ablage:** unverschlüsselt in den QGIS-Einstellungen
|
||||
(QSettings, Gruppe `vln_karten`). Wer das härten will, verlagert ihn
|
||||
in den QGIS-Authentifizierungsmanager (`QgsApplication.authManager()`).
|
||||
- **Requests** laufen synchron (blockierend) — für sehr große
|
||||
Datensätze wäre `QgsNetworkAccessManager` mit Tasks der nächste Schritt.
|
||||
- Die gewählte VKZ liegt in der Projektdatei (`writeEntry`-Scope
|
||||
`vln_karten`) — verschiedene Projekte können verschiedene Verfahren
|
||||
vorausgewählt haben.
|
||||
Voraussetzung: QGIS **3.22+** oder **QGIS 4** (Qt6/PyQt6 — der Code
|
||||
nutzt scoped Enums und `exec()`), plus ein VLN-Manager-Konto.
|
||||
|
||||
## Entwicklung
|
||||
```bash
|
||||
git clone https://entwicklung.flurneuordnung-sachsen.de/VLN_SN/API_Karte_QGISDemo.git
|
||||
cd API_Karte_QGISDemo
|
||||
|
||||
Für schnelles Iterieren empfiehlt sich das Plugin **Plugin Reloader**
|
||||
aus dem offiziellen QGIS-Repository — Codeänderungen wirken dann ohne
|
||||
QGIS-Neustart. Der Plugin-Ordner kann dafür per Symlink direkt aus dem
|
||||
Git-Arbeitsverzeichnis eingebunden bleiben (siehe Installation).
|
||||
# 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.
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -3,8 +3,8 @@ name=VLN Karten
|
||||
qgisMinimumVersion=3.22
|
||||
qgisMaximumVersion=4.99
|
||||
supportsQt6=True
|
||||
description=Beispiel-Plugin: Verfahrensumringe, Wege- und Gewässerplan und Karte alter Stamm über die VLN Karten-API laden und zurückschreiben.
|
||||
about=Demonstriert Login (Benutzername/Passwort -> API-Key), Laden von GeoJSON-Daten in editierbare Memory-Layer und Zurückschreiben der Änderungen an die API.
|
||||
description=Beispiel: Karten-API des VLN Managers aus QGIS ansteuern — Verfahrensgebiet, Plan 41, Karte alter Stand und Wertermittlung laden und zurückschreiben.
|
||||
about=Referenz-Implementierung für die VLN-Manager-Karten-API: Login (mail/password -> userauth-API-Key), vollständiges Laden je VKZ mit Paging (EPSG:25833), Splitten gemischter Geometrietypen in Memory-Layer und vereintes Zurückschreiben per PUT.
|
||||
version=0.1.0
|
||||
author=Erik
|
||||
email=phpwelt@gmail.com
|
||||
|
||||
Reference in New Issue
Block a user