OpenRoadCode Map Builder

Source: tools/map_builder/README.md

Reproducible Debian-container workflow for generating the offline map and routing data consumed by OpenRoadCode.

It automates the previously manual chain: discover Geofabrik regions, download and validate OSM PBF extracts, merge selected regions when necessary, build MapLibre-compatible MBTiles with tilemaker, install the OpenRoadCode map style and offline glyphs, build Valhalla routing data, validate all generated artifacts, write a build manifest, and publish/deploy the result as a /srv/openroadcode dataset.

The recommended production model uses a dedicated map-build machine. The vehicle does not compile its own map data; it pulls validated datasets from the build machine with scripts/runtime/pull_navigation_data.sh. See Navigation Build and Deployment.

Toolchain

The toolchain is pinned in toolchain.lock to specific tilemaker, Valhalla, glyph, and Debian versions. A container engine is used only as a build/data-compilation environment; the OpenRoadCode runtime does not require one. The scripts use Docker by default. To use Podman, set CONTAINER_ENGINE=podman:

CONTAINER_ENGINE=podman ./scripts/build-image.sh
CONTAINER_ENGINE=podman ./scripts/run-builder.sh tui

Build image

cd tools/map_builder
./scripts/build-image.sh

Interactive region selector

./scripts/run-builder.sh tui

The runner bind-mounts the local builder/ and templates/ directories into the container, so Python, TUI, and style-template edits are available immediately. Rebuild the image only after changing the Dockerfile, toolchain versions, or container-installed dependencies.

Controls: Up/Down and PageUp/PageDown navigate, Right expands or collapses a region group, Left collapses or moves to its parent, Space selects, / searches, c clears the search, Enter accepts the selected regions, and q quits. b also accepts the selection. Parent/child region combinations are rejected to prevent duplicate map data.

The last accepted selection is stored in .cache/selected-regions.json. On the next run, regions that still exist in the current Geofabrik index are selected with [x], and their parent groups are expanded so they are visible. Quitting with q leaves the previous accepted selection unchanged.

Non-interactive build

./scripts/run-builder.sh build --regions north-america/us/michigan

Multiple regions are comma separated:

./scripts/run-builder.sh build --regions north-america/us/michigan,north-america/us/ohio

After a successful interactive or non-interactive build, the builder reports the selected region names, their combined source PBF size, total deployable output size, elapsed build time, and output path.

List known Geofabrik IDs with:

./scripts/run-builder.sh list

Generated output

The host build-output/ directory is mounted in the container as /srv/openroadcode, so generated file URLs and Valhalla paths are identical during validation and after deployment.

build-output/
├── build-manifest.json
├── maps/
│   ├── source/
│   ├── vector/openroadcode.mbtiles
│   ├── glyphs/
│   ├── styles/openroadcode.json
│   └── routes/
└── valhalla/
    ├── valhalla.json
    ├── admins.sqlite
    ├── timezones.sqlite
    ├── tiles/
    └── tiles.tar

maps/routes/ is runtime/debug space. Routes are sent dynamically to the native map renderer rather than generated as part of the base dataset. Vehicle-side deployment preserves this directory across dataset updates.

The canonical style name is openroadcode.json; runtime code should not depend on a region-specific filename.

Validation

Validation runs automatically after a build. It checks source PBFs with osmium, MBTiles SQLite integrity and required vector layers, style JSON and runtime sources, glyph presence, Valhalla databases/tiles/extract, an optional valhalla_service /status smoke test, and SHA-256 checksums for key artifacts.

Run validation again with:

./scripts/validate-host.sh

A dataset is not considered deployable without a validated build-manifest.json.

Publish on the map-build machine

The recommended vehicle-pull model publishes the latest validated dataset at /srv/openroadcode on the map-build machine:

./scripts/deploy-to-srv.sh

The deployment script refuses to install an output tree without a validated build-manifest.json. It synchronizes generated data into /srv/openroadcode while preserving maps/routes/ as runtime/debug space.

The vehicle can then preview and pull that dataset over SSH:

./scripts/runtime/pull_navigation_data.sh \
  --source mapbuilder@MAP_HOST:/srv/openroadcode \
  --dry-run

./scripts/runtime/pull_navigation_data.sh \
  --source mapbuilder@MAP_HOST:/srv/openroadcode

These commands are run from the OpenRoadCode repository on the vehicle, not from tools/map_builder on the build host.

SSH key authentication is recommended. For the pull model, the vehicle only requires read access to the published build-machine dataset; the build machine does not need privileged SSH access to the vehicle.

Optional push deployment

deploy-to-srv.sh --remote remains available for development or manually managed targets:

./scripts/deploy-to-srv.sh --remote openroad@192.168.1.50
make deploy REMOTE=openroad@192.168.1.50

For production vehicle updates, prefer the Pi-initiated pull workflow because it controls update timing, stages and validates the incoming dataset, retains the previous dataset, and can roll back after a failed Valhalla restart.

Cache and scratch data

.cache/ stores downloaded Geofabrik data, .scratch/ stores intermediate build data, and build-output/ contains only the deployable result. These directories are intentionally ignored by Git.

Tests

make test

The included tests cover Geofabrik region parsing/selection rules and MapLibre style installation/validation.

Attribution

Generated datasets are based on OpenStreetMap/Geofabrik data and use open-source tilemaker, Valhalla, and glyph assets. Downstream applications must preserve the applicable licenses and attribution requirements.