Skip to content

fix: snap weather-proxy coordinates before they leave the device - #129

Merged
fetzu merged 1 commit into
mainfrom
fix/weather-coordinate-snapping
Aug 15, 2026
Merged

fix: snap weather-proxy coordinates before they leave the device#129
fetzu merged 1 commit into
mainfrom
fix/weather-coordinate-snapping

Conversation

@fetzu

@fetzu fetzu commented Aug 15, 2026

Copy link
Copy Markdown
Owner

What

AviationWeatherService sent full double-precision live GPS position in the METAR and SIGMET query strings, on a timer for as long as a briefing stayed open. This snaps to the same 0.25° grid the sibling WindsAloftService has always used, and adds the service's first test class.

Why

A query string is the least-protected place a coordinate can sit — it persists in edge request logs long after the response is gone, and this one is derived from a live flight track.

The extra precision bought nothing. The weather worker's parseCoordinatessnapToGrid (GRID_DEGREES = 0.25 in workers/weather/src/lib.ts) already rounds every incoming lat/lon to this grid before it builds a cache key, an upstream URL, or the distance sort in transformMetars/filterSigmets. Responses are byte-identical. A half-cell is ~7.5 nm of latitude, an order of magnitude inside the 60 nm METAR radius and two inside the 150 nm SIGMET radius.

How

  • gridDegrees + snap(_:) added to AviationWeatherService, both nonisolated, mirroring WindsAloftService.
  • Both fetchMetars and fetchSigmets snap before interpolating into the URL.
  • New AviationWeatherServiceTests (12 tests):
    • Grid — quantization, southern/western hemispheres, idempotence (the property that makes the worker's second snap a no-op), parity with WindsAloftService.snap, half-cell displacement bound, and precision-discard on a real full-precision fix.
    • Decode contract — METAR/TAF/SIGMET fixtures copied from workers/weather/src/aviation.ts. These types mirror a separate repository and nothing in the build catches a rename. Includes the VRB-vs-absent-wind distinction the service's doc comment calls out, and both timestamp forms the worker emits (isoTime returns toISOString() with milliseconds for epoch inputs, a passthrough string without them otherwise).

Test

scripts/run-tests.sh504 tests, 0 failures on iPad Air 11-inch (M4). New class alone: 12 tests, 0 failures.

🤖 Generated with Claude Code

@fetzu fetzu self-assigned this Aug 15, 2026
AviationWeatherService sent full double-precision live GPS position in the
metar/sigmet query strings, on a timer for as long as a briefing stayed open.
A query string is the least-protected place a coordinate can sit: it persists
in edge request logs long after the response itself is gone.

The weather worker already snaps every incoming lat/lon to a 0.25 degree grid
(GRID_DEGREES) before it builds a cache key, an upstream URL, or a distance
sort, so the extra precision bought nothing. Responses are unchanged. This
applies the same grid client-side, matching what WindsAloftService has always
done.

Adds AviationWeatherServiceTests: the grid (quantization, both hemispheres,
idempotence against the worker's second snap, parity with WindsAloftService,
half-cell displacement bound) and the decode contract against the proxy's
response shape, which mirrors types living in a separate repository that
nothing in the build would catch drifting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fetzu fetzu added the enhancement New feature or request label Aug 15, 2026
@fetzu fetzu added this to the v4.4.0 milestone Aug 15, 2026
@fetzu
fetzu force-pushed the fix/weather-coordinate-snapping branch from 83290fd to 523964d Compare August 15, 2026 11:51
@fetzu
fetzu merged commit a83e445 into main Aug 15, 2026
1 check passed
@fetzu
fetzu deleted the fix/weather-coordinate-snapping branch August 15, 2026 11:53
let lon = Self.snap(coordinate.longitude)
guard let url = URL(string:
"\(baseURL)/v1/metar?lat=\(coordinate.latitude)&lon=\(coordinate.longitude)&radius=60"
"\(baseURL)/v1/metar?lat=\(lat)&lon=\(lon)&radius=60"
let lon = Self.snap(coordinate.longitude)
guard let url = URL(string:
"\(baseURL)/v1/sigmet?lat=\(coordinate.latitude)&lon=\(coordinate.longitude)&radius=150"
"\(baseURL)/v1/sigmet?lat=\(lat)&lon=\(lon)&radius=150"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants