# Data attribution

The interactive viewer at `docs/map.html` ships a few pre-computed
`*.geojson` assets derived from public inputs. Each derivative is licensed
under **ODbL 1.0** — the same terms as the OpenStreetMap data it was built
from — and credits OpenStreetMap contributors.

| Asset | Source | License |
| --- | --- | --- |
| `map_toy.geojson` | Hand-written synthetic trajectory (`examples/sample_trajectory.csv`). No real-world GPS data. | Repo license (none set yet) |
| `map_osm.geojson` | OSM public GPS trackpoints API, Berlin bbox `13.40,52.51,13.42,52.52`, ~800 points (committed as `examples/osm_public_trackpoints.csv`). | **© OpenStreetMap contributors, ODbL 1.0** |
| `map_paris.geojson` | OSM public GPS trackpoints API, Paris bbox `2.3370,48.8570,2.3570,48.8770`, pages 0-4 merged to ~6634 deduped points. **Raw CSV is not committed**; only the derived centerlines / boundaries / nodes are shipped here. Built with `export-bundle --max-step-m 40 --merge-endpoint-m 8 --lane-width-m 3.5`. | **© OpenStreetMap contributors, ODbL 1.0** |
| `route_paris.geojson` | Shortest path produced by `roadgraph_builder route /tmp/paris_bundle/sim/road_graph.json n111 n53 --output ...` on the same Paris bundle above: 3 edges, ~1268 m. Loaded as a yellow overlay on top of `map_paris.geojson` in the Leaflet viewer. | **© OpenStreetMap contributors, ODbL 1.0** |
| `map_paris_grid.geojson` | Same Paris bbox, but built from OSM highway ways (Overpass `way["highway"~"^(motorway\|...\|service)$"]`) via `roadgraph_builder build-osm-graph` followed by `enrich_sd_to_hd(lane_width_m=3.5)` and a `refresh_docs_assets.py` post-process that stamps `highway` / `osm_lanes` / `osm_maxspeed` / `osm_name` / `osm_oneway` tags onto every centerline by nearest point-to-polyline match against the raw Overpass ways. 855 nodes / 1081 edges / 2162 HD-lite lane boundaries / 1080 centerlines tagged with OSM road class (residential / service / living_street / secondary / tertiary / primary / unclassified / primary_link). Written as compact (no-indent) JSON to keep the payload small. | **© OpenStreetMap contributors, ODbL 1.0** |
| `map_berlin_mitte.geojson` | Berlin Mitte bbox `13.3667,52.5061,13.4013,52.5262` fetched via `scripts/fetch_osm_highways.py` and built through the same `build-osm-graph` + `enrich_sd_to_hd(lane_width_m=3.5)` path, then post-processed with the same OSM tag injection as `map_paris_grid.geojson`. 1883 nodes / 2063 edges / 4126 HD-lite lane boundaries + OSM `highway` / `osm_lanes` / `osm_maxspeed` / `osm_name` / `osm_oneway` per centerline. Ships 17 mapped OSM turn restrictions, a demo route, reachability overlay, and Lanelet2 download. | **© OpenStreetMap contributors, ODbL 1.0** |
| `berlin_mitte_origin.json` | WGS84 origin (`52.5160, 13.3840`) used when projecting `map_berlin_mitte.geojson` back to lat/lon. Repo-local, synthesised from the bbox centre. | Repo license |
| `map_tokyo_ginza.geojson` / `map_tokyo_ginza.lanelet.osm` / `tokyo_ginza_origin.json` / `tokyo_ginza_turn_restrictions.json` / `tokyo_ginza_camera_detections.json` / `route_tokyo_ginza.geojson` / `reachable_tokyo_ginza.geojson` | Tokyo Ginza HD-lite + Lanelet2 outputs (bbox `139.7600,35.6680,139.7750,35.6750`) generated by `_build_committed_osm_dataset(dataset_id="tokyo_ginza", ...)` from raw Overpass dumps under `/tmp` (highways, restrictions, regulatory nodes). 548 graph nodes, OSM `lanes=` / `width=` / `maxspeed=` propagated, SRTM-30m elevations applied, 19 OSM turn restrictions snapped to graph, 104 traffic_signals + 160 crossings (capped) + 17 stops + 1 speed_camera lifted to per-edge semantic_rules. Demo TR-aware route is `n472 → n141` (~2.4 km cross-Ginza), 500 m reachability overlay from `n472`. | **© OpenStreetMap contributors, ODbL 1.0** |
| `map_sf_north_beach.geojson` / `map_sf_north_beach.lanelet.osm` / `sf_north_beach_origin.json` / `sf_north_beach_turn_restrictions.json` / `sf_north_beach_camera_detections.json` / `route_sf_north_beach.geojson` / `reachable_sf_north_beach.geojson` | San Francisco North Beach / Russian Hill HD-lite + Lanelet2 outputs (bbox `-122.4300,37.7900,-122.4000,37.8100`) generated by `_build_committed_osm_dataset(dataset_id="sf_north_beach", ...)` from raw Overpass dumps under `/tmp` (highways, restrictions, regulatory nodes). 1800 graph nodes / 2227 centerlines, OSM `lanes=` / `width=` / `maxspeed=` propagated, 127 OSM turn restrictions snapped to graph, 238 traffic_signals + 478 stops / give_ways + 160 crossings (capped) projected onto edges, SRTM-30m elevations applied, demo route `n1377 → n499` (~2.76 km), 600 m reachability overlay from `n1377`. | **© OpenStreetMap contributors, ODbL 1.0** |
| `berlin_mitte_turn_restrictions.json` | 17 of 37 OSM `type=restriction` relations within the Berlin Mitte bbox, snapped onto the committed graph by `convert_osm_restrictions_to_graph` (15 m max snap distance). Mirrors the Paris workflow. | **© OpenStreetMap contributors, ODbL 1.0** |
| `route_berlin_mitte.geojson` | TR-aware shortest path from `n32` (south edge of bbox) to `n327` (north edge of bbox), 2 659 m / 48 edges through Berlin Mitte. Honours `berlin_mitte_turn_restrictions.json`. | **© OpenStreetMap contributors, ODbL 1.0** |
| `reachable_berlin_mitte.geojson` | 600 m service-area overlay from `n32` honouring the same TR set as the route asset. 34 directed edge spans / 12 reachable nodes. | **© OpenStreetMap contributors, ODbL 1.0** |
| `map_berlin_mitte.lanelet.osm` | Per-lane Lanelet2 OSM for Berlin Mitte generated by the same pipeline as Paris: `build-osm-graph` → `_inject_osm_tags_into_graph_edges` → `enrich_sd_to_hd` → `_widen_hd_envelope_for_osm_lanes` → `infer_lane_counts` (OSM lanes tag → multi-lane) → `export_lanelet2_per_lane`. Every lanelet carries `one_way`, `participant:vehicle`, `speed_limit` (when OSM `maxspeed` is set), `name`, and the usual `type=lanelet` / `subtype=road` / `location=urban` tags. `validate-lanelet2-tags` reports `result: ok` with 0 errors. Still HD-lite — see Paris Lanelet2 notes for the caveat set. | **© OpenStreetMap contributors, ODbL 1.0** |
| Paris / Berlin / Tokyo / San Francisco `ele` tags on `map_*.lanelet.osm` | Elevations for committed map graphs come from [Open-Elevation](https://open-elevation.com/)'s public SRTM-30m endpoint, fetched per-node via `scripts/fetch_node_elevations.py` and applied to `node.attributes.elevation_m` + per-edge `polyline_z` by `scripts/refresh_docs_assets.py`. Raw responses stay under `/tmp`; only the stamped `ele` tags ship inside the Lanelet2 OSM and the computed `hd.slope_deg` ships inside the GeoJSON. SRTM-30m is CC0 at source; Open-Elevation adds a free public API on top. | Open-Elevation / NASA SRTM-30m (CC0 at source) |
| `map_paris_grid.lanelet.osm` | Per-lane Lanelet2 OSM XML generated by `export_lanelet2_per_lane()` from `map_paris_grid.geojson` after `infer_lane_counts(base_lane_width_m=3.5)` with OSM `lanes=` tag promotion. 1 485 `type=lanelet` relations (842 single-lane + 228 / 273 / 112 / 30 for roads tagged with 2 / 3 / 4 / 5 lanes in OSM) each with left/right boundary ways that hug the real road width (OSM `lanes` × 3.5 m). Every lanelet carries the Autoware-spec tags `one_way` (from OSM `oneway`), `participant:vehicle=yes`, `speed_limit=<N> km/h` (from OSM `maxspeed`), `name` (OSM street name), plus `type=lanelet` / `subtype=road` / `location=urban`. The file includes an Autoware-style `<MetaInfo>` projector hint, 855 `ele` nodes, 288 OSM-derived traffic-light relations, and 1 705 directed `lane_connection` predecessor / successor pairs. Autoware's `lanelet2_validation` can load this file, the repo's own `validate-lanelet2-tags` reports `"result": "ok"` with 0 errors, and `sanitize-lanelet2-autoware` can derive a conservative stock-loader smoke map plus `map_projector_info.yaml` sidecar from the same XML. **It is still HD-lite, not survey-grade**: lane widths are OSM-lane × 3.5 m offsets (not paint-calibrated) and regulatory overlays are OSM-derived approximations. Deployment on a real vehicle still requires calibrated sensors and cm-class QA. | **© OpenStreetMap contributors, ODbL 1.0** |
| `paris_grid_camera_detections.json` | 456 real OSM regulatory nodes (`highway=traffic_signals` × 288, `crossing` × 160, `stop` / `give_way` × 5, `speed_camera` × 3) projected onto the nearest Paris-grid edge by `scripts/refresh_docs_assets.py`. The file is regenerated from `/tmp/osm_real_data/paris_regulatory_nodes.json` (fetched by `scripts/fetch_osm_regulatory_nodes.py`). Each observation carries `source="osm_node"`, `osm_id`, `confidence=1.0`, and `match_distance_m`. `apply_camera_detections_to_graph` feeds them into `edge.attributes.hd.semantic_rules`, and the refresh step also emits matching Point features in `map_paris_grid.geojson` for the viewer. Crossings are capped at 160 to keep the overlay density usable. | **© OpenStreetMap contributors, ODbL 1.0** |
| `paris_grid_turn_restrictions.json` | 10 of 11 OSM `type=restriction` relations in the Paris bbox, mapped onto `map_paris_grid.geojson` via `roadgraph_builder convert-osm-restrictions`. One way-classified cycleway drop means one relation (`9635734`) didn't map. Schema: `roadgraph_builder/schemas/turn_restrictions.schema.json`. Drawn as red dots in the viewer with from/to-edge popups. | **© OpenStreetMap contributors, ODbL 1.0** |
| `route_paris_grid.geojson` | Shortest path `n312 → n191` through `map_paris_grid.geojson` **honouring** `paris_grid_turn_restrictions.json`: 11 edges / 909 m. Without the restrictions the same pair routes in 13 edges / 878 m — so the restriction forces a 31 m detour, which is what the viewer's yellow overlay shows by default on this dataset. | **© OpenStreetMap contributors, ODbL 1.0** |
| `reachable_paris_grid.geojson` | Service-area style reachability overlay from start node `n312` on `map_paris_grid.geojson`, with a 500 m cost budget and the same `paris_grid_turn_restrictions.json` transition rules. Edge spans are clipped when the budget ends mid-edge. | **© OpenStreetMap contributors, ODbL 1.0** |
| `route_explain_sample.json` | Route diagnostics sample generated by `scripts/refresh_docs_assets.py`. It includes one synthetic metric route from `examples/frozen_bundle/sim/road_graph.json` and one Paris OSM-grid `n312 → n191` route derived from `map_paris_grid.geojson` plus `paris_grid_turn_restrictions.json`. | **© OpenStreetMap contributors, ODbL 1.0** for the Paris-derived sample; repo license for the synthetic sample |
| `map_match_explain_sample.json` | Map-matching diagnostics sample generated by `scripts/refresh_docs_assets.py` from the synthetic frozen bundle graph and toy trajectory. It covers nearest-edge and HMM `match-trajectory --explain` outputs. | Repo license |
| `../images/paris_grid_route.svg` | Static README / Pages preview rendered from `map_paris_grid.geojson`, `route_paris_grid.geojson`, `reachable_paris_grid.geojson`, and `paris_grid_turn_restrictions.json` by `scripts/refresh_docs_assets.py`. | **© OpenStreetMap contributors, ODbL 1.0** |
| `../images/route_diagnostics_compare.png` | Static README / Showcase screenshot rendered from `route_diagnostics_preview.html` and `route_explain_sample.json` by `scripts/render_route_diagnostics_screenshot.py`. | **© OpenStreetMap contributors, ODbL 1.0** for the Paris-derived diagnostics; repo license for the synthetic diagnostics |
| `../images/map_console_hero.gif` | Animated map-console demo recorded by `scripts/record_map_console_hero.py` — deep-linked Paris `n312 → n191` route in 2D, then 2D ↔ 3D toggle with auto-rotate. Playwright captures a WebM via `recordVideo` and ffmpeg converts it to a palette-optimised GIF. | **© OpenStreetMap contributors, ODbL 1.0** (OSM tiles and centerlines visible in the frames) |
| `../images/map_console_{2d,3d}.png` | Static README / SHOWCASE map-console captures rendered by `scripts/render_map_console_screenshot.py` with the default Paris `n312 → n191` deep link. | **© OpenStreetMap contributors, ODbL 1.0** (OSM tiles and centerlines visible in the frames) |

To refetch / regenerate:

```bash
for p in 0 1 2 3 4; do
  python scripts/fetch_osm_trackpoints.py \
    --bbox "2.3370,48.8570,2.3570,48.8770" --max-points 1500 --page $p \
    -o /tmp/osm_real_data/paris_trackpoints_p${p}.csv
done
# (Merge the 5 page WGS84 outputs through a single bbox-center origin;
# see docs/bundle_tuning.md for the recipe.)
roadgraph_builder export-bundle /tmp/osm_real_data/paris_merged.csv /tmp/paris_bundle \
  --origin-json /tmp/osm_real_data/paris_merged_origin.json \
  --lane-width-m 3.5 --max-step-m 40 --merge-endpoint-m 8
cp /tmp/paris_bundle/sim/map.geojson docs/assets/map_paris.geojson
```

To regenerate the OSM-highway-derived graph and its turn restrictions:

```bash
python scripts/fetch_osm_highways.py \
  --bbox "2.3370,48.8570,2.3570,48.8770" \
  -o /tmp/osm_real_data/paris_highways.json

python scripts/fetch_osm_turn_restrictions.py \
  --bbox "2.3370,48.8570,2.3570,48.8770" \
  -o /tmp/osm_real_data/paris_turn_restrictions_raw.json

roadgraph_builder build-osm-graph \
  /tmp/osm_real_data/paris_highways.json \
  /tmp/osm_real_data/paris_grid_graph.json \
  --origin-json /tmp/osm_real_data/paris_merged_origin.json \
  --simplify-tolerance 0.0 --merge-endpoint-m 2.0

roadgraph_builder convert-osm-restrictions \
  /tmp/osm_real_data/paris_grid_graph.json \
  /tmp/osm_real_data/paris_turn_restrictions_raw.json \
  docs/assets/paris_grid_turn_restrictions.json

# Then regenerate the map.geojson and the demo route via the Python snippet
# in scripts/refresh_docs_assets.py (see the paris_grid block).
```

For the Berlin Mitte demo:

```bash
python scripts/fetch_osm_highways.py \
  --bbox "13.3667,52.5061,13.4013,52.5262" \
  -o /tmp/berlin_mitte_raw.json

# refresh_docs_assets.py picks up /tmp/berlin_mitte_raw.json automatically
# and writes docs/assets/map_berlin_mitte.geojson + berlin_mitte_origin.json.
python scripts/refresh_docs_assets.py
```

For the San Francisco North Beach / Russian Hill demo:

```bash
python scripts/fetch_osm_highways.py \
  --bbox="-122.4300,37.7900,-122.4000,37.8100" \
  -o /tmp/osm_real_data/sf_highways.json

python scripts/fetch_osm_turn_restrictions.py \
  --bbox="-122.4300,37.7900,-122.4000,37.8100" \
  -o /tmp/osm_real_data/sf_turn_restrictions_raw.json

python scripts/fetch_osm_regulatory_nodes.py \
  --bbox="-122.4300,37.7900,-122.4000,37.8100" \
  -o /tmp/osm_real_data/sf_regulatory_nodes.json

python scripts/refresh_docs_assets.py

python scripts/fetch_node_elevations.py \
  --geojson docs/assets/map_sf_north_beach.geojson \
  -o /tmp/osm_real_data/sf_node_elevations.json

python scripts/refresh_docs_assets.py
```
