Skip to content

feat(grid): support 8-character (extended) Maidenhead locators#246

Merged
patrickrb merged 1 commit into
mainfrom
optio/task-58e3cfdb-184a-4666-8d97-223b1062b0b3
Jul 24, 2026
Merged

feat(grid): support 8-character (extended) Maidenhead locators#246
patrickrb merged 1 commit into
mainfrom
optio/task-58e3cfdb-184a-4666-8d97-223b1062b0b3

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Problem

The @/lib/grid helpers that power the live distance & bearing readout on the logging form (added in #233) only recognised 4- and 6-character Maidenhead locators. An 8-character extended-square grid — routinely logged by VHF/UHF/microwave and satellite operators where the extra precision matters — was treated as invalid:

  • isValidGrid('FN31pr55') returned false
  • gridToLatLon('FN31pr55') returned null

so the "how far, which way" readout simply showed nothing for a perfectly valid, more-precise grid. The grid_locator column is varchar(10) and ADIF import stores the raw GRIDSQUARE, so 8-char grids already flow into the DB — they just couldn't be resolved to a position.

Solution

Purely additive changes in src/lib/grid.ts:

  • GRID_RE now accepts an optional extended-square digit pair, but only after a subsquare (/^[A-R]{2}[0-9]{2}([A-X]{2}([0-9]{2})?)?$/) so a level can't be skipped (FN3155 stays invalid).
  • gridToLatLon adds the extended-square offset: the two digits subdivide the subsquare into a 10×10 grid (30″ lon × 15″ lat per cell); the result is centred on the cell, mirroring how 4- and 6-char grids are centred.

4- and 6-character behaviour is unchanged (verified by the existing tests). Everything downstream — gridPath, the logging-form readout, isValidGrid — benefits automatically because it flows through these two functions.

Testing

  • Added unit tests to tests/grid.spec.ts covering 8-char validation (accept/reject, including "can't skip the subsquare level" and "extended square must be digits"), that an 8-char locator resolves inside its parent subsquare near the 6-char centre, and that gridPath resolves with an 8-char locator on either end.
  • Confirmed the new tests fail before the implementation (3 red) and pass after (17/17 green).
  • npm run typecheck, npm run lint, and npm run build all clean.

Future follow-up

src/components/ContactLocationMap.tsx keeps its own simplified grid parser. It handles 8-char grids gracefully today (ignores the extended pair, landing on the subsquare centre — a sub-arcminute difference, imperceptible on the map, no crash). A worthwhile cleanup is to point that component at the canonical @/lib/grid gridToLatLon so the map pin and the readout share one source of truth, but that's a separate refactor kept out of this focused change.

The Maidenhead helpers powering the live distance/bearing readout on the
logging form only recognised 4- and 6-character locators. An 8-character
(extended-square) grid — routinely logged by VHF/UHF/microwave and
satellite operators for the extra precision — was rejected: isValidGrid
returned false and gridToLatLon returned null, so the 'how far, which way'
readout showed nothing for a perfectly valid grid.

Extend GRID_RE to accept an optional extended-square digit pair (only
after a subsquare, so a level can't be skipped) and refine gridToLatLon to
add the extended-square offset (a 10x10 subdivision of the subsquare,
30" lon x 15" lat per cell) centred on the cell. 4/6-char behaviour is
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodelog Ready Ready Preview, Comment Jul 24, 2026 11:09am

Request Review

@patrickrb
patrickrb merged commit a6db4d4 into main Jul 24, 2026
5 checks passed
patrickrb added a commit that referenced this pull request Jul 24, 2026
… API (#249)

Commit #246 added canonical 8-character (extended) Maidenhead support to
`src/lib/grid.ts` (`isValidGrid`, `gridToLatLon`), and #248 uses the on-air
station's grid as the distance/bearing origin. But three input validators
kept their own 4/6-only regex and drifted behind:

- `new-contact` form's inline `validateGridLocator`
- `POST /api/stations`
- `PUT /api/stations/[id]`

Result: typing an 8-char grid (e.g. `FN31pr55`) into the logging form showed
"Invalid grid locator format" and blocked save, and saving an 8-char station
grid — the exact locator used as the distance origin — 400'd. VHF/UHF/
microwave and satellite operators log 8-char locators for the extra precision,
so this silently rejected valid input.

Centralize the check as `gridLocatorError(grid)` in the canonical grid module
(null for blank/valid, message otherwise) and route all three call sites
through it, so the form and the API can never drift from `isValidGrid` again.

Tested: added unit coverage for `gridLocatorError` (blank, 4/6/8-char, malformed);
`grid.spec.ts` 30/30 pass; typecheck, lint, and build all clean.

Co-authored-by: Optio Agent <optio-agent@noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant