Skip to content

OSM hiking & cycling trail routing for Land mode#41

Open
radumarias wants to merge 5 commits into
mainfrom
claude/land-trail-routing
Open

OSM hiking & cycling trail routing for Land mode#41
radumarias wants to merge 5 commits into
mainfrom
claude/land-trail-routing

Conversation

@radumarias
Copy link
Copy Markdown
Member

Summary

Re-implements #33 from scratch on current main. The original PR had heavy merge conflicts from the wind/compass/GPS work that landed since. The trail routing logic (osm-land.ts) is identical to the original — this is a clean application onto the current codebase.

Closes #33.

What it does

When Land mode is active, each route leg now snaps to OpenStreetMap hiking/cycling trails instead of drawing a straight line. Supported way types:

  • highway=path | footway | track | bridleway | cycleway | pedestrian | steps
  • foot=designated or bicycle=designated

Pipeline

Layer What
src/lib/server/osm-land.ts Queries Overpass for trail ways, builds a geojson-path-finder graph, snaps endpoints (≤5 km), returns routed polyline + length + hike/bike way counts
/api/route?land=1 Calls computeLandTrailRoute first, falls back to straight-line
/api/multi-route?land=1 Each leg attempts trail routing; trailLegs count in response
RouteView Shows 🥾 Trail route via OpenStreetMap: X km (Y hike · Z bike ways)
WaypointsView Shows 🥾 N trail in the track summary

Constraints

  • MAX_SPAN_DEG = 1.5 (~165 km) — wider trips bail early
  • MAX_SNAP_KM = 5 — endpoints must be within 5 km of a trail
  • Graduated bbox padding keeps Overpass payloads bounded
  • 24h cache via the existing cached() helper

Verified

  • pnpm check — 0 errors
  • pnpm test — 91/91 pass
  • pnpm build — succeeds
  • API smoke test: /api/route?from=13.7308,100.5418&to=13.7253,100.5435&land=1 (Lumpini Park, Bangkok) → kind: 'trail', 0.91 km, 3755 hike + 79 bike ways

Preview

https://weather-voodoo-2vuhhzarh-xorio-s-projects.vercel.app

i18n

New keys route.{trailPrefix, trailWaysSuffix, trailLabel}, waypoints.trailLegs in en / th / ro.

Test plan

  • Switch to Land mode, pick two points in a trail-rich area (park, hiking area) — polyline follows real paths
  • Meta-line shows 🥾 Trail route via OpenStreetMap with hike/bike counts
  • Pick endpoints far from trails — graceful fallback to straight-line with reason
  • Switch to Sea mode — ferry/sea routing unchanged (no regression)
  • Waypoints tab with 3+ points in Land mode — track summary shows 🥾 N trail legs
  • Language switcher → TH / RO — trail strings render correctly

https://claude.ai/code/session_01B9DMrLPT7hYfdCTsJgFF9q


Generated by Claude Code

Re-implements #33 from scratch on current main (the original PR had
heavy merge conflicts from the wind/compass/GPS work). The trail
routing logic (`osm-land.ts`) is identical to the original.

When Land mode is active, each route leg now snaps to OpenStreetMap
hiking/cycling trails (path, footway, track, bridleway, cycleway,
pedestrian, steps, foot/bicycle=designated) instead of drawing a
straight line.

Pipeline:
  * `osm-land.ts` — queries Overpass for trail ways within a
    graduated bbox, builds a geojson-path-finder graph, snaps
    endpoints to the nearest vertex (≤5 km), and returns the routed
    polyline + length + hike/bike way counts.
  * `/api/route?land=1` — calls computeLandTrailRoute first, falls
    back to straight-line on failure.
  * `/api/multi-route?land=1` — each leg attempts trail routing;
    trailLegs count in the response.
  * RouteView shows "🥾 Trail route via OpenStreetMap: X km (Y hike
    · Z bike ways)". Straight-line fallback shows the trail failure
    reason.
  * WaypointsView shows "🥾 N trail" in the track summary.

Constraints: MAX_SPAN_DEG=1.5 (~165 km), MAX_SNAP_KM=5, 24h
Overpass cache. Trails are dense — tight bbox keeps payloads bounded.

i18n: route.trailPrefix/trailWaysSuffix/trailLabel,
waypoints.trailLegs in en/th/ro.

91/91 tests pass, 0 type errors, build succeeds.

https://claude.ai/code/session_01B9DMrLPT7hYfdCTsJgFF9q
claude added 4 commits May 25, 2026 20:32
Open-Meteo's geocoding API only indexes cities and populated places —
searching for "Lumpini Park", "Hyde Park London", or any trail/POI
returned empty results.

Now if Open-Meteo returns no results, the geocoder falls back to
Nominatim (OpenStreetMap's geocoder), which knows parks, mountains,
beaches, trails, buildings — everything tagged in OSM.

The flow: Open-Meteo first (fast, good for cities) → Nominatim
fallback (slower, but finds POIs). Both are free and unkeyed.

https://claude.ai/code/session_01B9DMrLPT7hYfdCTsJgFF9q
The controls card (waypoint chips + Done button, route meta-line,
search fields) was hidden in fullscreen via display:none. This
broke waypoint editing — you couldn't see/tap chips or press Done.

Now the controls card stays visible but compact in fullscreen:
reduced padding, no rounded corners, horizontally scrollable if
needed. The map fills the remaining space below.

https://claude.ai/code/session_01B9DMrLPT7hYfdCTsJgFF9q
maxZoom: 11 forced the map to zoom out far even when all waypoints
were within a few blocks. Now caps at 16 so nearby points stay
zoomed in with just enough padding (60px) to see all markers.

https://claude.ai/code/session_01B9DMrLPT7hYfdCTsJgFF9q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants