Archive osm-overlays change, sync 4 specs to main
Synced to main specs: - osm-poi-overlays (new): POI overlay requirements - osm-tile-overlays (new): tile overlay requirements - map-display (updated): added overlay layer entries - planner-session (updated): added overlay/POI sync Archived to openspec/changes/archive/2026-04-11-osm-overlays/. All 4 artifacts complete. All 45 tasks complete. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4ebf75a42
commit
7dc4fdec3b
12 changed files with 179 additions and 98 deletions
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-03-28
|
||||
214
openspec/changes/archive/2026-04-11-osm-overlays/design.md
Normal file
214
openspec/changes/archive/2026-04-11-osm-overlays/design.md
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
## Context
|
||||
|
||||
The Planner currently has three base tile layers (OSM, OpenTopoMap, CyclOSM) in
|
||||
`packages/map/src/layers.ts`, rendered via Leaflet's `LayersControl`. There are
|
||||
no overlay layers and no POI display. brouter-web offers hillshading, Waymarked
|
||||
Trails networks, and ~60 Overpass-powered POI categories — a model worth
|
||||
adopting selectively.
|
||||
|
||||
The Planner is stateless (Yjs CRDT), collaborative, and uses BRouter for
|
||||
routing. Overlay state should sync across participants.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Add tile-based overlays (hillshading, waymarked trails) to the layer switcher
|
||||
- Add viewport-scoped POI overlays from Overpass API with per-category toggling
|
||||
- Auto-suggest relevant overlays based on routing profile
|
||||
- Build a reusable Overpass client shared with waypoint-notes POI snap
|
||||
|
||||
**Non-Goals:**
|
||||
- Custom tile server or self-hosted overlays (use public tile services)
|
||||
- Full brouter-web layer catalog (50+ layers, most country-specific)
|
||||
- Offline/cached tile data
|
||||
- Vector tile overlays (MVT) — stick with raster for now
|
||||
- POI editing or contributing back to OSM
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Tile overlay definitions
|
||||
|
||||
Add an `overlayLayers` export to `packages/map/src/layers.ts` alongside
|
||||
existing `baseLayers`. Each overlay is a transparent tile layer rendered on top
|
||||
of the base layer.
|
||||
|
||||
Initial overlays:
|
||||
|
||||
| Id | Name | URL | Attribution |
|
||||
|----|------|-----|-------------|
|
||||
| `hillshading` | Hillshading | `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png` via [Leaflet.TileLayer.Terrarium](https://github.com/pka/leaflet-terrarium-hillshading) or pre-rendered from `https://tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png` | SRTM/Mapzen |
|
||||
| `waymarked-cycling` | Cycling Routes | `https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png` | Waymarked Trails (CC-BY-SA) |
|
||||
| `waymarked-hiking` | Hiking Routes | `https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png` | Waymarked Trails (CC-BY-SA) |
|
||||
| `waymarked-mtb` | MTB Routes | `https://tile.waymarkedtrails.org/mtb/{z}/{x}/{y}.png` | Waymarked Trails (CC-BY-SA) |
|
||||
|
||||
These are all free, public tile endpoints used by brouter-web and other OSM
|
||||
tools. No API keys needed.
|
||||
|
||||
**Alternative considered**: Thunderforest Outdoors or OpenCycleMap — requires
|
||||
API key, limited free tier. Not worth the complexity.
|
||||
|
||||
### D2: Overlay toggle in LayersControl
|
||||
|
||||
Leaflet's `LayersControl` already supports overlays natively via
|
||||
`LayersControl.Overlay` (react-leaflet). Add overlay tile layers as checkboxes
|
||||
alongside the existing base layer radio buttons. No custom UI needed for Phase 1.
|
||||
|
||||
### D3: POI category system
|
||||
|
||||
Define POI categories as a typed configuration mapping OSM tags to display
|
||||
properties. Inspired by brouter-web's `layers/overpass/` structure but
|
||||
simplified to the categories most relevant for route planning:
|
||||
|
||||
```typescript
|
||||
interface PoiCategory {
|
||||
id: string;
|
||||
name: string; // i18n key
|
||||
icon: string; // emoji or SVG icon id
|
||||
color: string; // marker color
|
||||
query: string; // Overpass QL fragment, e.g. "nwr[amenity=drinking_water]"
|
||||
profiles?: string[]; // routing profiles where this is auto-enabled
|
||||
}
|
||||
```
|
||||
|
||||
**Initial categories** (curated from brouter-web's full list):
|
||||
|
||||
| Category | POI types (OSM tags) | Icon | Auto-enable for |
|
||||
|----------|---------------------|------|-----------------|
|
||||
| Drinking water | `amenity=drinking_water`, `amenity=water_point` | 💧 | all |
|
||||
| Shelter | `amenity=shelter`, `tourism=wilderness_hut` | 🛖 | hiking |
|
||||
| Camping | `tourism=camp_site`, `tourism=caravan_site`, `tourism=picnic_site` | ⛺ | all |
|
||||
| Food & drink | `amenity=restaurant`, `amenity=cafe`, `amenity=fast_food`, `amenity=pub`, `amenity=biergarten` | 🍽️ | — |
|
||||
| Groceries | `shop=supermarket`, `shop=convenience`, `shop=bakery` | 🛒 | — |
|
||||
| Bike infrastructure | `amenity=bicycle_parking`, `amenity=bicycle_repair_station`, `amenity=bicycle_rental` | 🔧 | cycling |
|
||||
| Accommodation | `tourism=hotel`, `tourism=hostel`, `tourism=guest_house` | 🏨 | — |
|
||||
| Viewpoints | `tourism=viewpoint` | 👁️ | hiking |
|
||||
| Toilets | `amenity=toilets` | 🚻 | — |
|
||||
|
||||
**Not included** (from brouter-web but too niche): ATMs, banks, benches,
|
||||
telephones, kneipp water cures, car parking, railway stations, art galleries,
|
||||
museums, ice cream shops, BBQs. Can be added later by extending the config.
|
||||
|
||||
### D4: Overpass client
|
||||
|
||||
Create `apps/planner/app/lib/overpass.ts` with:
|
||||
|
||||
- `queryPois(bbox, categories): Promise<Poi[]>` — builds Overpass QL query
|
||||
combining all enabled categories into one request (union query), returns
|
||||
parsed GeoJSON features
|
||||
- **Bbox query**: `[bbox:south,west,north,east]` in Overpass QL, scoped to
|
||||
current Leaflet viewport
|
||||
- **Endpoint**: `https://overpass-api.de/api/interpreter` (public, no key)
|
||||
- **Response format**: Request `[out:json]` for easier parsing than XML
|
||||
- **Deduplication**: Overpass may return same node via multiple tags — dedup by
|
||||
OSM id
|
||||
|
||||
This client is also used by the waypoint-notes POI snap feature (smaller radius
|
||||
query around a single waypoint).
|
||||
|
||||
### D5: POI caching and rate limiting
|
||||
|
||||
Overpass API is public and rate-limited. Must be respectful:
|
||||
|
||||
- **Debounce**: 500ms after map `moveend` before querying
|
||||
- **Abort**: Cancel in-flight requests when viewport changes (AbortController)
|
||||
- **Tile-based caching**: Quantize viewport to grid tiles (e.g., 0.1° cells),
|
||||
cache results per tile. Reuse cached tiles that overlap new viewport.
|
||||
- **TTL**: 10 minutes for cached tiles (POI data changes slowly)
|
||||
- **Max concurrent**: 1 request at a time
|
||||
- **429 handling**: Exponential backoff, disable auto-refresh temporarily, show
|
||||
"POI data unavailable" message
|
||||
- **Zoom threshold**: Only query POIs at zoom >= 12 (avoids massive result sets
|
||||
at country-level zoom)
|
||||
|
||||
### D6: POI overlay panel
|
||||
|
||||
A collapsible panel (not the LayersControl — too many items) for toggling POI
|
||||
categories. Positioned below the layer switcher on the right side of the map.
|
||||
|
||||
- Toggle button with POI icon to open/close
|
||||
- Checkbox per category with icon and name
|
||||
- "Loading..." indicator while Overpass query is in flight
|
||||
- Category count badge showing number of visible POIs
|
||||
- Panel state (which categories are enabled) synced via Yjs so all participants
|
||||
see the same POIs
|
||||
|
||||
### D7: POI marker rendering
|
||||
|
||||
- Use Leaflet `L.Marker` with `L.DivIcon` for each POI (not CircleMarker —
|
||||
need icons)
|
||||
- Icon shows the category emoji/icon at 20×20px
|
||||
- Popup on click showing: name, category, opening hours (if available), website
|
||||
link (if available), OSM link
|
||||
- **Clustering**: Use `leaflet.markercluster` at low zoom levels to avoid
|
||||
thousands of markers. Cluster by category color.
|
||||
- **Z-index**: POI markers below route and waypoint markers
|
||||
|
||||
### D8: Profile-aware overlay defaults
|
||||
|
||||
When the routing profile changes (via Yjs `routeOptions.profile`), suggest
|
||||
relevant overlays:
|
||||
|
||||
- **Cycling profiles** (cycling-safe, cycling-fast, etc.): Auto-enable
|
||||
Waymarked Cycling overlay + Bike infrastructure POIs
|
||||
- **Hiking profiles**: Auto-enable Waymarked Hiking + Shelter + Viewpoints
|
||||
- **MTB profiles**: Auto-enable Waymarked MTB + Bike infrastructure
|
||||
|
||||
"Auto-enable" means toggling on when profile changes, not forcing — users can
|
||||
still disable. Only auto-enable on profile change, not on page load (respect
|
||||
user's previous choice stored in Yjs).
|
||||
|
||||
### D9: Yjs overlay state
|
||||
|
||||
Store enabled overlays in Yjs `routeOptions` Y.Map:
|
||||
|
||||
```
|
||||
routeOptions.overlays = ["hillshading", "waymarked-cycling"]
|
||||
routeOptions.poiCategories = ["drinking_water", "camping", "bike_infra"]
|
||||
```
|
||||
|
||||
Array of string IDs. Changes sync to all participants. Persisted in crash
|
||||
recovery localStorage snapshot.
|
||||
|
||||
### D10: POI-to-waypoint integration
|
||||
|
||||
POIs can become waypoints through three paths, all using the same snap logic:
|
||||
|
||||
1. **"Add as waypoint" button** in POI popup → appends to end of route
|
||||
2. **Click near a POI** (within 50m) → new waypoint snaps to POI position
|
||||
3. **Drag waypoint near a POI** → snaps on drop, clears name if dragged away
|
||||
|
||||
When snapping, the waypoint's Yjs Y.Map stores:
|
||||
- `osmId`: OSM node ID for future cross-referencing
|
||||
- `poiTags`: Key tags (phone, address, website, opening hours, amenity type)
|
||||
|
||||
This metadata persists through Yjs and will be available for Journal display.
|
||||
|
||||
### D11: Overpass endpoint fallback
|
||||
|
||||
Primary endpoint is `overpass.kumi.systems` (higher rate limits, same as
|
||||
brouter-web). Falls back to `overpass-api.de` if the primary fails. The
|
||||
Overpass API sometimes returns rate limit errors as HTTP 200 with
|
||||
"rate_limited" in the body — the client checks for this pattern.
|
||||
|
||||
### D12: Z-index layering
|
||||
|
||||
Marker z-index offsets centralized in `apps/planner/app/lib/z-index.ts`:
|
||||
- Cursors (-1000) < Ghost waypoint (-100) < Waypoints (1000) <
|
||||
Waypoint highlighted (1200) < POI markers (1500) < Highlight dot (2000)
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Overpass API availability**: Public endpoint, no SLA. If down, POI overlays
|
||||
fail gracefully (show message, tile overlays still work). Fallback endpoint
|
||||
(`overpass.kumi.systems` → `overpass-api.de`) implemented.
|
||||
- **Tile service availability**: Waymarked Trails and hillshading tiles are
|
||||
community-run. → Degrade gracefully if tiles 404. Consider self-hosting tiles
|
||||
if usage grows.
|
||||
- **Performance with many POIs**: Dense areas (cities) may return hundreds of
|
||||
POIs. → Zoom threshold (>=10) + 100 element limit mitigate this. Marker
|
||||
clustering deferred — can add later if density becomes an issue.
|
||||
- **Overpass query cost**: Combining many categories into one query is efficient
|
||||
but returns large payloads. → Only query enabled categories, not all. 1MB
|
||||
maxsize limit, 10s timeout.
|
||||
- **Routing rate limit**: Multi-waypoint routes with POI snapping can trigger
|
||||
rapid recomputes. Increased rate limit from 60 to 300/hour to accommodate.
|
||||
30
openspec/changes/archive/2026-04-11-osm-overlays/proposal.md
Normal file
30
openspec/changes/archive/2026-04-11-osm-overlays/proposal.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
## Why
|
||||
|
||||
The Planner shows three base tile layers (OSM, OpenTopoMap, CyclOSM) but no overlays. Route planners like brouter-web offer hillshading, waymarked trail networks, and toggleable POI layers from OpenStreetMap — information that is essential for planning multi-day bike tours and hikes. Without overlays, users must cross-reference other tools to find water sources, campsites, bike repair stations, or see official trail routes.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Tile overlays**: Add hillshading and Waymarked Trails (cycling, hiking, MTB) as toggle-able overlay layers in the Leaflet LayersControl
|
||||
- **POI overlay panel**: Add a collapsible panel to toggle categories of OSM points of interest (water, shelter, camping, food, bike infrastructure, accommodation) queried from the Overpass API within the current viewport
|
||||
- **POI markers**: Render POI results as categorized markers with icons, name labels, and popups showing OSM tags (opening hours, website, etc.)
|
||||
- **Profile-aware defaults**: Auto-enable relevant overlays based on the active routing profile (cycling → Waymarked Cycling + bike POIs; hiking → Waymarked Hiking + water/shelter POIs)
|
||||
- **Overpass client**: Shared utility for querying the Overpass API with caching, debouncing, rate limit handling, and endpoint fallback (kumi.systems → overpass-api.de)
|
||||
- **POI → waypoint**: "Add as waypoint" button in POI popups, plus automatic snapping of placed/dragged waypoints to nearby POIs (50m threshold) with OSM metadata preserved
|
||||
- **POI metadata on waypoints**: Waypoints snapped to POIs store osmId and key tags (phone, address, website, opening hours) for future Journal display
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `osm-tile-overlays`: Hillshading and Waymarked Trails tile overlays in the map layer switcher
|
||||
- `osm-poi-overlays`: Toggleable POI categories from Overpass API rendered as map markers within the viewport
|
||||
|
||||
### Modified Capabilities
|
||||
- `map-display`: Add overlay layers to the layer switcher alongside existing base layers
|
||||
- `planner-session`: Persist enabled overlay state in Yjs so all participants see the same overlays
|
||||
|
||||
## Impact
|
||||
|
||||
- **Files**: `packages/map/src/layers.ts` (overlay tile definitions), new POI overlay components in `apps/planner/`, new Overpass client in `packages/map/` or `apps/planner/app/lib/`
|
||||
- **APIs**: Overpass API (external, rate-limited — public endpoint at `overpass-api.de`)
|
||||
- **Dependencies**: None new — Leaflet handles tile overlays natively, Overpass is a REST API
|
||||
- **Performance**: Tile overlays are lightweight (transparent PNGs). POI overlays need viewport-scoped queries with caching to avoid excessive Overpass load.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Base layer switching
|
||||
The map SHALL support switching between multiple base tile layers, and SHALL support toggling overlay tile layers independently.
|
||||
|
||||
#### Scenario: Switch to OpenTopoMap
|
||||
- **WHEN** a user selects "OpenTopoMap" from the layer switcher
|
||||
- **THEN** the map tiles change to topographic tiles from OpenTopoMap
|
||||
|
||||
#### Scenario: Available base layers
|
||||
- **WHEN** a user opens the layer switcher
|
||||
- **THEN** the options include OpenStreetMap, OpenTopoMap, and CyclOSM
|
||||
|
||||
#### Scenario: Available overlay layers
|
||||
- **WHEN** a user opens the layer switcher
|
||||
- **THEN** overlay checkboxes are shown for Hillshading, Cycling Routes, Hiking Routes, and MTB Routes
|
||||
|
||||
#### Scenario: Toggle overlay
|
||||
- **WHEN** a user checks an overlay checkbox in the layer switcher
|
||||
- **THEN** the overlay tiles are rendered on top of the current base layer
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: POI overlay panel
|
||||
The Planner SHALL provide a collapsible panel for toggling POI categories on the map.
|
||||
|
||||
#### Scenario: Open POI panel
|
||||
- **WHEN** a user clicks the POI toggle button on the map
|
||||
- **THEN** a panel opens showing checkboxes for each POI category with icons and names
|
||||
|
||||
#### Scenario: Close POI panel
|
||||
- **WHEN** the POI panel is open and the user clicks the toggle button again
|
||||
- **THEN** the panel collapses and POI markers remain visible on the map
|
||||
|
||||
### Requirement: POI categories
|
||||
The Planner SHALL support the following POI categories queried from OpenStreetMap via Overpass API: drinking water, shelter, camping, food & drink, groceries, bike infrastructure, accommodation, viewpoints, and toilets.
|
||||
|
||||
#### Scenario: Enable a POI category
|
||||
- **WHEN** a user enables the "Drinking water" category in the POI panel
|
||||
- **THEN** drinking water POIs within the current map viewport are fetched from Overpass and rendered as markers
|
||||
|
||||
#### Scenario: Disable a POI category
|
||||
- **WHEN** a user disables a previously enabled POI category
|
||||
- **THEN** markers for that category are removed from the map
|
||||
|
||||
#### Scenario: Multiple categories enabled
|
||||
- **WHEN** a user enables "Camping" and "Drinking water" simultaneously
|
||||
- **THEN** both categories of markers are visible, each with distinct icons
|
||||
|
||||
### Requirement: POI markers
|
||||
Each POI SHALL be rendered as a map marker with a category-specific icon.
|
||||
|
||||
#### Scenario: POI marker display
|
||||
- **WHEN** POIs are loaded for an enabled category
|
||||
- **THEN** each POI appears as a small icon marker at its coordinates on the map
|
||||
|
||||
#### Scenario: POI marker popup
|
||||
- **WHEN** a user clicks a POI marker
|
||||
- **THEN** a popup shows the POI name, category, and available details (opening hours, website, OSM link)
|
||||
|
||||
#### Scenario: POI marker clustering
|
||||
- **WHEN** many POIs are visible in a small area
|
||||
- **THEN** markers are clustered with a count badge, and expand when the user zooms in
|
||||
|
||||
### Requirement: Viewport-scoped POI loading
|
||||
The Planner SHALL load POIs only within the current map viewport, refreshing when the viewport changes.
|
||||
|
||||
#### Scenario: Load POIs on viewport
|
||||
- **WHEN** POI categories are enabled and the user pans or zooms the map
|
||||
- **THEN** POIs are fetched for the new viewport after a 500ms debounce
|
||||
|
||||
#### Scenario: Zoom threshold
|
||||
- **WHEN** the map zoom level is below 12
|
||||
- **THEN** POI queries are not sent and a message indicates the user should zoom in to see POIs
|
||||
|
||||
#### Scenario: Cached results
|
||||
- **WHEN** the user pans back to a previously viewed area within 10 minutes
|
||||
- **THEN** cached POI results are displayed without a new Overpass query
|
||||
|
||||
### Requirement: Overpass rate limit handling
|
||||
The Planner SHALL handle Overpass API rate limits gracefully.
|
||||
|
||||
#### Scenario: Rate limited response
|
||||
- **WHEN** the Overpass API returns a 429 status
|
||||
- **THEN** the Planner shows a temporary "POI data unavailable — try again shortly" message and retries with exponential backoff
|
||||
|
||||
#### Scenario: Overpass unavailable
|
||||
- **WHEN** the Overpass API is unreachable
|
||||
- **THEN** the Planner shows a message and tile overlays continue to function normally
|
||||
|
||||
### Requirement: Profile-aware POI defaults
|
||||
The Planner SHALL auto-enable relevant POI categories when the routing profile changes.
|
||||
|
||||
#### Scenario: Cycling profile POI defaults
|
||||
- **WHEN** the routing profile is changed to a cycling variant
|
||||
- **THEN** the "Bike infrastructure" POI category is automatically enabled
|
||||
|
||||
#### Scenario: Hiking profile POI defaults
|
||||
- **WHEN** the routing profile is changed to a hiking variant
|
||||
- **THEN** "Shelter" and "Viewpoints" POI categories are automatically enabled
|
||||
|
||||
#### Scenario: User override persists
|
||||
- **WHEN** a user manually disables an auto-enabled POI category
|
||||
- **THEN** it remains disabled until the next profile change
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Hillshading overlay
|
||||
The Planner map SHALL offer a hillshading tile overlay that visualizes terrain relief.
|
||||
|
||||
#### Scenario: Enable hillshading
|
||||
- **WHEN** a user toggles "Hillshading" in the layer switcher
|
||||
- **THEN** semi-transparent terrain shading tiles are rendered on top of the base layer
|
||||
|
||||
#### Scenario: Hillshading with any base layer
|
||||
- **WHEN** hillshading is enabled and the user switches base layers
|
||||
- **THEN** hillshading remains visible on top of the new base layer
|
||||
|
||||
### Requirement: Waymarked Trails cycling overlay
|
||||
The Planner map SHALL offer a Waymarked Trails cycling overlay showing official cycle route networks.
|
||||
|
||||
#### Scenario: Enable cycling routes overlay
|
||||
- **WHEN** a user toggles "Cycling Routes" in the layer switcher
|
||||
- **THEN** official cycling routes (EuroVelo, national networks) are rendered as colored lines on the map from Waymarked Trails tiles
|
||||
|
||||
#### Scenario: Cycling overlay at different zoom levels
|
||||
- **WHEN** cycling routes overlay is enabled
|
||||
- **THEN** international routes are visible at low zoom and local routes appear at higher zoom levels
|
||||
|
||||
### Requirement: Waymarked Trails hiking overlay
|
||||
The Planner map SHALL offer a Waymarked Trails hiking overlay showing official hiking trail networks.
|
||||
|
||||
#### Scenario: Enable hiking routes overlay
|
||||
- **WHEN** a user toggles "Hiking Routes" in the layer switcher
|
||||
- **THEN** official hiking trails (GR routes, national trails) are rendered as colored lines on the map
|
||||
|
||||
### Requirement: Waymarked Trails MTB overlay
|
||||
The Planner map SHALL offer a Waymarked Trails MTB overlay showing official mountain bike trail networks.
|
||||
|
||||
#### Scenario: Enable MTB routes overlay
|
||||
- **WHEN** a user toggles "MTB Routes" in the layer switcher
|
||||
- **THEN** official MTB trails are rendered as colored lines on the map
|
||||
|
||||
### Requirement: Multiple simultaneous overlays
|
||||
The Planner map SHALL support enabling multiple tile overlays at the same time.
|
||||
|
||||
#### Scenario: Hillshading plus cycling routes
|
||||
- **WHEN** a user enables both "Hillshading" and "Cycling Routes"
|
||||
- **THEN** both overlays are visible simultaneously, with cycling routes rendered above hillshading
|
||||
|
||||
### Requirement: Overlay tile attribution
|
||||
Each tile overlay SHALL display proper attribution when enabled.
|
||||
|
||||
#### Scenario: Attribution updates
|
||||
- **WHEN** an overlay is toggled on
|
||||
- **THEN** its attribution text is added to the map attribution control
|
||||
- **WHEN** the overlay is toggled off
|
||||
- **THEN** its attribution text is removed
|
||||
|
||||
### Requirement: Profile-aware overlay suggestions
|
||||
The Planner SHALL auto-enable relevant tile overlays when the routing profile changes.
|
||||
|
||||
#### Scenario: Switch to cycling profile
|
||||
- **WHEN** the routing profile is changed to a cycling variant
|
||||
- **THEN** the Waymarked Trails cycling overlay is automatically enabled
|
||||
|
||||
#### Scenario: Switch to hiking profile
|
||||
- **WHEN** the routing profile is changed to a hiking variant
|
||||
- **THEN** the Waymarked Trails hiking overlay is automatically enabled
|
||||
|
||||
#### Scenario: User can disable auto-enabled overlays
|
||||
- **WHEN** an overlay was auto-enabled by a profile change
|
||||
- **THEN** the user can manually disable it and it stays disabled until the next profile change
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Real-time collaborative editing
|
||||
The Planner SHALL synchronize waypoint edits, route options, and overlay preferences across all connected participants in real-time using Yjs CRDTs.
|
||||
|
||||
#### Scenario: Add waypoint
|
||||
- **WHEN** participant A adds a waypoint to the map
|
||||
- **THEN** participant B sees the waypoint appear within 500ms
|
||||
|
||||
#### Scenario: Reorder waypoints
|
||||
- **WHEN** participant A drags a waypoint to reorder it
|
||||
- **THEN** participant B sees the updated waypoint order within 500ms
|
||||
|
||||
#### Scenario: Concurrent edits
|
||||
- **WHEN** participant A and B both add waypoints simultaneously
|
||||
- **THEN** both waypoints appear for both participants without conflict
|
||||
|
||||
#### Scenario: Overlay sync
|
||||
- **WHEN** participant A enables the "Hillshading" tile overlay
|
||||
- **THEN** participant B sees hillshading appear on their map within 500ms
|
||||
|
||||
#### Scenario: POI category sync
|
||||
- **WHEN** participant A enables the "Drinking water" POI category
|
||||
- **THEN** participant B sees drinking water markers appear on their map
|
||||
80
openspec/changes/archive/2026-04-11-osm-overlays/tasks.md
Normal file
80
openspec/changes/archive/2026-04-11-osm-overlays/tasks.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
## 1. Tile Overlay Definitions
|
||||
|
||||
- [x] 1.1 Add `overlayLayers` export to `packages/map/src/layers.ts` with hillshading, Waymarked Cycling, Waymarked Hiking, Waymarked MTB tile configs
|
||||
- [x] 1.2 Add `LayersControl.Overlay` entries in `MapView.tsx` and `PlannerMap.tsx` for each overlay
|
||||
- [x] 1.3 Verify overlay attribution updates correctly when toggling overlays on/off
|
||||
|
||||
## 2. Overlay State Sync
|
||||
|
||||
- [x] 2.1 Add `overlays` string array to Yjs `routeOptions` Y.Map for enabled tile overlay IDs
|
||||
- [x] 2.2 Add `poiCategories` string array to Yjs `routeOptions` Y.Map for enabled POI category IDs
|
||||
- [x] 2.3 Sync LayersControl state with Yjs — toggling overlay updates Yjs, Yjs changes toggle layers
|
||||
- [x] 2.4 Include overlay state in crash recovery localStorage snapshot
|
||||
|
||||
## 3. Overpass Client
|
||||
|
||||
- [x] 3.1 Create `apps/planner/app/lib/overpass.ts` with `queryPois(bbox, categories)` function
|
||||
- [x] 3.2 Build Overpass QL union queries from enabled POI category configs
|
||||
- [x] 3.3 Parse `[out:json]` response into typed `Poi` objects (id, lat, lon, name, category, tags)
|
||||
- [x] 3.4 Deduplicate results by OSM node ID (same node may match multiple tag queries)
|
||||
|
||||
## 4. POI Caching & Rate Limiting
|
||||
|
||||
- [x] 4.1 Implement tile-based cache: quantize viewport to 0.1° grid cells, cache per cell with 10-minute TTL
|
||||
- [x] 4.2 Add 500ms debounce on map `moveend` before triggering Overpass query
|
||||
- [x] 4.3 Use AbortController to cancel in-flight requests when viewport changes
|
||||
- [x] 4.4 Handle 429 responses with exponential backoff and user-visible message
|
||||
- [x] 4.5 Enforce zoom >= 12 threshold — show "Zoom in to see POIs" message below
|
||||
|
||||
## 5. POI Category Configuration
|
||||
|
||||
- [x] 5.1 Define `PoiCategory` type and initial category configs (water, shelter, camping, food, groceries, bike infra, accommodation, viewpoints, toilets)
|
||||
- [x] 5.2 Map each category to Overpass QL tag queries, icon, color, and applicable routing profiles
|
||||
|
||||
## 6. POI Overlay Panel
|
||||
|
||||
- [x] 6.1 Create collapsible POI panel component with toggle button (map right side, below layer switcher)
|
||||
- [x] 6.2 Render checkbox per POI category with icon, name, and visible count badge
|
||||
- [x] 6.3 Show loading indicator while Overpass query is in flight
|
||||
- [x] 6.4 Show empty/error states (no results, Overpass unavailable, zoom too low)
|
||||
|
||||
## 7. POI Marker Rendering
|
||||
|
||||
- [x] 7.1 Render POI markers using `L.Marker` with `L.DivIcon` showing category icon
|
||||
- [x] 7.2 Add click popup with POI name, category, opening hours, website, and OSM link
|
||||
- [x] 7.3 Add `leaflet.markercluster` for clustering dense POI areas (dynamic import to avoid bundle bloat)
|
||||
- [x] 7.4 Set z-index so POI markers render below route polyline and waypoint markers
|
||||
|
||||
## 8. Profile-Aware Defaults
|
||||
|
||||
- [x] 8.1 Define profile-to-overlay mapping (cycling → waymarked-cycling + bike POIs, hiking → waymarked-hiking + shelter + viewpoints, MTB → waymarked-mtb + bike POIs)
|
||||
- [x] 8.2 Auto-enable mapped overlays on routing profile change (update Yjs arrays)
|
||||
- [x] 8.3 Only auto-enable on explicit profile change, not on initial page load (respect existing Yjs state)
|
||||
|
||||
## 9. i18n
|
||||
|
||||
- [x] 9.1 Add translation keys for all overlay names, POI category names, and UI strings (en + de)
|
||||
|
||||
## 10. Testing
|
||||
|
||||
- [x] 10.1 Unit tests for Overpass client: query building, response parsing, deduplication
|
||||
- [x] 10.2 Unit tests for POI cache: tile quantization, TTL expiry, cache hit/miss
|
||||
- [x] 10.3 Unit tests for profile-to-overlay mapping
|
||||
- [x] 10.4 E2E test: enable hillshading overlay, verify tile requests
|
||||
- [x] 10.5 E2E test: enable POI category, verify markers appear (mock Overpass response)
|
||||
|
||||
## 11. POI-Waypoint Integration
|
||||
|
||||
- [x] 11.1 Add "Add as waypoint" button to POI popup — appends POI location + name as waypoint
|
||||
- [x] 11.2 Snap click-to-add waypoints to nearby POIs (50m threshold) with name + metadata
|
||||
- [x] 11.3 Snap dragged waypoints to nearby POIs, clear name/metadata when dragged away
|
||||
- [x] 11.4 Snap route-inserted waypoints to nearby POIs
|
||||
- [x] 11.5 Store osmId and poiTags on Yjs waypoint Y.Map for snapped POIs
|
||||
|
||||
## 12. Resilience
|
||||
|
||||
- [x] 12.1 Fallback Overpass endpoint (kumi.systems → overpass-api.de)
|
||||
- [x] 12.2 Handle Overpass rate limit returned as HTTP 200 with error body
|
||||
- [x] 12.3 Reduce query size (100 results, 1MB maxsize, 10s timeout)
|
||||
- [x] 12.4 Extract z-index constants into z-index.ts for consistent marker layering
|
||||
- [x] 12.5 Increase Planner route rate limit from 60 to 300 requests/hour
|
||||
Loading…
Add table
Add a link
Reference in a new issue