Skip to main content

Ergonomic Python SDK for the Lodapi REST API (LoD2 buildings, terrain, 3D tiles).

Project description

lodapi — Python SDK

Ergonomic Python client for the Lodapi REST API: federated LoD2 buildings across all 16 German Bundesländer, DGM terrain elevation, and 3D Tiles tileset discovery.

v0 / unstable. This is 0.0.1. The public surface may change before 0.1. Pin an exact version if you depend on it.

Install

pip install lodapi
pip install lodapi[geo]     # adds geopandas/shapely → .to_geodataframe()

Requires Python 3.10+.

Quickstart

from lodapi import Client

# api_key is optional — without one you use the anonymous free tier.
c = Client(api_key="lod_...")

# One page of buildings for a WGS84 bbox (minLon, minLat, maxLon, maxLat):
page = c.buildings(bbox=(7.0, 50.9, 7.1, 51.0), limit=500)
print(page.count, page.next_cursor)

# Transparent cursor pagination — iterate every feature across all pages:
for feature in c.buildings.iter(bbox=(7.0, 50.9, 7.1, 51.0)):
    ...

# Or page-by-page:
for p in c.buildings.pages(bbox=(7.0, 50.9, 7.1, 51.0)):
    ...

# Single building + its roof surfaces:
b = c.buildings.get("DEBBAL0100000001")
roof = c.buildings.roof("DEBBAL0100000001")

# Stream a binary GLB of the bbox to disk (or get bytes back with out=None):
c.buildings.glb(bbox=(13.40, 52.51, 13.41, 52.52), out="city.glb")

# Terrain:
h = c.terrain.elevation(lat=50.94, lon=7.05)         # ElevationResponse
prof = c.terrain.profile(coords=[(7.0, 50.9), (7.1, 51.0)], samples=100)
c.terrain.datasets()                                  # DGM raster datasets
c.terrain.mesh_datasets()                             # quantized-mesh tilesets

# Datasets + 3D Tiles:
c.datasets()                                          # LoD2 datasets per BL
c.tilesets(bbox=(6.93, 50.92, 7.02, 50.96))           # tilesets in a bbox
c.tilesets.get("nw-koeln")                            # one tileset
c.tilesets.tileset_json("nw-koeln")                   # raw 3D-Tiles root (dict)

c.close()   # or use `with Client(...) as c: ...`

There is no bl parameter — federation across the 16 Bundesländer is server-side and automatic. A bbox tuple (minLon, minLat, maxLon, maxLat) is serialized to the API's "minLon,minLat,maxLon,maxLat" string for you; you may also pass an already-formatted string.

Auth

Pass api_key="lod_..." to the constructor; it is sent as the X-API-Key header. The free tier is anonymous — omit the key entirely and calls still work. A malformed/unknown/revoked key raises AuthError on the first call.

Keys have the shape lod_ + 32 lowercase chars (see ADR-0014).

Soft quota

Per the Concierge-Billing MVP (ADR-0014), quota is soft — exceeding it is not an error. The server may return X-Lodapi-Quota-Used / -Limit headers; the SDK surfaces them after each call:

c.buildings(bbox=(...))
print(c.last_quota.used, c.last_quota.limit, c.last_quota.remaining)

Both fields are None for anonymous calls or keys without a configured quota.

GLB defaults

c.buildings.glb(...) applies a neutral, glTF-consumption-friendly default set for frame/origin/rotation. These differ from the raw REST-API defaults: the API itself does not rotate, while the SDK rotates Z-up → Y-up so the model stands upright out-of-the-box in Blender / three.js / model-viewer / Cesium (glTF is Y-up per spec).

param raw API default SDK default why the SDK differs
target_frame utm utm metric, scale-true (not MapLibre-bound)
origin center center model centred on the origin
rotate_x 0 -90 Z-up → Y-up for glTF-spec conformance
rotate_z 0 0 no extra spin around the vertical axis

All other GLB params (z_base, compression, colorize_roofs, merge_buildings, include_ground, weld_tolerance_m) pass through to the API's own defaults when omitted. Every default is overridable per call:

# Reproduce the scenerii-App view (MapLibre-aligned):
c.buildings.glb(
    bbox=(13.40, 52.51, 13.41, 52.52),
    target_frame="mercator", origin="corner", rotate_x=-90, rotate_z=-90,
)

# Get raw geospatial Z-up (no rotation, matches the raw API):
c.buildings.glb(bbox=(13.40, 52.51, 13.41, 52.52), rotate_x=0)

Errors

All API errors follow RFC 7807 application/problem+json (base https://lodapi.de/errors). The SDK parses the document onto a typed exception:

from lodapi import LodapiError, AuthError, NotFoundError, RateLimitError, ApiError

try:
    c.buildings.get("NOPE")
except NotFoundError as exc:
    print(exc.status, exc.title, exc.detail, exc.instance)
  • AuthError — 401
  • NotFoundError — 404
  • RateLimitError — 429 (reserved; quota is soft today)
  • ApiError — any other non-2xx
  • LodapiError — base class + transport/network errors

Transient 5xx and network errors are retried (default 2 retries, exponential backoff). 4xx are never retried.

GeoDataFrame support (geo extra)

With pip install lodapi[geo], building/roof FeatureCollections convert to a geopandas.GeoDataFrame (CRS EPSG:4326):

page = c.buildings(bbox=(7.0, 50.9, 7.1, 51.0))
gdf = page.to_geodataframe()      # this page's features
roof = c.buildings.roof("DEBBAL0100000001")
roof.to_geodataframe()

Without the extra, to_geodataframe() raises ImportError with an install hint; the rest of the SDK works fine.

Configuration

Client(
    api_key=None,                      # optional lod_* key
    base_url="https://api.lodapi.de",  # override for staging/local
    timeout=30.0,                      # per-request seconds
    max_retries=2,                     # transient 5xx/network only
)

Regenerating the response models

The response models in src/lodapi/models.py are hand-written, derived from the OpenAPI snapshot at 04_engineering/api/openapi/openapi.json (the live API serves it at GET /openapi.json). For ~11 endpoints this is leaner than full codegen.

When the API surface grows or churns:

  1. Refresh the snapshot (GET /openapi.json → the repo copy).

  2. Either edit models.py by hand against the new components.schemas, or adopt codegen:

    uvx openapi-python-client generate \
        --path 04_engineering/api/openapi/openapi.json
    

    and re-point resources/* at the generated models. The resource facade is intentionally decoupled from the model layer, so swapping the model source is mechanical.

Development

uv run --extra dev pytest        # from code/sdk-python/

Tests mock all HTTP with respx — no live calls against prod.

License

Apache-2.0. © scenerii GmbH.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lodapi-0.0.2.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lodapi-0.0.2-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file lodapi-0.0.2.tar.gz.

File metadata

  • Download URL: lodapi-0.0.2.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for lodapi-0.0.2.tar.gz
Algorithm Hash digest
SHA256 3b16694ecd55c1cdbc365254fc915f2985247ce20e3ffdb3f7950129f4702708
MD5 d5f6a8eb390cfe8f5deb3f512eeb2f03
BLAKE2b-256 392e7cd242ac30170edce8d9a0a2a2641f1f0746ec3d4c4c8d429da75fec3bc0

See more details on using hashes here.

File details

Details for the file lodapi-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: lodapi-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 19.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for lodapi-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 656c472509d6e0d77a6df77e7c9bd3bb7e605d8a4eaf43d01a52b5bb505816da
MD5 d052719a32cdb56f13f5c31366fbf4a3
BLAKE2b-256 cb1b69394ddd3fc586fff9a9e4dcd060534c24bbcb4295da761f216a72b2e619

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page