Skip to content

feat: elevation_tool for ground elevation and climb along a route - #293

Open
mattpodwysocki wants to merge 9 commits into
mainfrom
feat/elevation-tool
Open

mattpodwysocki wants to merge 9 commits into
mainfrom
feat/elevation-tool

Conversation

@mattpodwysocki

Copy link
Copy Markdown
Contributor

Part 4 of #289. The server had no elevation at all, so "how hilly is this bike route?" or "how high is this town?" had no answer from Mapbox data.

What it does

elevation_tool takes one of:

For a line it returns distance, climb, descent, start, end, highest and lowest points, and a profile of up to 50 points (12 in the text). It also returns a map ref with the line and ▲/▼ markers at the high and low points.

Why DEM tiles and not Tilequery

Tilequery can't read mapbox.mapbox-terrain-dem-v1 (422), and Terrain v2 contours come in 10 m steps or coarser:

Point Terrain v2 contours DEM tiles
Near Twin Peaks, SF 240 m 249.5 m
Civic Center, SF 10 m 20.9 m
Longs Peak summit 4,346 m (surveyed 4,346 m)

So the server fetches the DEM's .pngraw tiles (terrain-RGB) and decodes heights, with bilinear interpolation between pixels. The PNG decoder is ~100 lines on Node's zlib (8-bit RGB/RGBA, not interlaced), so no new dependency.

Cost

  • A path is sampled at up to 500 points, never closer than one DEM pixel.
  • Samples share tiles, and each tile is fetched once per call.
  • On long paths the zoom drops until the path needs at most 48 tiles. The result reports zoom and resolution_m so the model can say how rough it is.
  • Swings under 2 m don't count toward climb, so DEM noise on flat ground doesn't add up.

Live:

Call Result
Ferry Building to Twin Peaks by bike 274 m climb, 1 tile, 7.5 m resolution, 0.6 s
SF to LA by car high point 1,276 m near Tejon Pass, ~120 m resolution, 1.3 s

Open on #289

Whether describe_tileset_tool and tilequery_tool come next, and whether they're on by default, is waiting on the team (comment on #289). This tool is narrow, so it's on by default.

Testing

  • A PNG encoder in the tests covers all five row filters, RGB and RGBA, and bad input (not a PNG, 16-bit, palette, truncated).
  • Synthetic terrain tiles test point heights, interpolation, climb along a path, a directions ref, the zoom choice for a long route, the noise threshold, a refused token, and that -0 m never prints.
  • npm test: 1102 pass. Not yet tried in Claude Desktop or ChatGPT.

🤖 Generated with Claude Code

Reads Mapbox Terrain-DEM v1 raster tiles and decodes heights on the
server (a small PNG decoder on node:zlib, no new dependency). Takes up
to 25 points, a path, or a directions_tool ref, and returns elevations
or climb, descent, high and low points and a profile, plus a map ref.
Long paths drop to a coarser zoom so a call needs at most 48 tiles.

Part of #289

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mattpodwysocki
mattpodwysocki requested a review from a team as a code owner October 20, 2026 23:57
@mattpodwysocki mattpodwysocki added the enhancement New feature or request label Oct 20, 2026
mattpodwysocki and others added 3 commits October 20, 2026 19:57
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mattpodwysocki and others added 5 commits November 6, 2026 12:19
directions_ref becomes route_ref, which also takes a map_matching_tool
ref, so "how much did I climb on this run?" works from a GPS trace as
well as a planned route. Matched pieces of a trace with gaps are joined
in order. mapMatchingRequestFromRef is exported next to
directionsRequestFromRef, and only accepts map_matching_tool's own
profiles since the profile goes into the URL path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… at the point (#324)

* feat(ground_location_tool): land use, water and elevation at the point

ground_location_tool now says what the ground is at the coordinate: land
use from Mapbox Streets v8 (park, cemetery, school...), a protected area
it's in (with the name, e.g. a national park), a building it's inside,
whether it's on water and how far the nearest water is, plus elevation
from Terrain-DEM via elevation_tool's tile reader. Two Tilequery requests
and one DEM tile, in parallel with the existing calls; a failure leaves
those fields out.

Adds tool-selection eval cases for land use and elevation questions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: CHANGELOG entry for #324

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ground_location_tool): include picks land, water, elevation or traffic

The spot facets are now chosen with include, so a call only pays for
what the question needs (default: land, one Tilequery request), and a
new layer is a new include value rather than a new tool. Adds traffic:
live congestion per road class within 100 m from Mapbox Traffic v1.
Water moves out of land into its own facet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ground_location_tool): address review feedback

- A facet request that throws or returns bad JSON is left out instead of
  failing the whole call.
- Wetlands from landuse_overlay are land use; only national parks are
  protected areas.
- include defaults to none, so existing calls make no extra request.
- Traffic distance_m points at the worst segment for each road class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the CHANGELOG conflict with 0.17.1-dev.1 and restores the
elevation_tool entry (#293) that the #324 squash merge dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant