The ground reference¶
Status: the standard, accepted 2026-11-09. It applies to duckOSM, SimReady, urbanstyle, roadstyle and the projects that read their outputs.
One area has one ground. Every project computes on it, every renderer draws it, and every file says which ground it was made on.
Why¶
Before this standard, each project chose its own ground. roadstyle's map terrain came from the AWS Terrain Tiles (Terrarium: SRTM and other sources); duckOSM sampled Copernicus GLO-30 (a surface model with roofs and trees); SimReady used FABDEM for Monaco and a 1 m LiDAR DTM for Vancouver. No file said which ground it was made on. So a road, a bridge deck or a vehicle with its own height floated above the drawn terrain or sank into it, and no check could find the cause.
1. One ground reference for each area¶
The ground reference of an area is one elevation file. duckOSM records it, in main.elevation_metadata, the row of ele.
| Field | Rule |
|---|---|
product |
DTM (bare earth). A surface model (DSM, Copernicus GLO-30) is not a reference: its roofs and trees go into the road heights |
source, uri |
the file or the model, with its version (FABDEM V1-2, a national DTM, a LiDAR DTM) |
dem_crs |
its CRS |
vertical_datum |
known (EGM2008, EGM96, a national datum). unknown is not a reference |
resolution_m, license |
known |
sha256 |
the hash of the file's bytes, the ground id of every product below. NULL when no whole file was read (Copernicus tiles streamed from AWS, a URL): such a source is not a reference |
The other projects do not choose a ground. They read this row (or the same values in the area's settings, for SimReady:
areas/<area>/area.yaml dem: and sources.yaml, which must name the same file).
If no DTM exists for an area, a DSM can stand in, but only with product: DSM in the row. Every product made on it then
shows "ground: surface model" (section 4). It is never silent.
2. One rule for heights¶
- Every height is in metres above the vertical datum of the reference, at scale 1.
- A height from another source (an OSM
eletag, a bridge profile, a LiDAR road surface) is first put on that datum. - Exaggeration exists only at display time. It applies to everything with a height together: the terrain, the roads, the decks, the buildings and the vehicles.
3. One ground that every renderer draws: the conformed ground¶
| Layer | What it is | Who makes it |
|---|---|---|
| reference ground | the DTM of section 1, as a grid (main.elevation_grid, the raster's own pixels) |
duckOSM |
| road heights | the designed (graded) height of each carriageway, from the reference ground, its structures and its rules | SimReady (sim.elevation_profile, method graded) |
| conformed ground | the reference ground bent to the roads: under a roadway it is the road's height, beside it it blends back to the DTM, away from roads it is the DTM | SimReady (terrain-conform) |
- Every renderer draws the conformed ground: the GLB terrain, roadstyle's map terrain, Cesium, CARLA, Unity. No renderer uses another elevation source (for example the AWS Terrain Tiles) for an area that has a reference.
- roadstyle's map terrain for such an area comes from raster tiles made from the conformed ground (encoded as Terrarium
PNG, as MapLibre reads them), not from
basemaps.jsonontop.dem. - A page or a file for an area with no reference says "ground: none" or "ground: AWS Terrain Tiles" where its user sees it.
- A road surface, a deck, a vehicle: its height is the road height (section 3), never a second sample of a DEM.
Where the ground cannot hold a road¶
The conformed ground is a height grid: one height at each point. So it cannot hold a road in three cases:
| Case | Why | Example (Monaco) |
|---|---|---|
| bridge | the road is above the ground; the ground under it stays the DEM | Boulevard du Larvotto, 10 to 16 m above the ground |
| tunnel | the road is under the ground; the ground above it stays the DEM | Boulevard du Larvotto, 5 to 13 m under the ground |
| two roads side by side at different heights | a wall separates them, and a grid cannot have a wall: it steps from cell to cell, as a saw-tooth | the ramp to Avenue Pasteur, 6.9 m above the road beside it, 5.2 m away |
In Monaco, 60 of 1,892 ground-level carriageways (about 1.2 km) have a road at a different height less than 8 m away (steeper than 1:2).
The rule: a renderer draws each road at its road height. It lays a road on the conformed ground only where the ground holds it. Where the ground does not hold it, the renderer draws the road as its own surface at the road height, with a wall from its edge down to the ground (or up to the ground, in a cut). The GLB does this already: each carriageway is its own mesh at its graded height. A map that lays every road on its terrain (MapLibre's flat road layers) is wrong at these roads, even when its terrain is the conformed ground.
4. Every product names its ground¶
| Product | Where the ground id goes |
|---|---|
| duckOSM database | main.elevation_metadata (sha256, product, vertical_datum) |
| SimReady model | sim.source, and each elevation_profile row's source_id |
.xodr |
<header>: a <userData code="ground"> with the ground id and the datum |
.glb |
the root node's extras.ground |
| SUMO network | a <param key="ground" .../> of the network |
| roadstyle page | the page option ground (the id, the source, the datum), shown with the attribution |
| vehicles run report | ground |
Two files of one area with different ground ids do not belong together. A tool that reads both stops with an error that names the two ids.
5. Checks¶
| Check | Pass when |
|---|---|
| the reference | product = DTM, vertical_datum known, sha256 equals the file's |
| the road on its ground | under each roadway that the ground holds (section 3), the road surface minus the conformed ground is at most 0.05 m. A roadway that the ground does not hold is drawn as its own surface, not on the ground |
| the map terrain | the terrain tiles of a page minus the conformed ground grid, at the tile pixels' points (each pixel's north-west corner, where MapLibre reads it): at most 0.10 m (the PNG step). Not at the grid's points: MapLibre interpolates the tile pixels again, so there the difference is metres on steep ground |
| one ground in an area | every product of the area has the same ground id |
Where each project stands (2026-11-10, evening; roadstyle 2026-11-11)¶
| Project | Now | To do |
|---|---|---|
| duckOSM | Done. main.elevation_metadata has product, vertical_datum, license and sha256 (the ground id) in the Python command and in the Rust build (file and browser bytes); main.elevation_grid from the Rust build. A --dem file in ele needs its product, its vertical datum and its license (options or the config section elevation), or --not-reference. The default source with no --dem is still Copernicus GLO-30 (a DSM, no ground id) |
nothing |
| SimReady | Monaco on FABDEM (a DTM), Vancouver on a 1 m LiDAR DTM; terrain-conform and the GLB on main (the gltf adapter); the connecting roads of the .xodr have no elevation |
the ground id in its products; the conformed ground as a grid that others read; the elevation of the connecting roads |
| roadstyle | the map terrain comes from the area's ground (the page option ground: a PMTiles file made from main.elevation_grid; roadstyle PRs 145-147), and the attribution names it; the 3D bridge decks stand on it. With no ground, the AWS Terrain Tiles, and the page says so. The vehicles take SimReady's graded profile and the Monaco vehicles page draws the conformed ground (roadstyle branch claude/vehicles-p2, not merged) |
draw the roads that the ground does not hold (section 3: bridges, tunnels, roads side by side) at their road height, with the 3D bridge decks on the same profile |
| urbanstyle | a "rough terrain" switch with the AWS Terrain Tiles; bridge heights from LiDAR feed SimReady | the same terrain tiles as roadstyle |
Related: Add elevation, Elevation sources.