feat(grid): support 8-character (extended) Maidenhead locators#246
Merged
Merged
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
@/lib/gridhelpers 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')returnedfalsegridToLatLon('FN31pr55')returnednullso the "how far, which way" readout simply showed nothing for a perfectly valid, more-precise grid. The
grid_locatorcolumn isvarchar(10)and ADIF import stores the rawGRIDSQUARE, 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_REnow 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 (FN3155stays invalid).gridToLatLonadds 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
tests/grid.spec.tscovering 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 thatgridPathresolves with an 8-char locator on either end.npm run typecheck,npm run lint, andnpm run buildall clean.Future follow-up
src/components/ContactLocationMap.tsxkeeps 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/gridgridToLatLonso the map pin and the readout share one source of truth, but that's a separate refactor kept out of this focused change.