GET /v1/terrain/elevation — Punktabfrage Geländehöhe
Liefert die Geländehöhe (DHHN2016, m über NHN) an einem WGS84-Punkt. Die API routet die Anfrage über einen PostGIS-Spatial-Index auf den zuständigen DGM1-Tile und sampelt das Pixel via GDAL/rasterio mit HTTP-Range-Request auf dem Cloud-Optimized GeoTIFF. Kein Bundesland ist hartcodiert — Punkte überall in Deutschland funktionieren, sofern Lodapi den DGM1-Layer für die zuständige Region ingested hat.
Wann verwenden
- Höhenkote für einen Adresspunkt holen (z.B. für PV-Konfigurator, Statik-Vorabprüfung).
- Karten-UI mit Hover-Tooltip „Höhe an Cursor-Position”.
- Geo-Pipeline: LoD2-Gebäude auf Geländehöhe normieren (Z-Differenz Gebäude-First-Floor – Terrain).
Wenn du viele Punkte entlang einer Linie sampeln willst → /v1/terrain/profile. Wenn du eine Fläche als Mesh brauchst → /v1/terrain-mesh/datasets (3D-Tiles-Mesh-Tilesets).
Examples
curl — Brandenburger Tor
curl -s 'https://api.lodapi.de/v1/terrain/elevation?lat=52.5163&lon=13.3777' | jq
{
"elevation_m": 34.95,
"datum": "DHHN2016",
"source_bl": "bb",
"snapshot": "2025-12-18",
"license": "dl-de-zero-2.0",
"attribution": "© SenStadt Berlin (DL-DE/Zero 2.0)",
"tile_id": "bb_33_388_5818"
}
Python — Adresse → Höhe
import httpx
def elevation_at(lat: float, lon: float) -> float | None:
r = httpx.get(
"https://api.lodapi.de/v1/terrain/elevation",
params={"lat": lat, "lon": lon},
)
if r.status_code == 404:
return None # außerhalb der Coverage oder NoData (Wasserfläche)
r.raise_for_status()
return r.json()["elevation_m"]
print(elevation_at(52.5163, 13.3777)) # → 34.95
TypeScript — Cesium-Integration
async function clampToGround(lon: number, lat: number) {
const url = new URL("https://api.lodapi.de/v1/terrain/elevation");
url.searchParams.set("lat", String(lat));
url.searchParams.set("lon", String(lon));
const r = await fetch(url);
if (r.status === 404) return null;
const { elevation_m } = await r.json();
return Cesium.Cartesian3.fromDegrees(lon, lat, elevation_m);
}
Parameters
| Parameter | In | Type | Required | Default | Range | Beschreibung |
|---|---|---|---|---|---|---|
lat | query | float | yes | — | −90..90 | WGS84-Breite (Grad) |
lon | query | float | yes | — | −180..180 | WGS84-Länge (Grad) |
srs | query | string | no | EPSG:4326 | — | Räumlicher Bezug. Aktuell nur EPSG:4326. |
Response
200 OK · application/json — Schema-Quelle openapi.json (#/components/schemas/ElevationResponse).
Felder:
| Feld | Beschreibung |
|---|---|
elevation_m | Höhe in Metern, DHHN2016, gerundet auf 3 Nachkommastellen |
datum | Vertikales Datum (immer DHHN2016 in Phase 1) |
source_bl | 2-Buchstaben-BL-Code des zuständigen Tile |
snapshot | Snapshot-Datum des DGM1-Quelldatensatzes (ISO 8601) |
license | Lizenz-ID (dl-de-zero-2.0, cc-by-4.0, dl-de-by-2.0) |
attribution | Pflicht-Attribution-String für UI-Anzeige |
tile_id | Eindeutige Tile-Kennung (Cache-Key oder Audit-Pfad) |
Fehler
| Status | Bedeutung | Bedingung |
|---|---|---|
422 | Unprocessable Entity | lat/lon außerhalb Range, srs nicht EPSG:4326 (FastAPI/Pydantic-Validation) |
404 | Not Found, kein Tile | Punkt außerhalb aller ingested Terrain-Coverages |
404 | Not Found, NoData | Tile gefunden, aber Pixel ist NoData (Wasser, Tile-Rand) |
502 | Bad Gateway | COG-Quelle nicht erreichbar (Pass-Through-Modus auf Behörden-Server) |
503 | Service Unavailable | rasterio nicht installiert (deploy issue) |
Alle Fehler sind im RFC-7807-Format (application/problem+json).
Stolperdrähte
- Wasserflächen liefern
404mit Hinweis-Text, nicht0.0. Mancher PV-Konfigurator interpretiert0als „Meereshöhe” — der bewusste 404 verhindert das. - Tile-Rand-Pixel werden 0.6 m einwärts gesampelt (siehe ADR-0009 §Edge-Cases) — sonst gibt es zwischen zwei benachbarten Tiles minimale Sprünge.
- CRS-Wechsel für östliche BL (BB/SN/MV/BE) erfolgt automatisch (UTM33N statt UTM32N). Aus Konsumentensicht: WGS84 rein, NHN raus.
source_blist die BL-Zuordnung des Datensatzes, nicht zwingend des Standorts. An Bundesländer-Grenzen kann ein Punkt im BB-Tile liegen, obwohl er administrativ noch zu BE gehört.
Live-Coverage
Stand 2026-06: alle 16 Bundesländer (16/16) via /v1/terrain/elevation. Aktuelles Inventar via /v1/terrain/datasets.
Verwandte Endpoints
GET /v1/terrain/datasets— Coverage + Snapshot-Stand.GET /v1/terrain/profile— Linien-Sampling für mehrere Punkte.