feat(gpx): lenient point parsing + route (<rte>) support

Task groups 1–2 of gpx-parser-robustness. The parser trusted its input:
`parseFloat(attr ?? "0")` turned a missing lat/lon into a 0,0 Null Island
point (which passes range validation) and garbage into NaN that poisoned
distance and gain/loss totals; `<rte>`/`rtept` files (Garmin courses, many
exporters) parsed to zero track points and were rejected.

- `parsePoint`: skip a trkpt/rtept whose lat/lon is missing or non-finite
  (no more 0,0 default); a non-finite `<ele>` becomes `undefined` so it
  never leaks NaN into totals. Parsing stays parseFloat-lenient (trailing
  junk like `471.0m` still accepted), gated by Number.isFinite.
- Drop segments left with fewer than 2 points (render nothing / break
  distance math).
- Parse `<rte>` as track segments appended after `<trk>` segments, rtept
  handled identically — route-only files now import.

No GpxData shape change; well-formed files parse identically. Updated the
geom single-point test to the new drop-invariant.

Verified: gpx typecheck + lint clean, 76/76 tests pass, journal + planner
typecheck unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-07-14 00:06:35 +02:00
parent e2f25e1d27
commit 99278fd305
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
4 changed files with 184 additions and 26 deletions

View file

@ -1,13 +1,13 @@
## 1. Parser lenience
- [ ] 1.1 Add a finite-number coordinate gate in `packages/gpx/src/parse.ts`: skip `trkpt`/`rtept` with missing or non-finite `lat`/`lon`; treat non-finite `<ele>` as undefined
- [ ] 1.2 Drop segments with fewer than 2 surviving points
- [ ] 1.3 Unit tests: missing-lat point skipped, garbage lat skipped with finite stats, garbage ele → undefined with finite gain/loss, single-point segment dropped, well-formed files byte-identical output to before
- [x] 1.1 Add a finite-number coordinate gate in `packages/gpx/src/parse.ts`: skip `trkpt`/`rtept` with missing or non-finite `lat`/`lon`; treat non-finite `<ele>` as undefined
- [x] 1.2 Drop segments with fewer than 2 surviving points
- [x] 1.3 Unit tests: missing-lat point skipped, garbage lat skipped with finite stats, garbage ele → undefined with finite gain/loss, single-point segment dropped, well-formed files byte-identical output to before
## 2. Route (`<rte>`) support
- [ ] 2.1 Parse `<rte>`/`rtept` as track segments (appended after `<trk>` segments), reusing the same point parsing and lenience
- [ ] 2.2 Unit tests: route-only file yields one segment and passes `validateGpx`; mixed trk+rte ordering; rtept with ele/time preserved
- [x] 2.1 Parse `<rte>`/`rtept` as track segments (appended after `<trk>` segments), reusing the same point parsing and lenience
- [x] 2.2 Unit tests: route-only file yields one segment and passes `validateGpx`; mixed trk+rte ordering; rtept with ele/time preserved
## 3. Timestamp repair