Skip to main content
Glama

bike-mapper

A road-cycling route planner for Dublin and Wicklow that ranks routes by how often you will have to stop, not just by how far you ride. It plans loops, out-and-backs and one-way rides to a target length, can seek out the hardest climbs or avoid them, keeps you off main roads if you ask, and exports GPX files that import straight into Garmin Connect. It runs as a local web page and as an MCP server, so you can plan a ride by talking to Claude Code.

Everything is built on open data: OpenStreetMap, the Copernicus GLO-30 elevation model and BRouter. The project started as a feasibility study (see Research) into whether open data can predict where a cyclist stops; the planner is what came out of it.

What it does

  • Fewest-stops routing. Every candidate route is scored against a directional index of stop causes derived from OSM (traffic signals, stop and give-way signs, roundabout entries, junction priority) and a per-class stop model, giving expected stops, stops per km and expected stopped time.

  • Three ride shapes with a length target. loop, out_and_back (same road back, or a free return) and one_way, with length_km or an explicit min_km/max_km range. Loops are searched over ring and petal via-point shapes so they still close from awkward starts such as the Howth peninsula.

  • Three objectives. flow (fewest stops and turns), climb (most and hardest climbing, via-points picked from the DEM) and flat.

  • Tunable routing. Lane bonus, turn cost, lane-exit cost, cycleway weight and a main_road_penalty (1 to 5) that trades distance for quieter roads, with presets (straight & lanes, plain flow, quiet roads, climb, flat). Ferries are never used.

  • Route explanation. Per route: km by road class, km on cycle infrastructure, main-road km, stop events by cause, turns, detected climbs (length, gain, grade), ascent and descent from DEM-sampled elevations.

  • GPX export with elevation, ready for Garmin Connect (Training & Planning > Courses > Import).

  • Web UI (Leaflet, single file) with click-to-pick start and end, place search, sliders for every setting, route cards and a cycle-infrastructure overlay.

  • MCP server exposing plan_route, route_details, export_gpx, geocode, list_presets, pick_points, status and start_brouter.

Related MCP server: livedot MCP

Quick start

Prerequisites: uv (brew install uv), Java 17+ (for BRouter), curl, and Node (only for the UI syntax check).

make setup           # uv sync
make planner-setup   # once: downloads BRouter 1.7.10 + Ireland tiles into data/brouter/, builds the flow index
make brouter         # terminal 1: BRouter on http://localhost:17777
make planner         # terminal 2: planner on http://localhost:8081

Open http://localhost:8081, click a start and an end (or search by name) and pick a mode. make planner-check runs pytest, ruff and the UI syntax check.

Data you need to provide

The data/ directory is gitignored apart from small samples and the stop model. Before make planner-setup you need:

path

what

where from

data/osm/ireland.osm.pbf

OSM extract

Geofabrik

data/osm/events_ireland.csv

stop-cause events

scripts/osm_stop_features.py over the PBF, see docs/planner.md

data/dem/*.tif

Copernicus GLO-30 tiles (optional, needed for climb/flat)

AWS Open Data, see docs/planner.md

BRouter's routing tiles are downloaded by the setup script. The committed stop model (data/model/stop_model.json) defaults to a literature-based prior; the empirical weight sets were derived offline from one rider's history, which is not in the repo.

Use it from Claude Code

The repo ships a project-scoped .mcp.json that registers the planner as the flow-planner MCP server (uv run scripts/flow_mcp.py). Start Claude Code in the repo, approve the server, make sure BRouter is running (or ask for start_brouter), then:

Plan a 60 km climbing loop from Rathfarnham.

Out and back from Howth to Donabate, 50 to 60 km, keep me off the main roads.

Export the best one for my Garmin.

Details, the full tool table and the evaluation results are in docs/mcp.md.

How it works

  1. The web page or MCP tool calls PlannerService in scripts/flowplan/.

  2. BRouter is asked for candidates with the custom profiles/flow.brf profile: four cost variants times four alternatives per leg, plus via-point sets for loops and out-and-backs, under a call budget.

  3. Near-duplicate candidates are dropped, the rest are matched to the flow index (data/flow/ireland.index.csv) and scored by the stop model.

  4. Routes are ranked by the objective, climbs are detected on a DEM-sampled profile, and the top n are returned with explanations and GPX.

docs/planner.md covers the architecture, the stop model, the BRouter profile, loop geometry, elevation sources and known limits.

Repository layout

path

contents

scripts/flowplan/

planner library: BRouter client, candidates, scoring, ranking, climbs, terrain (DEM), turns, GPX, service

scripts/flow_server.py

FastAPI web server and /api/plan, /api/route

scripts/flow_mcp.py

MCP server (FastMCP, stdio)

scripts/*.py

data pipeline and research scripts (OSM features, flow index, stop-model validation, evals)

profiles/flow.brf

the BRouter profile with node costs per stop cause, lane and main-road factors

web/index.html

the planner page

tests/

pytest suite (BRouter is mocked; no network needed)

docs/planner.md, docs/mcp.md

planner and MCP documentation, evaluation tables

docs/research/

the feasibility study, one note per question

docs/map/

Leaflet viewer over the study's sample outputs

data/samples/, data/model/

small committed data: eval CSVs, sample GPX, the stop model

.beads/

issue tracker export (see below)

Research

The planner rests on a set of research notes in docs/research/, each following one template, starting from the feasibility summary: OSM tag coverage in Ireland, OSM stop-cause features, empirical stop detection from ride GPS, validation of the stop model against observed stops, open elevation models checked on the Wicklow Gap, open climb datasets, motor-traffic counts, routing engines compared on node-level costs, and open sources for stop priors.

Development

  • make planner-check is the gate: pytest, ruff check, and a syntax check of the inline UI script.

  • Evaluations: uv run scripts/plan_eval.py (loop, out-and-back and one-way scenarios against a live BRouter) and uv run scripts/planner_eval.py (one-way pairs). Results are committed under data/samples/ and discussed in the docs.

  • Issues are tracked with beads (bd). The versioned export is .beads/issues.jsonl; run bd prime for the workflow. The commit history is one commit per bead, so git log reads as the project's change log.

  • The code was written with Claude Code acting as implementer and reviewer under a human orchestrator; CLAUDE.md and AGENTS.md hold the agent instructions.

Data licences

  • Map data (c) OpenStreetMap contributors, ODbL. Derived tables (the flow index, the stop-cause events, the committed samples) are derived databases under the ODbL.

  • Elevation: Copernicus DEM GLO-30, (c) European Union, Copernicus free licence.

  • Routing: BRouter (MIT), downloaded at setup, not vendored.

Licence

MIT, see LICENSE.

Available Tools

8 tools
export_gpxB

Write a planned route as GPX 1.1 (with elevation) into out_dir (must exist; absolute or ~).

Inputs: route_id (required), name, out_dir. Returns: result

geocodeB

Look up an Irish place name (Nominatim). Returns candidates with name and lat,lon.

Inputs: query (required), limit. Returns: result

list_presetsA

Settings presets (straight & lanes, plain flow, climb, flat) as dicts: pass settings to plan_route and objective as plan_route's objective argument.

Inputs: no named parameters. Returns: result

pick_pointsA

Ask the user to click 1 or 2 points on the planner map (opens the browser at the planner web page, which needs make planner running) and wait for them. Returns 'lat,lon' strings to use as start / end in plan_route.

Inputs: n, timeout_s. Returns: result

plan_routeA

Plan routes. mode: loop | out_and_back | one_way. start/end: 'lat,lon' or a place name (geocoded; the resolved place is echoed). Loops and out-and-backs need length_km (+-10%) or min_km and max_km; one_way needs end and ignores length. objective: flow (fewest stops/turns) | climb | flat. return_mode: same | free. settings: optional dict from list_presets (e.g. turn_cost, lane_bonus, weights; main_road_penalty 1..5 multiplies the cost of primary, secondary and trunk roads, 2.5 = 'quiet roads' preset). Loops take 30-60 s. Returns a markdown table of ranked routes (use the ids with route_details / export_gpx) plus plan notes.

Inputs: mode (required), start (required), n, end, +7 more. Returns: result

route_detailsB

Explain a planned route: km by road class, cycle-infrastructure km, stop events, climbs, turns.

Inputs: route_id (required). Returns: result

start_brouterA

Start the local BRouter server (scripts/run_brouter.sh serve) if it is not reachable; waits up to 60 s.

Inputs: no named parameters. Returns: result

statusC

BRouter reachability, region, stop-model weight set, flow index size and DEM coverage.

Inputs: no named parameters. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 8 tools

    Disambiguation5/5

    Each tool has a clearly distinct role in the route-planning workflow: planning, inspecting, exporting, presets, point picking, geocoding, status, and starting BRouter. There is no meaningful overlap; plan_route returns route IDs while route_details and export_gpx consume them.

    Naming Consistency4/5

    Most names follow a predictable snake_case verb_noun or noun pattern (route_details, export_gpx, list_presets, pick_points, start_brouter, plan_route). A few deviations like status and geocode are single-word but still clear and readable.

    Tool Count5/5

    Eight tools is well-scoped for a route-planning server, with each tool covering a necessary step from setup to output. No tool feels redundant or missing from the count.

    Completeness4/5

    The surface covers the core lifecycle: discover places, configure settings, plan routes, inspect details, and export GPX. Minor gaps exist around managing multiple saved routes or re-inspecting previously planned routes beyond the immediate result set.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Provides Claude Code and Claude Desktop with safe, read-only access to your local Garmin training data, readiness decisions, run details, and trends so you can get coaching advice in natural language.
      15
      164 PyPI
      9
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables users to ask ChatGPT or Claude where a runner or cyclist is in a live race, how the race is going, and what the weather along the course will be, returning live maps and 3D route flyovers inline in the chat. Also surfaces cycling tour day-by-day routes, GPX links and organiser notes, all read-only and free.
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables Claude to generate mountain bike loops or point-to-point routes from a place name or coordinates, computed on real OpenStreetMap trails by a routing engine and saved as GPX files with elevation, along with a summary and warnings about traffic, steep grades, protected areas and forbidden access. It also lists previously saved routes.
      2
      MIT
    • A
      license
      Not graded
      quality
      Not graded
      maintenance
      Connects Garmin training data to Claude so it can read recent activities, wellness and recovery signals, and upcoming calendar events. It also lets Claude create, update, and delete structured workouts that sync through intervals.icu to Garmin Connect and onto the user's watch.
      MIT