Flurstücke aus den Liegenschaftskatastern aller deutschen Bundesländer (außer Bayern) über eine einheitliche OGC-API-Features-Schnittstelle. Der Dienst verbirgt, dass dahinter 15 verschiedene WFS mit vier Datenschemata, zwei Projektionen und unterschiedlichen Ausgabeformaten stecken.
Ausgabe ist immer GeoJSON in EPSG:4326 mit einem festen Attributsatz.
15 von 16 Bundesländern sind angebunden und liefern nachweislich Daten. Für Bayern ist kein kostenfreier Endpunkt konfiguriert: Der WFS über GeodatenOnline ist gebührenpflichtig; der INSPIRE-Downloaddienst ist der aussichtsreichste Kandidat und noch zu prüfen.
cp .env.example .env # optional, alle Werte haben Vorgaben
docker compose up -dDanach liegt der Dienst auf http://127.0.0.1:8080; ein anderer Port geht über
ALKIS_PORT in der .env. Der Cache läuft eingebettet im Dienst selbst
(redb, eine Datei unter /data im Container)
Das Image ist zweistufig gebaut: statisch gegen musl übersetzt, dann in ein
distroless-Image gelegt. Es enthält nur die Binärdatei — knapp 9 MB, keine
Shell, kein Paketmanager, und der Prozess läuft als nonroot. Ein CA-Bundle
braucht es nicht: reqwest ist auf rustls mit gebündeltem Wurzelspeicher
eingestellt.
Weil im Image keine Shell steckt, hat es bewusst keine HEALTHCHECK-Anweisung.
GET /health prüft man von außen.
cargo build --release
ALKIS_BIND=127.0.0.1:8099 ALKIS_CACHE_PATH=./cache.redb ALKIS_CACHE_MAX_SIZE=2gb \
ALKIS_OSM_PBF_PATH=./data/baden-wuerttemberg-latest.osm.pbf \
./target/release/alkis-proxyDer Cache ist optional — ohne ALKIS_CACHE_PATH läuft der Dienst langsamer, aber
korrekt, ganz ohne weiteren Prozess. Ob er tatsächlich angebunden ist, sagt beim
Start die Zeile Cache angebunden; sonst steht dort eine Warnung. Die Datei
unter ALKIS_CACHE_PATH legt der Dienst selbst an, das Verzeichnis muss aber
existieren und beschreibbar sein.
ALKIS_OSM_PBF_PATH ist ebenso optional und aktiviert die zusätzliche
Collection flurstuecke-nutzungsart (siehe unten) —
ohne die Variable läuft der Dienst unverändert, ganz ohne die zweite Sammlung.
RUST_LOG=alkis_proxy=debug protokolliert jede eingehende Anfrage mitsamt
bbox und Client — das ist die Stelle, an der man sieht, welchen Ausschnitt
QGIS tatsächlich anfragt.
| Variable | Vorgabe | Bedeutung |
|---|---|---|
ALKIS_BIND |
0.0.0.0:8080 |
Adresse des HTTP-Servers |
ALKIS_CACHE_PATH |
– | Pfad der redb-Cache-Datei; fehlt er, läuft der Dienst ohne Cache |
ALKIS_CACHE_MAX_SIZE |
2gb |
Obergrenze der Cache-Datei (512mb, 32gb, oder Byte-Zahl); bei Überschreiten weichen die ältesten Kacheln |
ALKIS_UPSTREAM_TIMEOUT_SECS |
30 |
Zeitlimit je Landesdienst |
ALKIS_DEFAULT_LIMIT |
5000 |
Vorgabe für limit |
ALKIS_MAX_LIMIT |
5000 |
Obergrenze für limit |
ALKIS_PUBLIC_URL |
– | Nach außen sichtbare Basis-URL; nur nötig hinter einem Reverse Proxy |
ALKIS_DISABLE_COMPRESSION |
– | Gesetzt und nicht leer: keine Antwortkompression |
ALKIS_OSM_PBF_PATH |
– | Pfad einer lokalen OSM-PBF-Datei; aktiviert die optionale Collection flurstuecke-nutzungsart (siehe unten) |
RUST_LOG |
alkis_proxy=info |
Protokollierung |
Nur für docker compose, nicht vom Dienst selbst gelesen:
| Variable | Vorgabe | Bedeutung |
|---|---|---|
ALKIS_PORT |
8080 |
Port auf dem Host; im Container immer 8080 |
GET /collections/flurstuecke/items?bbox=<w>,<s>,<e>,<n>[&limit=][&offset=][&state=NW]
GET /collections/flurstuecke Beschreibung inkl. Quellen und Lizenzen
GET /collections/flurstuecke-nutzungsart/items?bbox=... nur mit ALKIS_OSM_PBF_PATH
GET /collections/flurstuecke-nutzungsart
GET /collections
GET /conformance Erfüllte Konformitätsklassen
GET /api API-Definition (OpenAPI 3.0)
GET /health Zustand, abgeschaltete Landesdienste, Nutzungsart-Index
GET /metrics Prometheus
bbox in WGS84, maximal etwa 8 km Kantenlänge. Ohne state bestimmt der
Dienst die zuständigen Bundesländer selbst.
Die Grenze ergibt sich aus der Kachelzerlegung: Eine Anfrage darf höchstens 64 Kacheln des 1-km-Rasters berühren. Geprüft wird im nativen CRS des jeweiligen Landes, also exakt so, wie gleich darauf abgerufen würde. Ein zu großer Ausschnitt liefert HTTP 400, keine leere Antwort.
Eine zweite Grenze gilt unabhängig von der Fläche: Enthält ein Ausschnitt mehr
Flurstücke als ALKIS_MAX_LIMIT (Vorgabe 5000), wird er ebenfalls mit HTTP
400 abgelehnt statt gekürzt geliefert. In dicht bebautem Gebiet reichen dafür
schon anderthalb Kilometer Kantenlänge — Stuttgart-Ost, 1,5 × 2,2 km: rund 5450
Flurstücke. Der Grund ist derselbe wie oben: Eine gekürzte Antwort trägt keinen
next-Link, weil der Dienst die fehlenden Flurstücke gar nicht kennt, und ist
damit von einer vollständigen nicht zu unterscheiden. Wer mehr auf einmal
braucht und den Speicher hat, setzt ALKIS_MAX_LIMIT hoch.
numberMatched nennt deshalb immer die vollständige Treffermenge — außer beim
Schema-Sample, das ohne bbox geliefert wird.
Zwei Wege, getestet mit QGIS 4.2.
- Layer → Datenquellenverwaltung → OGC API - Features
- Neu → Name frei wählen, URL
http://127.0.0.1:8099→ OK → Verbinden - Sammlung
Flurstücke (ALKIS)auswählen → Hinzufügen
Setze das Projekt-Koordinatensystem auf EPSG:4326, damit die Flächen sichtbar werden.
Ist der Kartenausschnitt größer als etwa 8 km Kantengröße — oder enthält er mehr
als ALKIS_MAX_LIMIT Flurstücke —, antwortet der Dienst mit HTTP 400. Damit
QGIS erst gar nicht so weit anfragt, sollte am Layer eine maßstabsabhängige
Sichtbarkeit gesetzt sein:
Layereigenschaften → Darstellung → Maßstabsabhängige Sichtbarkeit, Minimum etwa 1:50.000 — in Innenstädten eher 1:10.000, sonst greift die Flurstücksgrenze.
Ohne diese Einstellung erscheint beim Herauszoomen eine Fehlermeldung in der Meldungsleiste. Das ist unschön, aber harmlos — und deutlich besser als die Alternative, siehe den nächsten Abschnitt.
Für ein festes Projektgebiet genügt ein Vektorlayer mit der Item-URL:
http://127.0.0.1:8099/collections/flurstuecke/items?bbox=9.176,48.774,9.180,48.778&limit=2000
Layer → Layer hinzufügen → Vektorlayer → Protokoll: HTTP(S) und diese URL eintragen. Lädt einmalig, dafür ohne Nachladen beim Zoomen.
{
"id": "NW:05495803101089______",
"properties": {
"parcelId": "05495803101089______", "parcelIdSource": "flstkennz",
"bundesland": "NW",
"gemarkungSchluessel": "054958", "gemarkungName": "Köln",
"flur": "31", "zaehler": "1089",
"nenner": null, "flurstuecksnummer": "1089",
"flaecheQm": 9.0,
"gemeindeSchluessel": "05315000", "gemeindeName": "Köln",
"kreisName": "Köln",
"lagebezeichnung": "Marspfortengasse 6",
"nutzung": "Fläche gemischter Nutzung",
"stand": "2013-02-22"
}
}Welche Felder in welchem Land belegt sind, welche Angaben in keinem Dienst enthalten sind und was die Adapter bewusst verwerfen, steht in DATENMODELL.md.
Die öffentlichen ALKIS-Daten enthalten aus Datenschutzgründen keine
Eigentümerangaben. flurstuecke-nutzungsart liefert dieselben Flurstücke wie
flurstuecke, ergänzt um eine geschätzte Nutzungsart — ausschließlich
aus OpenStreetMap abgeleitet, nie aus ALKIS-Zusatzquellen:
{
"properties": {
"...": "alle Felder von flurstuecke, unverändert",
"category": "privat",
"rule": "wohngebaeude",
"confidence": 0.6,
"conflict": null
}
}| Feld | Bedeutung |
|---|---|
category |
privat | oeffentlich | bahn | unbekannt |
rule |
Name der greifenden Regel (src/classification/rules.rs) |
confidence |
Konfidenz der Schätzung, 0.0–1.0 |
conflict |
Abweichende Kategorie einer anderen greifenden Regel, falls vorhanden |
Aktiviert wird die Collection über ALKIS_OSM_PBF_PATH, den Pfad einer
lokalen .osm.pbf-Datei (z. B. von Geofabrik).
Ohne diese Variable taucht flurstuecke-nutzungsart in /collections gar
nicht erst auf — der Dienst läuft unverändert weiter.
ALKIS_OSM_PBF_PATH=./baden-wuerttemberg-latest.osm.pbf ./target/release/alkis-proxyDie Datei wird beim Start einmalig in einen residenten Index geladen
(Fortschritt sichtbar im Log, Endzustand über GET /health → Feld
classification: aus | wird_indiziert | bereit). Jede Bbox-Anfrage
klassifiziert danach live gegen diesen Index — es wird nichts vorberechnet
oder zwischengespeichert. Bis der Index fertig ist, liefert die Collection die
Flurstücke bereits, aber ohne Nutzungsart, dazu eine Warnung im Feld
warnings der Antwort.
Straßen, Rad- und Fußwege sowie Gleise liegen in OSM als Achsen vor, Flurstücke sind Flächen. Übersetzt wird das über einen Deckungsgrad: Die Länge der Achse innerhalb des Flurstücks, multipliziert mit einer aus den Tags abgeleiteten Nennbreite, ergibt die beanspruchte Fläche; geteilt durch die Flurstücksfläche ist das der Anteil, den das Verkehrsnetz belegt.
Der Nenner ist also das Flurstück, nicht die Achse. Ein Straßenflurstück von
8 × 200 m, durch das eine highway=residential läuft (Nennbreite 6,5 m), kommt
auf 200 × 6,5 / 1600 = 0,81; eine Grundstückszufahrt, die 10 m weit in eine
Parzelle von 600 m² hineinreicht, auf 0,05.
Ab 0,5 gilt ein Flurstück als Verkehrsfläche — zusätzlich muss es aber
langgestreckt sein (Polsby-Popper-Kompaktheit 4πA/U² unter 0,25). Diese
zweite Bedingung kostet nichts und fängt den wichtigsten Fehlerfall ab: Die
OSM-Achsen sind gegenüber den ALKIS-Grenzen um einige Meter versetzt und können
dadurch längs durch eine schmale Nachbarparzelle laufen. Ein Straßenflurstück
liegt bei 0,12, eine Reihenhausparzelle bei 0,44. Erst ab einem Deckungsgrad
von 0,9 entfällt die Formbedingung, für Kreuzungs- und Wendehammerflächen.
Erkannte Verkehrsflächen tragen category: "oeffentlich"; welche Regel
gegriffen hat, steht in rule — strasse, weg, bahnstrecke oder
verkehrsflaeche (letztere für flächig gemappte Fußgängerzonen). Diese Regeln
stehen bewusst hinter den Gebäuderegeln: Ein Gebäude auf dem Flurstück
schließt eine Verkehrsfläche sicher aus und ist die dritte Absicherung gegen
denselben Versatz.
Nicht als Verkehrsfläche zählen Brücken und Tunnel (bridge/tunnel) — was
über oder unter einem Flurstück verläuft, sagt nichts über dessen Nutzung —,
Grundstückszufahrten (service=driveway, parking_aisle) und
Straßenbahngleise (railway=tram), die meist in der Fahrbahn liegen.
Mit dem Verkehrsnetz fällt deutlich mehr in den Index als zuvor: Für Baden-Württemberg kommen rund zwei Millionen Ways hinzu, gut 400 MB resident; deutschlandweit ist mit 2,5–3 GB zu rechnen. Ein Vorfilter mit osmium-tool reduziert Startzeit und Speicher erheblich — optional, aber empfohlen:
osmium tags-filter germany-latest.osm.pbf \
w/operator:type w/building=house,residential,detached,apartments,public,school \
w/amenity=townhall,public_building,courthouse,community_centre,school \
w/leisure=schoolyard,park,playground \
w/landuse=industrial,commercial,railway \
w/tourism=zoo,hotel \
w/railway=rail,light_rail,narrow_gauge \
w/highway=motorway,trunk,primary,secondary,tertiary,unclassified,residential,living_street,pedestrian,busway,service,motorway_link,trunk_link,primary_link,secondary_link,tertiary_link,footway,cycleway,path,track,bridleway,steps \
-o germany-filtered.osm.pbfBekannte Einschränkungen (siehe Code-Kommentare in src/classification/):
- Nennbreiten sind Schätzungen. Die Breite einer Verkehrsfläche wird aus
width,lanesoder der Straßenklasse abgeleitet und liegt damit auf etwa ±50 % genau. Für den Schwellwertvergleich reicht das, für eine Flächenberechnung nicht —confidenceist kein Flächenanteil im geometrischen Sinn. - Böschungen und Bankettflurstücke neben Autobahn oder Bahndamm enthalten
keine Achse und bleiben
unbekannt. Ein Puffer um die Achsen statt der Längenrechnung würde sie mit erfassen, kostet aber deutlich mehr. - Nur einfache Ways, keine Multipolygon-Relationen. Größere Parks und manche Landnutzungsflächen sind in OSM oft als Relation statt als Way gemappt und werden nicht erkannt.
- Kein Neuladen bei geänderter PBF-Datei zur Laufzeit — Neustart nötig.
- Die Klassifikation ist eine Heuristik mit Konfidenzwert, keine verlässliche Eigentumsfeststellung.
Die Daten gehören den Ländern. Elf der fünfzehn Quellen verlangen eine Namensnennung — wer die Flurstücke veröffentlicht, muss den Quellenvermerk der beteiligten Länder mitführen.
Abschreiben muss man ihn nicht: Jede items-Antwort trägt ein Feld
attribution mit genau den Ländern, aus denen ihre Flurstücke stammen.
curl -s "http://127.0.0.1:8080/collections/flurstuecke/items?bbox=9.176,48.774,9.180,48.778" \
| jq -r .attribution
# LGL-BW (2026) Datenlizenz Deutschland - Namensnennung - Version 2.0 (Daten bearbeitet)Lizenz und geforderten Vermerk je Land führt DATENMODELL.md auf.
Vier Quellschemata: AdV „ALKIS vereinfacht" (12 Länder),
INSPIRE (SH), Baden-Württemberg (nora), Berlin. Das Saarland liefert AVE
in Großbuchstaben, Bremen unter NAS-Feldnamen — beides deckt derselbe Adapter ab.
Native Abfrage statt EPSG:4326. Angefragt wird immer im UTM-CRS des Landes. Die Berechnung des CRS nach WGS84 erfolgt im Proxy.
Kachelbasierter Cache. Beliebige Bounding-Boxen wiederholen sich nie; der Cache arbeitet deshalb auf einem 1-km-Raster im nativen CRS. Antworten, die exakt am Abrufslimit liegen, gelten als abgeschnitten — solche Kacheln werden geviertelt, und diese Erkenntnis wird mitgespeichert.
cargo test # 108 Unit- + 8 Integrationstests
cargo run --example demo # Harmonisierung an echten Fixtures
cargo run --example live # alle Landesdienste live prüfenDie Fixtures unter tests/fixtures/ sind echte Antworten der Landesdienste.