No description
  • Go 92.4%
  • Python 6.2%
  • Java 0.8%
  • Shell 0.6%
Find a file
2026-10-29 18:30:50 +00:00
assets vibecoded :stamp: 2026-10-01 16:02:35 +02:00
cmd/broom api: serve track annotation as POST /v1/annotate (kata n47k) 2026-10-29 18:30:50 +00:00
docs api: serve track annotation as POST /v1/annotate (kata n47k) 2026-10-29 18:30:50 +00:00
e2e routing: stitch alignment pieces into continuous tracks 2026-09-25 20:08:39 +02:00
examples routing: add sparse-track inference and road alignment 2026-09-24 20:36:39 +02:00
internal api: serve track annotation as POST /v1/annotate (kata n47k) 2026-10-29 18:30:50 +00:00
LICENSES release: reset repository with LFS fixtures for v0.9.0 2026-09-24 17:43:46 +02:00
pkg/routing api: keep recently used regions open; reopen region pairs locally (kata 62w2) 2026-10-20 22:00:43 +00:00
scripts graph format 2, build failure cooldown and build memory release (kata gg3c f931 tpy0) 2026-10-20 16:22:42 +00:00
testdata graph format 2, build failure cooldown and build memory release (kata gg3c f931 tpy0) 2026-10-20 16:22:42 +00:00
tools graph format 2, build failure cooldown and build memory release (kata gg3c f931 tpy0) 2026-10-20 16:22:42 +00:00
.gitattributes experiments: compare sparse-track inference and road routing 2026-09-24 18:49:14 +02:00
.gitignore update gitignore 2026-10-20 22:59:09 +02:00
.kata.toml release: reset repository with LFS fixtures for v0.9.0 2026-09-24 17:43:46 +02:00
AGENTS.md scripts: add test and test-oracles runners 2026-09-27 23:50:25 +02:00
broom-design.md routing: alternative routes and track variations 2026-09-26 17:25:36 +02:00
go.mod api: persist authenticated monitoring and accounting (aaww) 2026-09-27 15:48:22 +02:00
go.sum api: persist authenticated monitoring and accounting (aaww) 2026-09-27 15:48:22 +02:00
LICENSE release: reset repository with LFS fixtures for v0.9.0 2026-09-24 17:43:46 +02:00
Nakedfile nakedfile 2026-09-28 01:04:50 +02:00
NOTICE.md release: reset repository with LFS fixtures for v0.9.0 2026-09-24 17:43:46 +02:00
README.md api: serve track annotation as POST /v1/annotate (kata n47k) 2026-10-29 18:30:50 +00:00

broom

Vibecoded: some or all of this code was written by AI and accepted on vibes

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 five original profiles: walk, mtb, drive, offroad, and enduro.
  • 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.

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.