Fix a severe GPX parse slowdown and add route (<rte>) support
GPX tracks were silently failing to render for large real-world files: point_extensions used REXML's "extensions//*" XPath, and the // descendant axis costs low-single-digit milliseconds per call regardless of subtree size. Run once per point, that's negligible on small files but ~30s on a real 8000-point track — long enough to hit a proxy timeout in production, so the file just never appeared with no error. Replaced with plain recursive child traversal (~40x faster on the same file, confirmed 29.9s -> 3.16s); test_parsing_many_points_with_extensions_stays_fast guards against reintroducing it. Also adds support for <rte> routes (not just <trk> tracks and <wpt> waypoints), sharing the track/route conversion logic via a new line_feature helper. Routes get their own "route" kind and a dashed ROUTE_ICON in the file tree so the type is visible at a glance.
This commit is contained in:
1 parent
0394befdec
commit
708c08259b
6 files changed
+149
-19
No files matched your search
+21
-3
@@ -181,8 +181,16 @@ same GeoJSON `FeatureCollection` shape so the frontend doesn't need to
|
||||
know which format it got. `Gpx` parses the GPX with REXML: `<wpt>`
|
||||
waypoints become `Point` features (with `name`, `desc` and `time`
|
||||
carried through as properties), each `<trk>` becomes one
|
||||
`MultiLineString` feature (one line per `<trkseg>`); `<rte>` routes
|
||||
are not handled. `Fit` reads Garmin's binary FIT format directly
|
||||
`MultiLineString` feature (one line per `<trkseg>`), and each `<rte>`
|
||||
becomes one too (a route is a single continuous line by definition, no
|
||||
`<trkseg>`-style grouping) — the two are told apart only by a `kind`
|
||||
property (`"track"` vs `"route"`; waypoints are `"waypoint"`), which
|
||||
`Gpx.kinds` also exposes per-file so the file tree can show a dashed
|
||||
route icon next to a route file and a solid one for a track
|
||||
(`public/js/icons.js`'s `ROUTE_ICON`/`TRACK_ICON`,
|
||||
`public/js/gpx.js`'s `_kindIcon`). `<rte>` has no GPX equivalent of
|
||||
`<trk>`'s `<type>`, so a route's `activity_type` is always `nil`.
|
||||
`Fit` reads Garmin's binary FIT format directly
|
||||
(hand-rolled — see below) into a single `MultiLineString` `track`
|
||||
feature; FIT activity files don't really have a GPX-waypoint
|
||||
equivalent, so it never produces `Point` features. MapLibre's
|
||||
@@ -280,7 +288,17 @@ below). For FIT, those extras are the `record` message's own
|
||||
device-specific `<extensions>` block under each `<trkpt>` — usually
|
||||
Garmin's `TrackPointExtension`, matched by local element name only
|
||||
(`hr`/`cad`/`speed`) so it doesn't care which XML namespace prefix a
|
||||
given file happens to declare.
|
||||
given file happens to declare. That `<extensions>` block is walked by
|
||||
hand (a plain recursive `elements.each`) rather than REXML's
|
||||
`"extensions//*"` XPath, which this used originally: the `//`
|
||||
descendant axis costs low-single-digit milliseconds *per call*
|
||||
regardless of how small the subtree actually is, and this runs once
|
||||
per point — negligible on a short file, but ~30s for a real
|
||||
8000-point one (enough to hit a proxy timeout in production, so the
|
||||
track just silently never rendered). Plain traversal is ~40x faster on
|
||||
the same file; `test/test_gpx.rb`'s
|
||||
`test_parsing_many_points_with_extensions_stays_fast` is a regression
|
||||
test for it.
|
||||
+
|
||||
Each chart is its own single-series `<svg>` (elevation/speed/cadence/
|
||||
heart rate are on wildly different scales, so this is small multiples
|
||||
|
||||
Reference in new issue
Block a user