Skip to content

feat: distance from me on channel detail, proximity sort, operator marker on maps #70

Description

@pskillen

Problem

When out and about, an operator may have their codeplug on a phone and want to know how far they are from a repeater or other geolocated channel — and see it on a map. Today:

  • Channel detail shows coordinates, locator, and an embedded map, but no distance from the operator.
  • Channel list sorts alphabetically only; no proximity sort.
  • Maps (CodeplugMap on channel detail, channel list, zone detail) plot channels/zones but not the operator's position.

#59 shipped UseMyLocationButton for seeding coordinates on edit/converter, but there is no session operator position reused across browse views for distance and map context.

Intended outcome

Add operator-position awareness for field use — distance display, proximity sort, and "you are here" on maps.

Operator position (session)

  • Introduce a session-scoped operator position (lat/lon + optional accuracy), obtained via existing useGeolocation / UseMyLocationButtonnot persisted to localStorage or codeplug.
  • Provide a shared hook or context, e.g. useOperatorPosition() — set on explicit user action; clear on tab close or manual "Clear my location".
  • Surfaces to obtain position:

Channel detail page

When operator position is known and channel has useLocation + coordinates:

  • Show distance from me in Location section (e.g. 12.4 km or 850 m — pick sensible threshold for unit switch).
  • On embedded CodeplugMap: plot operator marker (distinct style from channel marker) plus channel marker; fit bounds or centre to show both when reasonable.
  • When position unknown: show Use my location to enable distance; distance row hidden or "—".

Channel list

  • Add sort control: Name (default) | Distance from me.
  • Distance sort: channels with geolocation ascending by haversine distance; channels without location at bottom (stable sub-sort by name).
  • Distance sort requires operator position — if unset, show helper + Use my location button; disable sort or prompt on select.
  • Optional v1: Distance optional column when position known (defer if sort alone is enough).
  • Channel list map (CodeplugMap at bottom of list): show operator marker when position set.

Zone detail map

  • On zone detail CodeplugMap: show operator marker when position set (zone hull + member channels + you).
  • No per-member distance table required for v1.

Distance math

  • New util src/lib/geoDistance.ts (or extend src/lib/geo.ts): haversine distance in metres; format for display (formatDistanceM(m)).
  • Use WGS84 coords throughout; no elevation.

Affected

  • New useOperatorPosition hook or lightweight context (session state only)
  • src/lib/geo.ts or geoDistance.ts — haversine + formatting
  • src/routes/channels/detail.tsx — distance field, map operator marker
  • src/routes/channels/list.tsx — sort control, optional map marker
  • src/routes/zones/detail.tsx — map operator marker
  • src/components/CodeplugMap/CodeplugMap.tsx — optional operatorPosition prop + marker rendering
  • docs/features/maidenhead.md or map docs — session geolocation for browse vs edit

Notes / dependencies

  • Builds on feat: "use my location" on maidenhead converter and channel editor #59 (UseMyLocationButton, useGeolocation, geolocation.ts) — reuse, don't fork.
  • Operator position is ephemeral — distinct from channel location and from saving QTH in Settings (out of scope).
  • Channels with hideFromMap still participate in distance sort/display if they have coordinates (hideFromMap affects map plots only — document behaviour).
  • Privacy: position used only after explicit button click; not uploaded; session-only.

Out of scope

  • Continuous GPS tracking / watchPosition while browsing.
  • Bearing / compass direction to repeater.
  • Filtering channel list by radius ("within 50 km") — sort is sufficient for v1.
  • Persisting last known operator position across sessions.
  • Summary page map (feat: revamp codeplug summary page with channel map and improved layout #61) — can follow same operatorPosition prop pattern later.

Manual verify

  1. Channel detail → Use my location → distance appears; map shows two markers.
  2. Deny permission → graceful message; no distance.
  3. Channel list → sort by distance → nearest repeater first; unlocated channels last.
  4. Zone detail map → operator marker visible alongside zone hull.
  5. Reload page → operator position cleared (session); distance hidden until requested again.

Workflow note

Branch from origin/main, atomic conventional commits (distance util → operator position hook → CodeplugMap marker → detail → list sort → zone map → docs), PR linking Closes #70.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions