trails/openspec/changes/activity-locations/proposal.md
Ullrich Schäfer 5dd4968626 docs+openspec: prior-art research (Organic Maps, Endurain, wanderer) and 15 proposals
Add docs/inspirations.md as the durable record of the 2026-07-05/06
prior-art research — per-project learnings with source paths, canonical
credit lines, and the changes each spawned — and extend the
acknowledgment lists in philosophy.md/architecture.md (Organic Maps,
Endurain, wanderer).

New OpenSpec changes (proposal/design/specs/tasks each):
- Organic Maps: elevation-profile-hardening, gpx-parser-robustness,
  hiking-time-estimate, poi-index, hiking-foot-profile
- Endurain: account-export, activity-duplicate-review,
  fit-parsing-hardening, activity-locations, self-hosting-guide,
  activity-privacy-controls
- wanderer: federation-hardening, link-share-tokens
- credits-page (user-visible acknowledgments)

Updated in-flight changes with wanderer prior-art sections:
route-federation, route-discovery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 07:23:00 +02:00

2.5 KiB

Why

Activities and routes display as bare stat rows — nothing tells a feed reader where "Sunday ride, 62 km" happened, which is often the most interesting fact. Endurain reverse-geocodes activity start points into city/country names (reviewed 2026-07-06), but does it by calling Nominatim/geocode.maps.co — a third-party request carrying user location that trails' privacy principles rule out. We can have the feature without the leak: an offline reverse geocoder using a bundled place dataset, running entirely on-instance.

What Changes

  • New shared package capability: offline reverse geocoding — nearest populated place (name, admin region, ISO country) for a coordinate, from a bundled GeoNames-derived dataset (cities500-class, a few MB), no network calls.
  • Activities and routes gain a derived location label ("Freiburg, Germany"), computed at save/import time from the geometry's start point and stored on the row.
  • Location shows on detail pages and feed/list cards; localized country names via the existing i18n locales.
  • Privacy-coupled: the label derives from where the track starts, so it is masked whenever the start location is hidden — it follows the same rules as the map/start data under the activity-privacy-controls change (if that lands, its hideLocation-style flag governs this label; until then, the label is only shown where the geometry itself is visible).
  • Backfill of existing rows via a one-shot background job.
  • Not in scope: full address geocoding, location search/filtering, per-point location, and any online geocoding fallback.

Capabilities

New Capabilities

  • activity-locations: Offline reverse geocoding of route/activity start points into display labels — dataset, derivation timing, storage, display, masking rule, and backfill.

Modified Capabilities

Impact

  • New packages/geocode (or module in an existing package): bundled dataset + nearest-place lookup (kd-tree/grid), pure and unit-tested; used by the journal only.
  • packages/db: location_name, location_country (text, nullable) on activities and routes + migration.
  • Journal: set at gpx-save/import time; one-shot pg-boss backfill job; feed card + detail page display (i18n).
  • Privacy manifest: note that location labels are derived on-instance with no third-party calls.
  • Bundle size: the dataset ships server-side only (journal), never to the browser.