Files
Projektwissen/docs/wissen/wfs_dienste.md
T

138 lines
6.9 KiB
Markdown
Raw Normal View History

# WFS-Dienste (ALKIS Sachsen)
Lebendes Dokument. Was die Plugins an WFS-Diensten ansprechen, wie sich diese Dienste **tatsächlich**
verhalten (nicht wie die Norm es vorsieht) und welche Anfragen erprobt funktionieren.
Betrifft ausschließlich **`sn_verfahrensgebiet`** (`functions/*_alkis*.py`). `sn_plan41` nutzt kein ALKIS/WFS,
sondern die VLN-Karten-API. Objektarten und Attribute im Detail: [`alkis_objektarten.md`](alkis_objektarten.md).
Quelle der Erkenntnisse: Sondierungsskripte in `referenz/wfs/` (nicht versioniert) und der Code selbst.
## Die drei Endpunkte
| Kürzel im Code | URL | WFS-Version | Geometrie | Verwendung |
|---|---|---|---|---|
| „vereinfacht" (`ALKIS_VEREINF_URL`) | `https://geodienste.sachsen.de/aaa/public_alkis/vereinf/wfs` | 2.0.0 | ja | `ave:KatasterBezirk` → Gemarkungen mit Fläche |
| „SF" (`ALKIS_SF_URL`) | `https://geodienste.sachsen.de/aaa/public_alkis/sf/wfs` | 2.0.0 | teils | `adv:AX_Flurstueck`, `adv:AX_BauRaumOderBodenordnungsrecht`, `adv:AX_PunktortTA`, `adv:AX_Grenzpunkt` |
| „NAS" (nur erwähnt) | – | – | – | `adv:AX_Gemarkung` hätte **keine** Geometrie, deshalb wird stattdessen `ave:KatasterBezirk` genommen |
CRS durchgängig **EPSG:25833** (ETRS89 / UTM 33N). In KVP-Parametern als `urn:ogc:def:crs:EPSG::25833`,
in der QGIS-WFS-URI als `srsname='EPSG:25833'`.
## Was der Dienst blockiert – die harten Grenzen
Ein Web Application Firewall vor `geodienste.sachsen.de` schränkt den Dienst gegenüber der WFS-2.0-Norm ein:
| Was | Verhalten | Beleg |
|---|---|---|
| **POST** (`GetFeature` als XML-Body) | blockiert | `referenz/wfs/_test_wfs_post.py` |
| **`FILTER`-XML mit GML-`Envelope`/`BBOX`** in der GET-URL | blockiert | `referenz/wfs/_test_wfs_bbox.py`, `grenzpunkt_alkis.py` Kopf |
| **KVP-Parameter `BBOX=minx,miny,maxx,maxy,crs`** | **funktioniert** – so wird PunktortTA räumlich gefiltert | `_punktort_ta_bbox_param()` |
| **`FILTER` mit `fes:ResourceId rid="…"`** in der GET-URL | funktioniert (`ImplementsResourceId=TRUE`) | `_test_wfs_get.py`, Grenzpunkt-Batches |
| **URL-Länge** | ~8192 Bytes; darüber 414/Abbruch | Batchgröße 100 IDs (~54 Byte/ID URL-kodiert) |
| **`COUNT`** | Serverlimit, im Code auf 20 000 gesetzt (`_MAX_FEATURES`) | `grenzpunkt_alkis.py` |
| **Serverseitiger Filter auf `gehoertZu`/`istTeilVon`** | nicht möglich (`ErrorInPropertyName`) | `grenzpunkt_alkis.py` Kopf |
| **`AX_Grenzpunkt` per BBOX** | nicht möglich – Objektart ohne eigene Geometrie | siehe unten |
Folgerung: **Nicht „vereinfachen", ohne gegen den Dienst zu testen.** Die scheinbar umständlichen
Mehrschritt-Strategien (BBOX → PunktortTA → GML-IDs → Grenzpunkt-Batches → Client-Join; Flurstücke
gemarkungsweise einzeln) sind die Antwort auf genau diese Grenzen.
## Anfragen je Objektart (erprobt)
### `ave:KatasterBezirk` (vereinfachter Dienst) → Gemarkungen
QGIS-WFS-Layer, **ohne** serverseitigen Filter (Dienst kann es nicht), danach Client-seitige Verschneidung
per `QgsGeometry.intersects` mit der Verfahrensgebiets-Geometrie:
```
url='https://geodienste.sachsen.de/aaa/public_alkis/vereinf/wfs'
typename='ave:KatasterBezirk' srsname='EPSG:25833'
```
Attribute: `schluessel` (voller Katasterbezirksschlüssel, **letzte 4 Stellen = Gemarkungsnummer**),
`ave_name` (Bezeichnung). Sachsen hat ≈ 2 000 Katasterbezirke – der Komplettabruf ist vertretbar.
### `adv:AX_BauRaumOderBodenordnungsrecht` (SF) → Verfahrensgebiet
Serverseitiger Attributfilter über die QGIS-WFS-URI (`sql=`):
```
url='…/sf/wfs' typename='adv:AX_BauRaumOderBodenordnungsrecht' srsname='EPSG:25833'
sql=SELECT * FROM AX_BauRaumOderBodenordnungsrecht WHERE bezeichnung LIKE '<vkz5>%'
```
`<vkz5>` = die **ersten 5 Stellen** der Verfahrenskennziffer (`get_variable("verfahrensnummer")[:5]`);
die Daten im Dienst führen teils abweichende Suffixe. Geladene Attribute:
`artderfestlegung`, `ausfuehrendestelle_ax_dienststelle_schluessel_stelle`, `adv_name`, `bezeichnung`,
`datumanordnung`, `datumbesitzeinweisung`, `datumrechtskraft`, `datumabgabe`.
Gruppierung im Plugin nach `(adv_name, bezeichnung)`.
### `adv:AX_Flurstueck` (SF)
**Eine WFS-Anfrage pro Gemarkung** (Sammelabfrage mit mehreren Nummern läuft ins Feature-Limit),
Ergebnisse in einen Memory-Layer zusammengeführt:
```
url='…/sf/wfs' typename='adv:AX_Flurstueck' srsname='EPSG:25833'
sql=SELECT * FROM AX_Flurstueck
WHERE gemarkung_ax_gemarkung_schluessel_gemarkungsnummer = '<gemarkungsnummer4>'
```
### `adv:AX_PunktortTA` (SF) → Trägerpunkt für Grenzpunkte
Roh-GET mit KVP-`BBOX`, GML-Antwort wird selbst geparst (`xml.etree`):
```
SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature
&TYPENAMES=adv:AX_PunktortTA
&SRSNAME=urn:ogc:def:crs:EPSG::25833
&COUNT=20000
&BBOX=<minx>,<miny>,<maxx>,<maxy>,urn:ogc:def:crs:EPSG::25833
```
Geometrieattribut `position`. Referenz auf den Grenzpunkt: Feldname je NAS-Version unterschiedlich –
Kandidaten in dieser Reihenfolge geprüft: `istTeilVon` (NAS 6.x), `gehoertZu`, `gehoertZuAX_Grenzpunkt`.
Wert kommt als `xlink:href` (`urn:adv:oid:DE…`) oder flach. Optional wird
`adv:qualitaetsangaben/adv:genauigkeitsstufe` in das Feld
`qualitaetsangaben_ax_dqpunktort_genauigkeitsstufe` gezogen.
### `adv:AX_Grenzpunkt` (SF)
Kein BBOX (ZUSO ohne eigene Geometrie). Abruf über die aus PunktortTA gewonnenen GML-IDs,
**Batches à 100**, GET mit `ResourceId`-Filter:
```
SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature
&TYPENAMES=adv:AX_Grenzpunkt&COUNT=20000
&FILTER=<fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">
<fes:ResourceId rid="DE…"/> … (bis zu 100)
</fes:Filter>
```
Danach Client-seitiger Join `PunktortTA.<referenzfeld>` ↔ `AX_Grenzpunkt.gml_id`; Grenzpunkt-Attribute
im Ergebnis mit Präfix `gp_`.
## Fehlerantworten, die im Betrieb auftauchen
| Antwort | Bedeutung | Reaktion im Code |
|---|---|---|
| HTTP 403 / HTML-Seite statt XML | WAF hat POST oder GML-XML-Filter geblockt | auf GET + KVP/ResourceId ausweichen |
| `ExceptionReport` mit `ErrorInPropertyName` | Attribut ist nicht serverseitig filterbar | Attribut client-seitig filtern |
| gültiger Layer, `featureCount() == 0` | Dienst liefert ohne (unterstützten) Filter nichts | Diagnosemeldung, leere Liste, manueller Fallback |
| HTTP 414 | URL zu lang | Batchgröße senken |
## Offene Punkte
- Die vier Skripte in `referenz/wfs/` sind Wegwerf-Sondierungen. Wenn Bedarf besteht, sie regelmäßig
laufen zu lassen (Dienst-Regression erkennen), gehören sie als `scripts/wfs_probe.py` ins Projektwissen –
mit Netzkennzeichnung, kein Unittest.
- `ave:KatasterBezirk` vs. Schreibweise `Katasterbezirk`/`KatasterBezirk`: im Code uneinheitlich
dokumentiert, maßgeblich ist der Wert in `gemarkung_alkis.py` (`ave:KatasterBezirk`).
## Siehe auch
- [`alkis_objektarten.md`](alkis_objektarten.md) – Objektarten und Attribute
- [`externe_dienste.md`](externe_dienste.md) – alle externen Dienste im Überblick
- [`fallstricke.md`](fallstricke.md) – Abschnitt „Daten und Dienste"
- `referenz/alkis/` (lokal, nicht versioniert) – OK-AAA 7.1.2, ALKIS-FSK 2.1.0