- Go 92.4%
- Python 6.2%
- Java 0.8%
- Shell 0.6%
| assets | ||
| cmd/broom | ||
| docs | ||
| e2e | ||
| examples | ||
| internal | ||
| LICENSES | ||
| pkg/routing | ||
| scripts | ||
| testdata | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| .kata.toml | ||
| AGENTS.md | ||
| broom-design.md | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Nakedfile | ||
| NOTICE.md | ||
| README.md | ||
broom
An offline OpenStreetMap routing engine in Go. Build a graph from an OSM PBF, open it as a library, and calculate routes for walking, mountain bikes, cars and trail motorcycles. No routing server required.
- Pure Go; builds with
CGO_ENABLED=0. - Memory-mapped, profile-independent graphs and per-profile metrics.
- BRouter
.brfprofile support, runtime overrides, and five original profiles:walk,mtb,drive,offroad, andenduro. - Elevation sampled at graph build time.
- Managed Geofabrik downloads and graph caching.
- Offline region discovery, generation pins, cache inventory and safe pruning.
- Geometry-aligned road tags and baked elevations; local adjacent-extract unions.
- Offline track annotation with direction, continuity and explicit unmatched/ambiguous spans, preserving the original coordinates, also served over HTTP for surface breakdowns of GPX tracks.
- Opt-in sparse-track inference and road alignment, with separate reconstruction/planning intent, owned proposals and explicit gaps, a calibrated preset for simplified exports and stitching into continuous tracks.
- Alternative routes, including variations that diverge from a recorded GPX track.
- Country-scale build, customization and routing on a laptop.
- GPX and GeoJSON export, a CLI, and a routing HTTP API with optional bearer authentication, plus an OSRM-shaped debug server.
Status: pre-release. The public API has a documented compatibility and deprecation window; a v1 guarantee and routing-quality acceptance remain pending. See current limitations.
Install
Requires Go 1.27 or newer. From a checkout:
CGO_ENABLED=0 go build -o broom ./cmd/broom
./broom --help
Or install the current published source revision:
CGO_ENABLED=0 go install code.rbel.co/rubiojr/broom/cmd/broom@latest
The executable goes into GOBIN, or $(go env GOPATH)/bin by default. For
repeatable builds, select a specific published version or commit instead of
@latest. Java and containers are needed only for optional oracle experiments.
First route
This downloads the smallest Geofabrik region covering the two waypoints, fetches elevation, builds a graph, and writes a walking route through Monaco:
./broom route --profile walk \
--from 7.4197,43.7384 --to 7.4276,43.7392 \
--format geojson --output monaco.geojson
Coordinates are always longitude, latitude. Distances and elevations are metres; durations are seconds. The first managed route needs network access and can take much longer than subsequent queries. Elevation downloads can be much larger than a small region's PBF.
For an explicit region and reusable graph:
./broom setup --profile walk monaco
Keep the graph path from the setup output. Passing it to route uses the local
graph directly, without revalidating managed downloads:
./broom route --profile walk \
--from 7.4197,43.7384 --to 7.4276,43.7392 \
--format gpx --output monaco.gpx /path/to/monaco.broom
CLI GPX and GeoJSON exports include available baked elevations automatically.
Unknown elevations omit GPX <ele> elements and leave GeoJSON positions 2D.
For a quick checkout smoke test without downloads, use the pinned Andorra
fixture. Run this in the repository root; andorra.broom should be a new file:
gzip -dc testdata/andorra-routing.broom.gz > andorra.broom
./broom route --profile mtb \
--from 1.53034,42.498586 --to 1.522465,42.504917 \
--format geojson --output andorra.geojson andorra.broom
HTTP API
Serve routes, alternatives, snapping and track annotation with regions acquired from OMS on demand:
./broom api --oms-server https://oms.rbel.co
curl 'http://127.0.0.1:8080/v1/route?profile=mtb&from=1.53034,42.498586&to=1.522465,42.504917'
Broom downloads OSM extracts and elevation from OMS and builds/caches graphs
locally. The first request for a region can take minutes. For a prepared local
graph instead, run ./broom api andorra.broom --warm.
The default listener is 127.0.0.1:8080, with limits of 120 requests per minute
per client IP and 8 concurrent requests. Set --token-file, --token or
BROOM_API_TOKEN to require bearer authentication. See the
HTTP API guide for JSON, GeoJSON and GPX requests,
limits, browser access and deployment options.
The server also keeps persistent SQLite accounting
and exposes /v1/stats and /v1/accounting with always-required, separate admin
authentication. Configure --admin-token-file and a persistent --accounting-db
to monitor health, usage and abuse. scripts/stats shows the remote deployment's
report over SSH.
Library
pkg/routing manages region selection, data acquisition, graph building and
queries. This is the shortest route from coordinates to a result:
package main
import (
"context"
"fmt"
"log"
"code.rbel.co/rubiojr/broom/pkg/routing"
)
func main() {
manager, err := routing.New(routing.Options{})
if err != nil {
log.Fatal(err)
}
result, err := manager.Route(context.Background(), routing.Request{
Profile: "walk",
Waypoints: []routing.Waypoint{
{Point: routing.Point{Lon: 7.4197, Lat: 43.7384}},
{Point: routing.Point{Lon: 7.4276, Lat: 43.7392}},
},
}, routing.RouteOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%.0f metres, %.0f seconds\n", result.Route.Distance, result.Route.Duration)
fmt.Println("graph:", result.GraphPath)
}
Manager.Route closes its internal router before returning. For an application,
prepare once with EnsureRegion, persist the canonical region/generation identity,
then use OpenRegion to reopen offline and reuse its caller-owned router. Close
it when finished. CachedRegions, CacheInfo and Prune expose local data
management. Existing path-based manager.Open(graphPath) remains supported.
SetupRegion retains explicit provider-refresh behavior.
Start with the integrator manual for the complete prepare → offline query → update → prune workflow, route colouring, elevations, custom providers and adjacent-extract preparation.
For direct control of local graphs, use
routing.Open(graphPath, routing.RouterOptions{}) → Router.Route(ctx, request)
→ Router.Close(). routing.Build builds from local PBF/HGT inputs.
routing.LoadProfile / routing.ParseProfile configure custom profiles;
routing.WriteGPX / routing.WriteGeoJSON export results.
pkg/routing is the sole public library package. The four-waypoint example
is runnable with go run ./examples/router and uses managed downloads.
Profiles and local data
# Build once. The HGT directory may use flat or latitude-band subdirectories.
./broom build --output region.broom --elevation /path/to/hgt region.osm.pbf
# Prepare an MTB metric before the first query.
./broom customize --profile mtb region.broom
# Load a custom BRF or change a global variable.
./broom route --profile mtb --set mtb_hard_factor=2 \
--from 1.53034,42.498586 --to 1.522465,42.504917 region.broom
build --elevation auto downloads elevation; --elevation none builds without
it. Choose deliberately: changing elevation requires rebuilding the graph.
--slim omits names and OSM way IDs. Profile changes require new metrics, not
new topology. See usage and compatibility.
Development and evidence
go test ./...
go vet ./...
staticcheck ./...
The normal tests use local fixtures. Optional Java and country-scale tests document their required inputs and skip when those inputs are absent.
- Current status and v1 work
- Integrator manual
- Usage, ownership and compatibility
- Migration to the consolidated API
- Release notes and upgrade steps
- BRF conformance
- Elevation model
- Performance: country-scale numbers and knobs
- Metric customization performance
- Turn restrictions
- Oracle harness
- Full-route Andorra evidence
- Design and targets
License
Broom's original code and profiles are MIT-licensed. The bundled BRouter lookup table retains its upstream MIT notice. OpenStreetMap-derived fixtures and graph data have separate ODbL terms. See NOTICE.md for attribution and third-party material.