Owner decision: adopt the two findings from the Mapbox cross-check. - LEVEL_HEIGHT 3.2 -> 3.1 m in block_height_report.py and the Blosm --level-height default; every levels-derived building drops ~0.6 m per 6 levels, matching Mapbox. - HEIGHT_OVERRIDES for the three rear building=yes/apartments structures that Mapbox gives as single storey (3.0 m) where our 2-level fallback rendered 6.4 m blocks. - classify_height() takes an optional way_id so overrides apply; the Mapbox cross-check passes it and now reports 66 match / 6 non-building / 0 disagree. - Geometry follows: blockout regenerated at 3.1 (frontage object names preserved, 6/6 exported), frontage re-imported, DemoMap rebuilt, packaged -qaselftest PASS (walk, reset, pause, transect 0 holes, wall probe 0/35). - Docs: Art/blender/README scale table + a note that Blosm adds ~1.9 m roof mass above levels x levelHeight; TODO item closed; MEMORY records the gotcha that blockout regeneration must re-verify the element.* frontage mapping.
Geo
Geographic sources, manifests, and the tools that turn them into Blender and Unreal content.
OSM is layout data only. It is not authoritative for façade detail, height, or pavement, and it is never sampled as a texture or panorama source.
Layout
| Path | Contents |
|---|---|
Geo/raw/ |
Verbatim source responses and downloads, checksummed. Never edited by hand. |
Geo/manifests/ |
One manifest per source: extract, DEM, and the local origin |
Geo/verification/ |
Per-pass notes: address, footprints, bounds, street profile, cross-sections, elevation |
Geo/tools/ |
Every geographic tool (Python, standard library only unless noted) |
Tools that build 3D content from these sources live next to their output in Art/blender/*/ (terrain_from_dem.py, street_mesh.py, site_assembly.py, blosm_blockout.py) because they need Blender; their shared geo helpers and the unit conversion come from Geo/tools/osm_common.py.
Tools
| Tool | Purpose |
|---|---|
osm_common.py |
Shared helpers: parse .osm, stitch and clip a street, project to UTM through PROJ, and the single metres→centimetres conversion (to_unreal_local) |
block_report.py |
Streets, boundary intersections, and the building set for a block, split by side |
block_height_report.py |
Every block building with its height source — tag, levels × 3.2 m, or a placeholder guess |
street_profile.py |
Carriageway width from crossings, sidewalk offsets, per-building setbacks |
street_section.py |
Cross-sections from the DEM: crown, gutters, kerb detection |
elevation_probe.py |
Samples 3DEP for relief and records the baseline for the release checks |
dem_stats.py |
Stdlib reader for the tiled GeoTIFF the 3DEP service returns; GDAL does not run on this host |
asset_metadata.py |
Writes and audits <asset>.geo.json sidecars: position, provenance, conversion |
mapbox_buildings.py |
Cross-checks every building's height against Mapbox mapbox-streets-v8 building data (Tilequery per centroid), merging our OSM height + source with theirs and flagging matches, close, disagreements, non-building volumes. Needs MAPBOX_ACCESS_TOKEN in .env; caches responses under Geo/raw/mapbox-cache/, and both the cache and its report are gitignored (Mapbox terms restrict redistribution) |
Unit boundary
There is exactly one conversion in the repository:
WGS84 (lat, lon) → EPSG:26911 (E, N metres) → project-local metres → Unreal centimetres
Geo/tools/osm_common.py::to_unreal_local is the only place that multiplies by 100. Per-asset sidecars record the result, and asset_metadata.py audit checks each saved sidecar against that function, so a file that drifts is caught rather than trusted.
Current state
The extract, both El Capitan footprints, the demo bounds, the building sets, the street profile, cross-sections, and the 3DEP DEM window are recorded in Geo/verification/ and Geo/manifests/. The local CRS and origin are fixed in manifests/local-origin.md.
Open items live in TODO.md under Geographic data.