trails/openspec/changes/osm-overlays/tasks.md
Ullrich Schäfer 22c5ebd838
Auto-enable POI categories on routing profile change
useProfileDefaults hook observes profile changes in Yjs and merges
relevant POI categories into the enabled set. Skips initial load to
respect existing state. Cycling profiles → bike infra, hiking →
shelter + viewpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 02:19:30 +02:00

4.3 KiB

1. Tile Overlay Definitions

  • 1.1 Add overlayLayers export to packages/map/src/layers.ts with hillshading, Waymarked Cycling, Waymarked Hiking, Waymarked MTB tile configs
  • 1.2 Add LayersControl.Overlay entries in MapView.tsx and PlannerMap.tsx for each overlay
  • 1.3 Verify overlay attribution updates correctly when toggling overlays on/off

2. Overlay State Sync

  • 2.1 Add overlays string array to Yjs routeOptions Y.Map for enabled tile overlay IDs
  • 2.2 Add poiCategories string array to Yjs routeOptions Y.Map for enabled POI category IDs
  • 2.3 Sync LayersControl state with Yjs — toggling overlay updates Yjs, Yjs changes toggle layers
  • 2.4 Include overlay state in crash recovery localStorage snapshot

3. Overpass Client

  • 3.1 Create apps/planner/app/lib/overpass.ts with queryPois(bbox, categories) function
  • 3.2 Build Overpass QL union queries from enabled POI category configs
  • 3.3 Parse [out:json] response into typed Poi objects (id, lat, lon, name, category, tags)
  • 3.4 Deduplicate results by OSM node ID (same node may match multiple tag queries)

4. POI Caching & Rate Limiting

  • 4.1 Implement tile-based cache: quantize viewport to 0.1° grid cells, cache per cell with 10-minute TTL
  • 4.2 Add 500ms debounce on map moveend before triggering Overpass query
  • 4.3 Use AbortController to cancel in-flight requests when viewport changes
  • 4.4 Handle 429 responses with exponential backoff and user-visible message
  • 4.5 Enforce zoom >= 12 threshold — show "Zoom in to see POIs" message below

5. POI Category Configuration

  • 5.1 Define PoiCategory type and initial category configs (water, shelter, camping, food, groceries, bike infra, accommodation, viewpoints, toilets)
  • 5.2 Map each category to Overpass QL tag queries, icon, color, and applicable routing profiles

6. POI Overlay Panel

  • 6.1 Create collapsible POI panel component with toggle button (map right side, below layer switcher)
  • 6.2 Render checkbox per POI category with icon, name, and visible count badge
  • 6.3 Show loading indicator while Overpass query is in flight
  • 6.4 Show empty/error states (no results, Overpass unavailable, zoom too low)

7. POI Marker Rendering

  • 7.1 Render POI markers using L.Marker with L.DivIcon showing category icon
  • 7.2 Add click popup with POI name, category, opening hours, website, and OSM link
  • 7.3 Add leaflet.markercluster for clustering dense POI areas (dynamic import to avoid bundle bloat)
  • 7.4 Set z-index so POI markers render below route polyline and waypoint markers

8. Profile-Aware Defaults

  • 8.1 Define profile-to-overlay mapping (cycling → waymarked-cycling + bike POIs, hiking → waymarked-hiking + shelter + viewpoints, MTB → waymarked-mtb + bike POIs)
  • 8.2 Auto-enable mapped overlays on routing profile change (update Yjs arrays)
  • 8.3 Only auto-enable on explicit profile change, not on initial page load (respect existing Yjs state)

9. i18n

  • 9.1 Add translation keys for all overlay names, POI category names, and UI strings (en + de)

10. Testing

  • 10.1 Unit tests for Overpass client: query building, response parsing, deduplication
  • 10.2 Unit tests for POI cache: tile quantization, TTL expiry, cache hit/miss
  • 10.3 Unit tests for profile-to-overlay mapping
  • 10.4 E2E test: enable hillshading overlay, verify tile requests
  • 10.5 E2E test: enable POI category, verify markers appear (mock Overpass response)

11. POI-Waypoint Integration

  • 11.1 Add "Add as waypoint" button to POI popup — appends POI location + name as waypoint
  • 11.2 Snap click-to-add waypoints to nearby POIs (50m threshold) with name + metadata
  • 11.3 Snap dragged waypoints to nearby POIs, clear name/metadata when dragged away
  • 11.4 Snap route-inserted waypoints to nearby POIs
  • 11.5 Store osmId and poiTags on Yjs waypoint Y.Map for snapped POIs

12. Resilience

  • 12.1 Fallback Overpass endpoint (kumi.systems → overpass-api.de)
  • 12.2 Handle Overpass rate limit returned as HTTP 200 with error body
  • 12.3 Reduce query size (100 results, 1MB maxsize, 10s timeout)
  • 12.4 Extract z-index constants into z-index.ts for consistent marker layering
  • 12.5 Increase Planner route rate limit from 60 to 300 requests/hour