trails/openspec/specs/osm-poi-overlays/spec.md
Ullrich Schäfer 0cf87b72ab
Fix medium-severity spec drift across 17 specs
- authentication-methods: document completeAuth mode param ("redirect"|"json");
  clarify add-passkey nudge (no dismiss mechanism, disappears on passkey add)
- journal-auth: session maxAge is 30 days; terms allow-list uses /legal/ prefix
  matching (broader than fixed list of paths)
- session-notes: mark awareness isolation and UndoManager isolation as not yet
  implemented (shared instances in current code)
- activity-feed: add fan-out scenario for visibility change to public
- explore: note that ?perPage is not yet implemented (hardcoded page size)
- multi-day-routes: add per-day GPX track split scenario (splitByDays option);
  document overnight vs isDayBreak naming gap
- osm-poi-overlays: debounce is 800ms + 2000ms min interval (not 500ms);
  retry is not automatic (fires on next viewport change)
- brouter-integration: rate limit corrected to 300/hour; add segment-cache
  requirement (client caches per-pair segments)
- wahoo-route-push: OAuth state shape uses camelCase (returnTo, pushAfter
  object) not snake_case with push_after boolean
- komoot-import: document noop adapter / ConnectedServiceManager bypass;
  note four Komoot-specific routes that bypass the generic OAuth framework
- background-jobs: exponential backoff not wired (retryLimit only); add SIGINT
- connected-services: add revoked status; name ConnectionNotActiveError
- infrastructure: add INTEGRATION_SECRET and SENTRY_DSN to env var lists;
  split secret decryption scenario by workflow (cd-apps vs cd-infra)
- secret-management: correct CD decryption — cd-apps only decrypts app.env;
  cd-infra decrypts both
- transactional-emails: welcome email is async (pg-boss job); magic-link email
  includes 6-digit numeric code
- journal-route-detail: websites are https: links (not mailto:); opening_hours
  is also displayed
- local-dev-environment: add mobile app (Expo) dev commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 11:11:31 +02:00

4.4 KiB

Purpose

POI overlay panel for toggling OpenStreetMap point-of-interest categories on the Planner map, with viewport-scoped loading, caching, and profile-aware defaults.

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 an 800ms debounce (DEBOUNCE_MS in use-pois.ts); a minimum request interval of 2000ms additionally throttles rapid viewport changes

Scenario: Zoom threshold

  • WHEN the map zoom level is below 10 (MIN_ZOOM constant in use-pois.ts)
  • 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 sets a backoff delay before the next request

Note: automatic retry is not implemented. The next request fires only on the next user viewport change or category toggle after the backoff delay has elapsed.

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 (not yet implemented)

  • WHEN a user manually disables an auto-enabled POI category
  • THEN it remains disabled until the next profile change

Note: the current implementation (use-profile-defaults.ts) always merges the profile defaults on every profile change without checking for prior manual overrides. The override-persistence behaviour is not yet implemented.