Skip to content

rsasaki0109/roadgraph_builder

Repository files navigation

roadgraph_builder

CI License: MIT Python

Build inspectable SD → HD-lite road graphs from trajectories, OSM, LiDAR, and camera data — and read them in a map console.

roadgraph_builder is a graph-first toolkit. It is not an autonomous-driving stack and not a survey-grade HD-map generator. It builds an SD road graph from GPS / OSM input, lets you route / reach / snap on it, enriches it with HD-lite lane / regulatory / elevation overlays, and exports it as navigation JSON / simulation GeoJSON / Lanelet2 OSM XML. A static browser console renders every layer from committed sample data with no build step.

Map console animated hero: Paris deep-link route in 2D, then 3D auto-rotate with road-class + junction colour coding

Open it: Map console · Showcase · Architecture · Benchmarks · Launch notes · Changelog · Contributing

Who this is for

  • Autoware users — produce small Lanelet2 smoke maps from SD / trajectory inputs before survey-grade mapping. sanitize-lanelet2-autoware writes stock-loader-friendly lanelet2_map.osm + map_projector_info.yaml; validate-lanelet2 bridges Autoware's lanelet2_validation CLI when on PATH.
  • OSS developers — pure-Python (numpy + jsonschema) CLI / library covering routing (A* / Dijkstra + turn restrictions + slope-aware + lane-change), reachability, map matching (HMM), HD-lite enrichment, schema validation, and three-way export. Heavy deps (laspy[lazrs], OpenCV) stay in [extras].
  • Researchers / educators — reproduce SD graph, HD-lite overlays, Lanelet2 output, and routing diagnostics on four committed city samples (Paris 20e grid, Berlin Mitte, Tokyo Ginza, San Francisco North Beach) without fetching live data; benchmark baselines, accuracy reports, and float32 drift studies are committed alongside.

Scope — HD-lite, not survey-grade. The pipeline produces Lanelet2-compatible OSM XML that Autoware's lanelet2_validation can load (the rich Roadgraph export, e.g. docs/assets/map_paris_grid.lanelet.osm); for stock Lanelet2 / Autoware loader smoke tests, sanitize-lanelet2-autoware rewrites it into a conservative lanelet2_map.osm plus optional map_projector_info.yaml. Lane widths are centerline-offset envelopes (default 3.5 m), lane counts fall back to 1 without paint observations, regulatory overlays are OSM-derived or sample-grade detections, and elevation is SRTM- or trajectory-derived when available. Real vehicle deployment still requires calibrated sensors, cm-class validation, and per-area QA beyond what this tool guarantees. Side-by-side numbers against Autoware's Kashiwanoha survey-HD sample (3 456 lanelets / km² vs our 232–544, ele on 100 % of nodes vs our 5–8 %, surveyed paint vs centerline offsets) and an Autoware-component-by-component "will it simulate?" matrix live in docs/AUTOWARE_HD_MAP_COMPARISON.md. TL;DR: the strict lanelet2.io.load + autoware_lanelet2_validation gates pass on all four committed cities, and v0.7.4+ now also emits type=stop_line ways (attached as ref_line to traffic_light regulatory_elements where they share an edge) and subtype=crosswalk lanelets — schema the behavior_velocity_planner traffic_light / crosswalk modules walk. right_of_way (unsignalised intersections) and the Autoware-extension-only standalone subtype=stop_line regulatory_element are still not emitted.

Measured results

v0.7.4 ships a signed diverging Slope mode (blue down / grey flat / red up, with degrees + percent grade in popup and hover card) and CLI flags match-trajectory --hmm-outlier-weight / --hmm-outlier-scale-m for the Gaussian / Cauchy emission mixture (see docs/hmm_mixture_calibration.md; defaults stay byte-identical to v0.7.3). Layered over the v0.7.2 Autoware-friendly Lanelet2 hardening.

HD-lite zoom: Paris 10ᵉ intersection at z19 with HD-lite lane boundary dashes (green = left, purple = right) flanking OSM-derived traffic lights, stop lines, and crosswalks

Signal Current result Source
Routing Paris TR-aware route 909 m vs unrestricted 878 m; the map console has 2D OSM + 3D graph views and route --explain compares A* with Dijkstra fallback work map · diagnostics compare · JSON
Accuracy Lane-count MAE @ 20 m: Paris 0.938 / Tokyo 0.903 / Berlin 1.220 accuracy report
Tuning Conservative cross-city start: --max-step-m 40 --merge-endpoint-m 8 tuning guide
Memory Default remains float64; float32 is opt-in after replay showed small RSS wins and ID drift float32 report

What the map console shows

The toolbar's Mode select walks through the pipeline tiers on any committed dataset. Same data, four progressively richer views:

Tier What is drawn What proves it
Basic Centerlines coloured by OSM highway class (motorway → red through residential → cyan) + graph nodes coloured by junction type (T / Y / crossroads / X / complex / through-or-corner / dead-end) build-osm-graph + pipeline.junction_topology — bare SD graph, topology-honest, 855 nodes / 1 081 centerlines for the Paris bbox.
SD + directed-state route (JS Dijkstra honouring OSM no_* / only_*) + turn restriction markers Paris n312 → n191 is 909 m / 11 edges / 8 TR honoured (the unrestricted shortest path is 878 m, a 31 m detour). Berlin Mitte, Tokyo Ginza, and San Francisco North Beach ship the same route / restrictions / reachability overlay pattern.
HD + HD-lite lane boundaries (green / purple dashes from enrich --lane-width-m 3.5) + OSM-derived traffic lights / stop lines / crosswalks + 500-600 m reachability spans Paris ships 456 OSM regulatory markers; San Francisco ships 876 regulatory markers plus 1 800 SRTM-backed elevation nodes for slope-aware inspection. Same overlays apply-camera and reachable produce from the CLI.
Full Everything above Default when the viewer opens; click Reach from click to pick any node and recompute reachability live in the browser.
Slope + every centerline recoloured by signed hd.slope_deg (blue downhill → grey flat → red uphill), 3D scene lifted by polyline_z with 6× exaggeration San Francisco North Beach surfaces 290 uphill / 315 downhill / 1 622 flat (|slope| < 0.5°) edges out of 2 227 — both ramp ends light up at the same time so the hill direction reads at a glance. Hover popups add slope: ±X.XX° (±YY.Y%, uphill / downhill / flat).
Lanelet2 Downloadable per-lane OSM XML for Autoware tooling docs/assets/map_paris_grid.lanelet.osm — 1 485 lanelets / 2 397 regulatory elements, including directed lane_connection predecessor / successor pairs, emitted by export_lanelet2_per_lane() after infer_lane_counts. Use sanitize-lanelet2-autoware for a stock-loader smoke profile. HD-lite envelopes, not survey-grade.

2D map console: Paris OSM grid with road-class colours, HD-lite lanes, regulatory markers, and inspector cards

3D graph console: same Paris grid as a Three.js scene with raycaster picking and hover card

Slope mode 3D: San Francisco North Beach lifted by polyline_z; signed slope ramp (blue downhill, grey flat, red uphill) shows hill direction at a glance

The inspector on the right is driven by the same data: Road classes (9 categories), Junctions (T / Y / crossroads / …), Route steps (edge id · direction · cumulative m), and a Route engine card whose expanded / queued / pops fields use the same diagnostics shape as the CLI's route --explain. Hovering a centerline or node in 2D or 3D updates a Hover card with Edge · Primary · 3 lanes · max 50 km/h style detail.

Run the console locally — no build step, just a static server:

cd docs && python3 -m http.server 8765
# http://127.0.0.1:8765/map.html
#   ?dataset=paris_grid  (default) | berlin_mitte | tokyo_ginza | sf_north_beach | paris | osm | toy
#   ?view=2d | 3d
#   ?mode=basic | sd | hd | full | slope   (centerline palette tier; slope = signed diverging)
#   ?from=n312&to=n191                     (deep-link route, auto-fits map bounds)
#
# Deep-link straight into the SF slope hero used above:
#   http://127.0.0.1:8765/map.html?dataset=sf_north_beach&view=3d&mode=slope

The animated hero, the two static PNGs, and the diagnostics screenshot are regenerated by scripts/record_map_console_hero.py / scripts/render_map_console_screenshot.py / scripts/render_route_diagnostics_screenshot.py — all three drive system Chrome through Playwright and pull OSM tiles, Leaflet, and Three.js over the network. OSM-derived asset attribution is tracked in docs/assets/ATTRIBUTION.md.

Route diagnostics comparison: safe A* expands 2 states while the Paris Dijkstra fallback expands 783 states

Paris OSM-highway grid with a TR-aware route and 500 m reachability overlay

GitHub “About” text (copy-paste)

Use the short description and topics listed in .github/ABOUT.md, or run the gh repo edit command there after the repository is public.

Features

Area Highlights
Input Trajectory CSV · OSM highway ways · LAS 1.0–1.4 / LAZ point clouds · pixel detections + camera calibration · raw RGB for lane-marking detection
Pipeline Gap segmentation → arc-length Gaussian centerline → X/T-split + union-find → duplicate / near-parallel merge → junction consolidation; topology-honest OSM path via build-osm-graph; 3D elevation when build --3d; incremental update-graph; parallel process-dataset
Routing A* / Dijkstra with no_* / only_* turn restrictions + route --explain diagnostics; uncertainty / slope / lane-change variants; RoutePlanner + ReachabilityAnalyzer; guidance turn-by-turn
Perception / LiDAR project-camera (Brown-Conrady distortion) + apply-camera + detect-lane-markings-camera; fuse-lidar with --ground-plane RANSAC + detect-lane-markings from intensity
HD / Lanelet2 enrich --lane-width-m envelopes, infer-lane-count, export-lanelet2 --per-lane, sanitize-lanelet2-autoware loader-smoke profile, validate-lanelet2-tags + validate-lanelet2 Autoware bridge
Output / Bench JSON (+ schema), GeoJSON, OSM XML 0.6, SVG, one-shot export-bundle; make bench deterministic suite with 3× regression gate (baseline + memory + accuracy reports under docs/)
Demo / Samples Map console + diagrams; four committed cities (Paris / Berlin / Tokyo / SF) + toy + OSM-public CSV samples

Full module index, command surface, and Mermaid diagrams in docs/ARCHITECTURE.md.

Current release surface

The latest shipped tag is v0.7.4. It keeps the full v0.6 / v0.7 command surface (lane-count inference, per-lane Lanelet2 export, uncertainty / slope / lane-change routing, camera lane detection, ground-plane LiDAR, Autoware validator bridging, incremental updates, dataset batch export) and adds the signed Slope mode + CLI HMM mixture flags described above. See CHANGELOG.md for the exact delta.

Quick start

# Contributor install (editable, drives `make test` and the CLI).
python3 -m venv .venv && .venv/bin/pip install -e .

# Evaluator install (no clone needed; pulls the v0.7.4 tag directly from GitHub).
python3 -m venv .venv && .venv/bin/pip install \
  "roadgraph-builder @ git+https://github.com/rsasaki0109/roadgraph_builder@v0.7.4"

# Either install gives you the same CLI entry point.
.venv/bin/roadgraph_builder doctor
.venv/bin/roadgraph_builder build examples/sample_trajectory.csv out.json
.venv/bin/roadgraph_builder enrich out.json out_hd_envelope.json
.venv/bin/roadgraph_builder validate out_hd_envelope.json

PyPI publishing is intentionally skipped for this cycle. The VCS install above is the supported "no clone" path; the GitHub Release tarball under releases is the audited artifact.

From the repo root, make doctor, make demo, make tune (bundle + validate for parameter exploration), and make test are shortcuts (see Makefile). Opt-in suites: make viewer-smoke runs the Playwright / system Chrome smoke for docs/map.html, pytest -m slow the long perf regressions, and pytest -m city_scale the real-OSM fetch tests. Tuning workflow: docs/bundle_tuning.md.

Multiple passes over the same area (--extra-csv)

build and export-bundle accept repeated --extra-csv PATH to concatenate extra trajectory CSVs that share the primary input's meter origin. Overlapping passes get fused (duplicate / near-parallel merge); non-overlapping passes land as independent polylines. attributes.direction_observed on each edge flips to bidirectional when at least one pass traversed the edge in the opposite direction.

roadgraph_builder build examples/sample_trajectory.csv out.json \
  --extra-csv examples/another_pass.csv \
  --extra-csv examples/yet_another_pass.csv

Three targets at once (nav SD / sim / Lanelet) — export-bundle

日本語: ナビ向け SD シード、シミュ用の フルグラフ+GeoJSONLanelet 互換 OSM を、いまあるパイプラインで一括書き出します(完成品 HD ではなく、同じ土台を三系統に分ける)。

# WGS84 origin: either pass --origin-json (lat0/lon0 file) or --origin-lat / --origin-lon
roadgraph_builder export-bundle examples/sample_trajectory.csv ./out_bundle \
  --origin-json examples/toy_map_origin.json \
  --lane-width-m 3.5 \
  --detections-json examples/camera_detections_sample.json

For very large bundles, add --compact-geojson to write sim/map.geojson without pretty indentation, and --compact-bundle-json to compact nav/sd_nav.json, sim/road_graph.json, and manifest.json. Defaults remain pretty-printed so frozen release bundles and human diffs stay stable.

One-shot demo (validates detections, runs bundle, validates manifest + sd_nav + road_graph):

./scripts/run_demo_bundle.sh /tmp/my_bundle

Parameter tuning (same export-bundle, fewer extras; open sim/map.geojson in QGIS and adjust max-step-m / merge-endpoint-m — see docs/bundle_tuning.md):

make tune
# or: ./scripts/run_tuning_bundle.sh /tmp/my_tune
Output Role
out_bundle/manifest.json Provenance — tool version, UTC time, origin, input basename, output paths. roadgraph_builder_version and generated_at_utc are dynamic; the remaining fields are treated as stable release output.
out_bundle/nav/sd_nav.json SD / routing seed — topology + edge length_m; allowed_maneuvers / allowed_maneuvers_reverse are geometry heuristics at the digitized end/start node (straight, and at junctions/dead-ends left/right/u_turn when inferred)
out_bundle/sim/ Simulation / vizroad_graph.json, map.geojson, trajectory.csv
out_bundle/lanelet/map.osm Lanelet / JOSM — OSM XML (with lanelets when L/R boundaries exist)

Use --lane-width-m 0 to skip HD-lite ribbon offsets. Origin must match your GeoJSON / Lanelet convention (see examples/*_origin.json). Validate manifest.json with roadgraph_builder validate-manifest (roadgraph_builder/schemas/manifest.schema.json) and nav/sd_nav.json with roadgraph_builder validate-sd-nav (sd_nav.schema.json).

Release stability policy: the default sample bundle is rebuilt in tests and stable generated files are compared byte-for-byte with examples/frozen_bundle/. manifest.json is compared after normalizing only roadgraph_builder_version and generated_at_utc; any other manifest drift is treated as a release-surface change.

allowed_maneuvers is not a legal turn-restriction layer. It is a permissive 2D topology hint for routing/display; signs, signals, and turn bans should be carried in optional turn_restrictions. Design note: docs/navigation_turn_restrictions.md.

Links

Resource URL
Local viewer docs/map.html (primary), docs/diagram.html (SVG viewer + route diagnostics), docs/index.html (redirects to map.html) — serve with cd docs && python3 -m http.server 8765
Changelog CHANGELOG.md
Plan / handoff docs/PLAN.md
PyPI Not published by default; see PyPI (optional)

Forks: URLs and OSM User-Agent

  • scripts/refresh_docs_assets.py — set ROADGRAPH_REPO_URL and ROADGRAPH_PAGES_URL before running to rewrite docs/assets/site.json (footer links in the viewer).
  • scripts/fetch_osm_trackpoints.py — set ROADGRAPH_USER_AGENT or pass --user-agent (OpenStreetMap policy).

Concept

  • Graph first — road = nodes + edges; centerlines / boundaries are attributes on the graph, not its definition.
  • Multi-modal — trajectory, LiDAR, and camera inputs are swappable modules; fusion is an explicit later stage.
  • SD seed → HD-litebuild makes the SD graph; enrich / fuse-lidar / apply-camera / convert-osm-restrictions layer on HD-lite envelopes, lane counts, semantics, and turn restrictions; export-lanelet2 --per-lane writes Autoware-compatible OSM XML. Not cm-class survey HD.

日本語で一言: SD の「道路のつながり+中心線」中間表現として使える。 HD に必要なレーン境界・高精度・規則は別データ (LiDAR 等) を足す前提。

Full SD→HD stage table, module index, and architecture diagrams in docs/ARCHITECTURE.md.

Interactive viewer (docs/)

The docs/ folder is a small static site. This repository is currently private; on the current GitHub plan, private-repo GitHub Pages is not available, so the supported preview path is local.

Local preview (no GitHub required):

cd docs && python3 -m http.server 8765
# http://127.0.0.1:8765/          — diagram viewer
# http://127.0.0.1:8765/map.html  — 2D/3D OSM map console + GeoJSON

When Pages is available, point Settings → Pages → Deploy from a branch at main / /docs; the published map.html is the same OSM + 3D graph console with the same ?view / ?dataset / ?mode / ?from / ?to deep links. Committed datasets:

  • Paris grid (default, 855 nodes / 1081 edges, 10 OSM turn restrictions, 500 m reachable)
  • Berlin Mitte (1883 / 2063, 17 restrictions, 600 m reachable, Lanelet2 download)
  • Tokyo Ginza (548 / 598, 19 restrictions, OSM regulatory nodes, Lanelet2 download)
  • San Francisco North Beach (1800 / 2227, 127 restrictions, SRTM slopes, Lanelet2 download)
  • Paris / OSM Berlin sample / Toy — older trajectory-derived samples

All city datasets are ODbL.

Regenerate bundled JSON/CSV/SVG for docs/ after changing examples or pipeline logic:

python3 scripts/refresh_docs_assets.py

Requirements

  • Python 3.10+
  • numpy, jsonschema (for validating exported JSON)

Install

From the repository root (use a virtual environment on PEP 668–managed systems):

python3 -m venv .venv
.venv/bin/pip install -e .

Public trajectory sample (OpenStreetMap)

The file examples/osm_public_trackpoints.csv is real, publicly contributed GPS data fetched from the OpenStreetMap API (/api/0.6/trackpoints). It is intended for tuning build / visualize on noisy trajectories.

  • License / attribution: OpenStreetMap data is © OpenStreetMap contributors and available under the Open Database License (ODbL). See openstreetmap.org/copyright.
  • Regenerate (optional; requires network): set a fork-specific agent if needed, then run:
export ROADGRAPH_USER_AGENT='myfork/1.0 (+https://github.com/you/roadgraph_builder)'
python3 scripts/fetch_osm_trackpoints.py -o examples/osm_public_trackpoints.csv

Also writes examples/osm_public_trackpoints_origin.json (WGS84 origin for the meters CSV) and examples/osm_public_trackpoints_wgs84.csv (timestamp,lon,lat) for map tooling.

Try another area if the bbox has no uploads: --bbox min_lon,min_lat,max_lon,max_lat (each side ≤ 0.25°).

Example (defaults are fine; starting point for the committed OSM sample):

roadgraph_builder build examples/osm_public_trackpoints.csv osm_graph.json \
  --max-step-m 40 --merge-endpoint-m 12 --centerline-bins 32
roadgraph_builder visualize examples/osm_public_trackpoints.csv osm_preview.svg \
  --max-step-m 40 --merge-endpoint-m 12 --centerline-bins 32

Usage (CLI)

Long-form per-subcommand walkthrough lives in docs/USAGE.md: build / enrich / fuse-lidar / detect-lane-markings / match-trajectory / route + reachable + guidance / export-lanelet2 + sanitize-lanelet2-autoware / build-osm-graph + convert-osm-restrictions / project-camera + apply-camera / validate* / make bench / CI parity. Short form below.

# Smallest end-to-end: build → enrich → validate.
roadgraph_builder build examples/sample_trajectory.csv out.json
roadgraph_builder enrich out.json out_hd.json --lane-width-m 3.5
roadgraph_builder validate out_hd.json

# Three-target bundle in one call (nav SD / sim / Lanelet2).
roadgraph_builder export-bundle examples/sample_trajectory.csv ./out_bundle \
  --origin-json examples/toy_map_origin.json --lane-width-m 3.5

# Routing on the frozen Paris bundle.
roadgraph_builder route examples/frozen_bundle/sim/road_graph.json n0 n1 --explain

# Tests (PYTEST_DISABLE_PLUGIN_AUTOLOAD avoids ROS pytest plugin breakage).
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 .venv/bin/pytest

Package layout

See docs/ARCHITECTURE.md for a single-page map with Mermaid diagrams of the data flow, CLI surface, bundle layout, and routing subsystem.

Python package: roadgraph_builder/

Path Role
roadgraph_builder/core/graph/ Node, Edge, Graph models
roadgraph_builder/io/trajectory/ Trajectory CSV loader
roadgraph_builder/io/lidar/ load_points_xy_csv, load_points_xy_from_las, read_las_header (pure-Python LAS 1.0-1.4 reader, LAZ via optional [laz] extra)
roadgraph_builder/io/camera/ load_camera_detections_json, apply_camera_detections_to_graph, CameraCalibration + pixel_to_ground + project_image_detections_to_graph_edges (pinhole + Brown-Conrady distortion)
roadgraph_builder/io/osm/ build_graph_from_overpass_highways, convert_osm_restrictions_to_graph (Overpass → graph / turn_restrictions)
roadgraph_builder/io/export/ JSON; GeoJSON (optional attribution/license fields); export_lanelet2; bundle (nav/sim/lanelet)
roadgraph_builder/pipeline/ build_graph pipeline
roadgraph_builder/hd/ enrich_sd_to_hd, fuse_lane_boundaries_from_points, centerline offsets
roadgraph_builder/utils/geometry.py Clustering / centerline helpers
roadgraph_builder/viz/ SVG export (trajectory + graph)
roadgraph_builder/semantics/ Trace fusion, map matching summaries, trip reconstruction, road-class and signalized-junction inference
roadgraph_builder/schemas/ road_graph, camera_detections, sd_nav, manifest (.schema.json)
roadgraph_builder/validation/ validate_*_document() for graph, detections, sd_nav, manifest
roadgraph_builder/cli/ Thin dispatcher plus domain command modules (build, validate, routing, export, camera, lidar, osm, guidance, trajectory, hd, incremental, dataset)
docs/ Static viewer + bundled sample assets
scripts/refresh_docs_assets.py Regenerate docs/assets and docs/images
scripts/render_route_diagnostics_screenshot.py Render the README route diagnostics comparison PNG with headless Chrome
scripts/compare_float32_drift.py Rebuild float64/float32 bundles and report topology / coordinate drift
scripts/run_demo_bundle.sh Validate → export-bundle → validate outputs (demo)
scripts/run_autoware_sanitizer_smoke.sh Sanitize every committed Lanelet2 OSM + validate tags + optional Python lanelet2 strict load / lanelet2_validation CLI bridge
.github/workflows/autoware-smoke.yml Optional workflow (workflow_dispatch + weekly) that installs ROS Jazzy lanelet2 packages and runs the smoke script
roadgraph_builder/io/export/geojson.py export_map_geojson() for Leaflet / OSM
roadgraph_builder/utils/geo.py meters ↔ WGS84; load_wgs84_origin_json
.github/ABOUT.md Short text + topics for GitHub About

Future extensions

  • Real-data camera integration (user-driven)project-camera works on any pixel-detections JSON + calibration. The shipped demo is synthetic with embedded ground truth so round-trip accuracy is provable; users wanting to run it on real photography can follow docs/camera_pipeline_demo.md. This repo intentionally does not ship imagery with viral license terms (e.g. Mapillary CC-BY-SA) so the MIT distribution stays unambiguous.
  • LiDAR-derived lane centerlines — today fuse-lidar fits the boundary ribbon from nearby points; a future pass could infer per-lane centerlines directly from the cloud.
  • Semantics layer — broader lane-type / rule / priority model (separate from raw geometry) beyond the current attributes.hd.semantic_rules free-form dict.
  • HD-complete Lanelet2 export — the current export_lanelet2 emits per-lane relations, directed lane_connection pairs, lane-change relations, traffic-light relations, and stop-line ways where inputs exist; richer right-of-way / priority modeling is still future work.

Codebase TODOs also mention: graph fusion across tiles/modalities and routing graph generation for other pathfinders (A* / Contraction Hierarchies).

Sample bundle

A frozen reference output lives at examples/frozen_bundle/ so you can browse the shape of export-bundle without running the pipeline (manifest.json, nav/sd_nav.json, sim/{road_graph,map,trajectory}, lanelet/map.osm). The frozen build runs the full optional pipeline — HD-lite enrich, LAS-fused LiDAR boundaries, camera detections, and turn restrictions — so the output reflects the maximal artifact shape. Regenerate with make release-bundle or bash scripts/build_release_bundle.sh; the script also packs dist/roadgraph_sample_bundle.tar.gz + a sha256 file suitable for attaching to a release.

The frozen bundle doubles as a release contract. Stable generated artifacts are byte-compared in tests. manifest.json is normalized only for roadgraph_builder_version and generated_at_utc; origin, inputs, graph stats, junction counts, optional-source metadata, and output paths must match the checked-in frozen manifest unless the release surface intentionally changes.

Every v* tag push triggers .github/workflows/release.yml, which runs the same script and attaches the tarball + sha256 to the auto-created GitHub Release.

API docs (pdoc)

make docs renders docstrings into a static site under build/docs/. Requires the [docs] extra (pip install -e ".[docs]"). Open build/docs/roadgraph_builder.html in a browser. Not deployed to a public URL — this is for local reference only.

Shell completion

Hand-written bash and zsh completion scripts live under scripts/completions/. They complete the subcommands and the common --turn-restrictions-json / --output / --origin-* / --lidar-points style path arguments. The completion smoke test derives the expected subcommands from the argparse parser, so future CLI additions should update the scripts in the same change instead of drifting.

# Bash (per-user)
mkdir -p ~/.local/share/bash-completion/completions
cp scripts/completions/roadgraph_builder.bash \
   ~/.local/share/bash-completion/completions/roadgraph_builder

# Zsh (per-user)
mkdir -p ~/.zsh/completions
cp scripts/completions/_roadgraph_builder ~/.zsh/completions/
# then in ~/.zshrc:
#   fpath=(~/.zsh/completions $fpath)
#   autoload -Uz compinit && compinit

roadgraph_builder --version (or -V) prints the installed package version.

Releases

Changes are listed in CHANGELOG.md.

Tag and push a version (example v0.1.0):

git tag -a v0.1.0 -m "Release 0.1.0"
git push origin main
git push origin v0.1.0

The release workflow above then builds and attaches roadgraph_sample_bundle.tar.gz + roadgraph_sample_bundle.sha256 to the auto-generated release notes.

PyPI (optional)

The distribution name in pyproject.toml is roadgraph-builder.

Manual publish

  1. Create a PyPI account and an API token with upload permission for this project.
  2. Install build tools: python -m pip install build twine.
  3. From a clean checkout: python -m build then twine upload dist/* (use API token when prompted).

Workflow scaffold (Trusted Publisher, no secrets)

.github/workflows/pypi.yml is a workflow_dispatch-only scaffold that builds sdist + wheel and publishes via pypa/gh-action-pypi-publish. To enable, configure Trusted Publishers on the PyPI project (workflow: pypi.yml, environment: pypi) and add a matching GitHub Environment. No tokens live in this repository.

Contributing

See CONTRIBUTING.md for dev setup, test commands, and the conventions used across the repo (one-topic commits, no Co-Authored-By trailers, schema discipline, data hygiene).

License

Released under the MIT License. © 2026 Ryohei Sasaki.

About

Build road graphs from GPS trajectories, OSM, LiDAR, and camera data. Exports navigation JSON, simulation GeoJSON, and Lanelet2-style OSM with routing, turn restrictions, reachability, and validation.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors