Problem
On the channel edit page (#11), Maidenhead locator and lat/lon coordinate fields are both active at once. Editing one continuously updates the other:
- Changing lat/lon calls
coordsToLocator(lat, lon, 6) on each keystroke (edit.tsx NumberInput onChange handlers).
- Blurring the locator field calls
locatorToCoords via applyLocator, snapping to the centre of the 6-character square.
Cycling locator → coords → locator → coords incrementally reduces coordinate precision because each round-trip quantises to the Maidenhead cell centre at 6-char resolution (~5 km). A user entering precise GPS coordinates will see them drift if they touch both fields.
Root cause
Two authoritative inputs kept in sync bidirectionally. Maidenhead is a lossy representation at fixed precision; treating both as live sources of truth causes drift.
Intended fix
UI behaviour
- One active input mode at a time: either coordinates (lat/lon) or Maidenhead locator — not both editable simultaneously.
- Switch control: button or segmented control to change mode, with options:
- Switch without converting (discard/sync the inactive field)
- Switch with converting (populate the target from the current authoritative value)
- Convert button: explicit one-shot "locator → coords" or "coords → locator" while staying in the current mode (optional; may be folded into switch-with-convert).
- Detail page: display both read-only values derived from the stored authoritative source (no drift on view).
Data model
Add explicit storage for the operator's chosen representation:
- New field on
Channel, e.g. maidenheadLocator: string | null (precision as entered, normalised uppercase).
- Mutual exclusivity: when
maidenheadLocator is set, location is null (or vice versa). Only one is authoritative in the model.
useLocation still gates whether location data is active at all.
- Update
docs/features/data-model/ and schema version / migration for existing projects (default: coords-only from current location field).
Import / export
- OpenGD77 import: unchanged — CPS provides
Latitude / Longitude only; populate location, leave maidenheadLocator null.
- OpenGD77 export: CPS expects coordinates — if
maidenheadLocator is authoritative, convert via locatorToCoords at export time and emit lat/lon; if location is authoritative, emit as today.
- No Maidenhead column in OpenGD77 CSV today.
Repro
- Edit a channel; enter precise coords (e.g.
55.953252, -3.188267).
- Tab to locator (auto-filled 6-char, e.g.
IO85 + subsquare).
- Blur locator (applies
locatorToCoords → centre of cell).
- Edit lat/lon slightly → locator updates → blur locator again.
- Observe coords have drifted from original precision.
Affected
src/models/codeplug.ts — maidenheadLocator (or equivalent) + migration
src/routes/channels/edit.tsx — mutually exclusive input UX
src/routes/channels/detail.tsx — read-only display from authoritative field
src/lib/import/ / export layer — coords conversion at boundary
docs/features/maidenhead.md, docs/features/data-model/
Notes / dependencies
Out of scope
- Storing both coords and locator as independently authoritative (that recreates the bug).
- Sub-6-char locator precision in the model unless explicitly entered by the user.
Workflow note
Branch from origin/main, atomic conventional commits, PR linking Closes #.
Problem
On the channel edit page (#11), Maidenhead locator and lat/lon coordinate fields are both active at once. Editing one continuously updates the other:
coordsToLocator(lat, lon, 6)on each keystroke (edit.tsxNumberInputonChangehandlers).locatorToCoordsviaapplyLocator, snapping to the centre of the 6-character square.Cycling locator → coords → locator → coords incrementally reduces coordinate precision because each round-trip quantises to the Maidenhead cell centre at 6-char resolution (~5 km). A user entering precise GPS coordinates will see them drift if they touch both fields.
Root cause
Two authoritative inputs kept in sync bidirectionally. Maidenhead is a lossy representation at fixed precision; treating both as live sources of truth causes drift.
Intended fix
UI behaviour
Data model
Add explicit storage for the operator's chosen representation:
Channel, e.g.maidenheadLocator: string | null(precision as entered, normalised uppercase).maidenheadLocatoris set,locationisnull(or vice versa). Only one is authoritative in the model.useLocationstill gates whether location data is active at all.docs/features/data-model/and schema version / migration for existing projects (default: coords-only from currentlocationfield).Import / export
Latitude/Longitudeonly; populatelocation, leavemaidenheadLocatornull.maidenheadLocatoris authoritative, convert vialocatorToCoordsat export time and emit lat/lon; iflocationis authoritative, emit as today.Repro
55.953252, -3.188267).IO85+ subsquare).locatorToCoords→ centre of cell).Affected
src/models/codeplug.ts—maidenheadLocator(or equivalent) + migrationsrc/routes/channels/edit.tsx— mutually exclusive input UXsrc/routes/channels/detail.tsx— read-only display from authoritative fieldsrc/lib/import// export layer — coords conversion at boundarydocs/features/maidenhead.md,docs/features/data-model/Notes / dependencies
src/lib/maidenhead.ts— do not fork.Out of scope
Workflow note
Branch from
origin/main, atomic conventional commits, PR linkingCloses #.