This FastAPI service returns a terrain viewshed as a GeoJSON Feature. It downloads the
required Copernicus GLO-30 DGED tiles from the Copernicus Data Space Ecosystem S3 service,
caches them locally, and performs each calculation in a local metre-based projection.
Terrain data produced using Copernicus WorldDEM™-30 © DLR e.V. 2010–2014 and © Airbus Defence and Space GmbH 2014–2018, provided under COPERNICUS by the European Union and ESA; all rights reserved.
POST /api/v1/viewsheds requires a bearer token and a JSON body:
{
"observer_coordinates": [174.2316077, -39.0035668],
"observer_height_agl_m": 30,
"target_height_agl_m": 0,
"radius_m": 10000
}Coordinates use GeoJSON order: [longitude, latitude]. Heights and radius are metres. The
default maximum radius is 100,000 m and can be reduced with max_radius_m.
The response is a GeoJSON Polygon or MultiPolygon:
{
"type": "Feature",
"properties": {
"observer_height_agl_m": 30,
"observer_coordinates": [174.2316077, -39.0035668],
"target_height_agl_m": 0,
"radius_m": 10000,
"dem": "Copernicus GLO-30 DGED",
"visible_area_sq_km": 126.4,
"visible_pixel_count": 140472,
"resolution_m": 30,
"earth_curvature": true,
"refraction_coefficient": 0.14285714285714285
},
"geometry": {
"type": "MultiPolygon",
"coordinates": []
}
}The visible area and pixel count are calculated from the binary 30 m raster before geometry
simplification. By default, the output geometry budget is
ceil(visible_area_sq_km × geometry_vertices_per_sq_km) vertices, clamped between the
geometry_min_vertex_budget floor and geometry_max_vertex_budget ceiling. Small components may
collapse during simplification when that is necessary to meet the budget.
All tuning values are environment-backed settings documented beside their defaults in
app/config.py. The most useful controls are:
geometry_vertices_per_sq_km(default100): increase this first to retain more curved edges and detail. It controls a global budget shared by all polygons and holes.geometry_min_vertex_budget(default8): raises the budget only for very small total visible areas; it is not a per-polygon minimum.geometry_max_vertex_budget(default10000): caps geometry complexity and response size for large visible areas.dem_resolution_m(default30): lower values create a finer working raster at substantially greater memory and compute cost. Values below GLO-30's native detail interpolate the source.dem_resampling_method(defaultbilinear): controls elevation interpolation onto that grid.geometry_polygon_connectivityandgeometry_simplification_preserve_topology: control how diagonal cells, components, and holes survive polygonisation and simplification.
The remaining simplification search controls are normally left at their defaults. The
coverage_boundary_sample_interval_degrees setting only identifies source tiles and does not
change output polygon detail.
For every request the service:
- Samples the requested circle at the configured coverage-boundary interval, constructs its geodesic bounds, and identifies all intersecting one-degree GLO-30 geocells.
- Downloads missing DGED GeoTIFFs from S3 and records them in the SQLite tile cache.
- Reprojects the required data into an Azimuthal Equidistant CRS centred on the observer, using the configured output grid (30 m by default).
- Runs GDAL
gdal_viewshedwith the requested observer and target heights and maximum distance. - Applies Earth curvature using GDAL curvature coefficient
1 - 1/7; the corresponding atmospheric refraction coefficient is1/7. - Polygonises visible cells, simplifies to the vertex budget, and transforms the result to EPSG:4326.
By default, the service locates each uncached geocell through the Copernicus catalogue, resolves
the corresponding object below the live CCM/COP-DEM_GLO-30-DGED S3 hierarchy, and stores the
resolved object key in SQLite. A deployment with a stable direct geocell hierarchy can bypass
catalogue discovery by setting glo30_s3_prefix; that prefix must use this layout:
<glo30_s3_prefix>/
Copernicus_DSM_10_<geocell>/DEM/Copernicus_DSM_10_<geocell>_DEM.tif
- Administrators sign into the web UI with email and password.
- Administrators create, activate, promote, and remove users at
/manage-users. - Each non-admin user receives a persistent bearer token. An administrator can regenerate it.
- JWT cookies authenticate web sessions. Persistent user tokens authenticate API requests.
- Every active user can inspect the current GLO-30 tile inventory at
/tile-cache. /docsand/openapi.jsonare available only after sign-in. Swagger is pre-authorised with the signed-in regular user's bearer token.
Create the first administrator inside the Compose service:
docker compose run --rm app alembic upgrade head
docker compose run --rm app python manage_users.py create admin@example.com 'change-this-password'Remove a user:
docker compose run --rm app python manage_users.py remove user@example.comCopy the supplied examples to the untracked deployment files:
docker-compose.yml.example -> docker-compose.yml
.env.example -> .env
The existing .env names are supported directly by app/config.py:
s3_access_key = ...
s3_secret_key = ...
s3_host_base = eodata.dataspace.copernicus.eu
s3_host_bucket = eodata.dataspace.copernicus.eu
secret_key = a-long-random-deployment-secrets3_host_bucket is retained for compatibility with the supplied configuration; the actual S3
bucket name defaults to eodata and can be changed with s3_bucket_name.
s3 access and secret keys are provided by Compernicus.
Running the application requires a Copernicus Data Space Ecosystem (CDSE) account that is registered for Copernicus Contributing Missions (CCM) access. Account holders may generate s3 credentials through the Copernicus Data Portal. The application downloads and caches the required terrain tiles using the authenticated CDSE APIs. Information on registering for CCM access and the available download interfaces (Copernicus Browser, OData and S3) is available from the Copernicus Data Space Ecosystem documentation.
The application and all development tools run only through Docker Compose.
docker compose up --buildThe service listens on http://localhost:8004. The Compose command applies migrations before
starting Uvicorn.
Run migrations explicitly:
docker compose run --rm app alembic upgrade headRun verification:
docker compose run --rm app pytest
docker compose run --rm app pytest --cov=app
docker compose run --rm app ruff check .
docker compose run --rm app ruff format --check .
docker compose run --rm app mypy app/Cached DEM files and the SQLite database are stored in the glo30-data volume. The default cache
expiry is 30 days from last use.
app/
├── main.py
├── config.py
├── database.py
├── dependencies.py
├── models/
├── schemas/
├── routers/
├── services/
├── repositories/
├── templates/
└── tests/
migrations/
Dockerfile
docker-compose.yml.example
Copernicus DEM GLO-30 (DGED). European Space Agency (ESA) and the Copernicus Programme. Digital Surface Model (DSM), 30 m global resolution. DOI: 10.5270/ESA-c5d3d65.