No description
  • Go 95.7%
  • Python 2.6%
  • Java 1.5%
  • Shell 0.2%
Find a file
2026-09-10 21:08:38 +02:00
cmd/broom routing: consolidate public API under pkg/routing 2026-09-10 19:32:38 +02:00
docs oracles: validate source endpoints and independent failure parity 2026-09-10 20:50:48 +02:00
examples/router examples: add four-waypoint router 2026-09-08 23:35:14 +02:00
internal oracles: validate source endpoints and independent failure parity 2026-09-10 20:50:48 +02:00
LICENSES oracles: validate full-route provenance and document release foundations 2026-09-10 18:25:57 +02:00
pkg/routing routing: consolidate public API under pkg/routing 2026-09-10 19:32:38 +02:00
testdata route: harden correctness and validate BRouter comparisons 2026-09-08 12:16:53 +02:00
tools/oracles oracles: validate source endpoints and independent failure parity 2026-09-10 20:50:48 +02:00
.gitignore update gitignore 2026-09-10 21:08:38 +02:00
.kata.toml Initial commit: broom offline routing engine 2026-09-03 17:49:43 +02:00
broom-design.md oracles: validate source endpoints and independent failure parity 2026-09-10 20:50:48 +02:00
go.mod data: harden content-addressed cache 2026-09-06 22:28:48 +02:00
go.sum Initial commit: broom offline routing engine 2026-09-03 17:49:43 +02:00
LICENSE oracles: validate full-route provenance and document release foundations 2026-09-10 18:25:57 +02:00
NOTICE.md routing: consolidate public API under pkg/routing 2026-09-10 19:32:38 +02:00
README.md routing: consolidate public API under pkg/routing 2026-09-10 19:32:38 +02:00

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 .brf profile support, runtime overrides, and four original profiles.
  • Elevation sampled at graph build time.
  • Managed Geofabrik downloads and graph caching.
  • GPX and GeoJSON export, a CLI, and an OSRM-shaped debug HTTP API.

Status: pre-release. The implementation works, but the public API is not frozen and routing-quality targets are still being evaluated. See current limitations before depending on it.

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 github.com/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

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

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"

    "github.com/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 repeated or offline queries, call manager.Open(graphPath) and reuse the returned router; call Close() when finished. SetupRegion also returns a caller-owned router. Managed setup revalidates source metadata, so it is not an offline-open API.

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.

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.