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.1.tar.gz (17.9 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.1-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: lodapi-0.0.1.tar.gz
  • Upload date:
  • Size: 17.9 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.1.tar.gz
Algorithm Hash digest
SHA256 b6d05cdeda5c5aa7ca5473ef5c29db540b3d844af1b5d7446c6922bea7ea7b44
MD5 723bf3e8a16aeb73ac570f461f957ffa
BLAKE2b-256 f4b11a57d9d28568003f784de5e2789f6fd5cfbdc663c64fa8343c5a9e38770c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: lodapi-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 18.7 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e4b55aa898e3ec0801ed7506053b08fbb0bc45322c1e2ce25f6cb081e34f5145
MD5 c0554b074e6cfb33f023a828b3cb226a
BLAKE2b-256 735c117bd6097458e9e29960c704d38b466c998780fa9e0ac9ba06c27ef24613

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