# 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 '%' ``` `` = 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 = '' ``` ### `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=,,,,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= … (bis zu 100) ``` Danach Client-seitiger Join `PunktortTA.` ↔ `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