Scene Generation
This page covers the concepts behind the pipeline that powers DigitalTwinClient.prepare_map(). For a practical quickstart, see GIS Pipeline.
Overview
Scene generation converts geospatial data into AODT-ready maps.
AODT maps support data layers such as buildings, building interiors, terrain, and vegetation. These data may be created procedurally, by importing GIS source data, or some combination of the two.
Available parameters
AODT supports two building import methods: OpenStreetMap (OSM) and CityGML (GML). Additional parameters let you generate further layers (terrain, vegetation, interiors) or modify the scene.
Below are the parameters which may be passed as keyword arguments when constructing OSMTask or GMLTask. Default None defers to the GIS pipeline defaults.
TerraformConfig fields
TerraformConfig controls the terrain-shaping pass that fits terrain to buildings. It is only available when using terrain (rather than synthetic, flat ground). Every field is optional; None falls back to the GIS pipeline default shown below.
Example
A typical OSM job with a partial TerraformConfig override. Any field left as None falls back to the GIS pipeline default:
See client/examples/example_prepare_map_terraform.py for a full CLI version.
Output layout
A successful job writes the following layout. AODT scenes are stored as Universal Scene Description (USD) files alongside JSON metadata sidecars:
Inside master.usd, the key prims are:
- Buildings — meshes with material assignments and per-face
GlobalSurfaceHashprimvars (USD per-primitive variables). The hash is a stable per-face identifier that lets calibration and result lookups join back to specific building surfaces across runs. - Terrain / ground plane — the terrain mesh, conformed to building bases when terraform is enabled. With no elevation source, this is a flat plane.
- Interiors — floor-slice meshes generated per building (skipped when
disable_interiors=True). Buildings with ambiguous geometry are skipped here and are also excluded from indoor user equipment (UE) mobility, so the two stay consistent. - Vegetation — tree instances. See Vegetation for how candidates are sampled and filtered if placed procedurally.
- Georeferencing metadata — recorded as USD attributes (
asim:crs,asim:center_lat,asim:center_lon,asim:vertical_datum) on the stage. To inspect without opening the USD, read the mirrored fields frommaster_metadata.json. Calibration, AODT simulations, and the viewer all read these to anchor the scene to real-world coordinates.
Each top-level USD is accompanied by a sidecar *_metadata.json file. It is a small JSON document recording:
- Georeferencing —
crs,center_lat,center_lon,center_z,vertical_datum, and the projected anchor (anchor_x,anchor_y). These mirror theasim:*attributes on the USD and let downstream tools read the scene’s georeferencing without opening the USD. - Provenance —
date_created,input_hashandinput_hash_recipe(a stable fingerprint of the job parameters and source-file bytes), andcode_version(a map of the library versions — aodt, pyproj, proj, gdal, rasterio, usd, python — that influenced the output). The same fields are also written into the USD’scustomLayerDataundergis:*keys.
Terrain
The pipeline imports terrain when include_elevation=True. When include_elevation=False, the pipeline emits a synthetic, flat ground plane — TerraformConfig settings are then ignored, since there is no terrain to reshape.
Terrain sources
The ground_source field selects which digital elevation model (DEM) provider feeds the terrain mesh:
For GML jobs, if the input CityGML files contain TINRelief (Triangulated Irregular Network) features, those features are imported as terrain automatically — you do not need to set ground_source.
Conforming buildings to terrain
Imported buildings and terrain rarely line up by default — the two data sources often have different vertical references, and even when they share one, slopes and DEM noise can leave buildings clipping into the ground or floating above it. The pipeline reconciles them in one of two ways.
Default behavior — building grounding. Every map generation includes a step that matches buildings to terrain. For each building, the pipeline samples the terrain elevation under the footprint center and rigid-translates the building in Z so its base sits at that height. No geometry is reshaped; buildings are dropped onto the terrain. This handles the common case of mismatched datums and gently varying terrain.
How well this simple grounding works depends on terrain resolution and shape. On flat or smoothly varying terrain it is usually enough. On noisy DEMs, steep slopes, or coarse-resolution terrain under fine-grained footprints, individual buildings can still clip into or float above the ground.
For greater control — terraform_config.terraform=True. For these cases, the pipeline can additionally reshape the terrain itself so each building sits cleanly:
- For each footprint, derive a representative base elevation from the terrain samples underneath it (
building_base_method, default"max"). - Smooth those base elevations across neighboring buildings so adjacent footprints share consistent heights (
base_merge_distance,base_influence_radius,base_influence_sigma,base_smooth_iters). - Re-tessellate and reshape the terrain mesh locally so it is flat at the smoothed base elevation under each footprint, with a
pad_radiusblend ring outside. - Optionally smooth the terraformed terrain (
terraform_smooth,terraform_smooth_iters,terraform_smooth_lambda). - Re-seat buildings on the now-flattened patches.
See TerraformConfig fields for the full set of tuning knobs.
Vegetation
The pipeline places trees procedurally across the scene’s plantable area. The pipeline writes output_folder_key/sim/vegetation.geojson, describing every placed tree for downstream consumers.
Placement algorithm
Placement runs in three phases.
1. Build the permissible area. The scene bounding box minus a union of exclusion polygons derived from the input data. Excluded categories include buildings, roads, railways, water bodies (inland and supplemental marine sources), bare-rock terrain (rock, scree, sand, dunes, glaciers, beaches), harbors and piers, sports facilities, playgrounds, parking, runways and taxiways, and power infrastructure. For GML jobs, GML building footprints are added to the exclusion set on top of any OSM data.
2. Place mapped trees first. OSM natural=tree and natural=tree_row features are imported at their exact coordinates and count toward the density target. When an OSM tree carries a height tag, the tag is used to derive its scale; otherwise a uniform random scale is drawn.
3. Procedural fill. The remaining target count is sampled inside the permissible area using a Thomas cluster process: parent points are drawn uniformly across the permissible area, and each parent emits a small cluster of child points with Gaussian scatter. This produces natural-looking groves and clearings rather than a uniform grid. A minimum-spacing pass then thins out positions that came out too close to each other.
After placement, an overlap-resolution pass keeps canopies clean:
- For each pair of overlapping canopies, the larger of the two trees is shrunk so the canopies just touch.
- Trees adjacent to buildings are shrunk so their canopies do not enter the footprint.
- Trees that would have to shrink below
vegetation_scale_minare removed.
Vegetation model and sizing
Every tree is an instance of a single shared prototype with a canopy diameter of about 6 m and a total height of about 10 m at scale 1.0. Per-instance size is randomized:
- A uniform random scale is drawn from
[vegetation_scale_min, vegetation_scale_max](default0.8–1.2) and applied isotropically to the whole tree (canopy and trunk together). - Mapped OSM trees with a
heighttag have their scale derived from the tag (height / 10 m), clipped to the same[vegetation_scale_min, vegetation_scale_max]range.
Density
vegetation_density is a target in trees per hectare across the permissible area. The pipeline computes a target count density × permissible_area_ha, subtracts the number of mapped OSM trees, and asks the procedural sampler for the remainder.
The achieved count is usually lower than the target. Each of the steps above can drop trees: positions outside the permissible area after Gaussian scatter are filtered, the minimum-spacing pass thins clusters, and the overlap-resolution pass shrinks or removes trees that crowd buildings or each other. As a result:
- Sparse scenes typically meet the target density.
- Dense urban scenes saturate well below the target. Increasing
vegetation_densitypast the saturation point yields diminishing returns; lower it if you want a measured outcome to match the configured value. - Fully excluded scenes — for example a bounding box that lies entirely inside buildings or water — emit zero trees and log
No permissible area for vegetation.
Setting vegetation_density=0 is a special case: procedural fill is disabled entirely, and only mapped OSM trees are placed. Use this when you want to keep real-world tree locations without adding any synthetic ones.
Indoor
The pipeline generates per-building interior geometry. Buildings shorter than one floor, or with self-intersecting or otherwise unusable geometry, are skipped.
To skip indoor generation entirely, set disable_interiors=True. This is the right choice when:
- You only need outdoor links.
- You’re iterating on map geometry and want shorter pipeline runs.
- Your scene has no buildings (terrain-only export).
Common issues
coords area is too large
In practice, the pipeline supports map areas up to 25 km². A hard cap is enforced at 100 km², after which client.prepare_map() returns success=False with this message:
Even within the technical cap, processing time grows quickly past 25 km². Use a smaller box, or split your region across multiple prepare_map calls.
Missing or malformed S3 endpoint
When S3Config(provider="minio", endpoint_url=...) is wrong, the workflow fails with an upload error. Unlike the bounding-box case above, these errors raise from client.prepare_map() as a RuntimeError (or ValueError for malformed URLs) — they do not return success=False.
Common forms:
Check that:
endpoint_urlstarts withhttp://orhttps://and is reachable from the worker host.- The bucket exists and the access/secret keys are correct.
- For the default worker stack, MinIO uses
minioadmin/minioadmin.
Cesium tiles render fails
Gzip-compressed tiles (the GIS pipeline’s current default) require the hosting layer to set Content-Encoding: gzip on each object. If your host does not, either configure it to do so or pass cesium3dtiles_gzip=False to prepare_map.
Reference
- DigitalTwinClient.prepare_map
- OSMTask, GMLTask, and
TerraformConfig - GIS Pipeline quickstart
- Configuring Sim YAML
- Limitations