From d5bcecb3457f6b878995c078eb7a4811988c0f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Thu, 2 Apr 2026 20:19:55 +0100 Subject: [PATCH] Fix distance calculation for GPX files without elevation data Distance was only stored in the elevation profile array, which was empty when track points had no elements. Now computed via haversine independently and exposed as gpxData.distance. Tested: berlin-dresden-radweg-2021.gpx (5660 points, no elevation) now correctly reports 248.8 km. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/journal/app/lib/activities.server.ts | 3 +-- apps/journal/app/lib/routes.server.ts | 6 +----- packages/gpx/src/parse.ts | 8 ++++---- packages/gpx/src/types.ts | 2 ++ 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/journal/app/lib/activities.server.ts b/apps/journal/app/lib/activities.server.ts index 4e48b8c..e3a399c 100644 --- a/apps/journal/app/lib/activities.server.ts +++ b/apps/journal/app/lib/activities.server.ts @@ -23,8 +23,7 @@ export async function createActivity(ownerId: string, input: ActivityInput) { if (input.gpx) { try { const gpxData = await parseGpxAsync(input.gpx); - const profile = gpxData.elevation.profile; - distance = profile.length > 0 ? Math.round(profile[profile.length - 1]!.distance) : null; + distance = gpxData.distance || null; elevationGain = gpxData.elevation.gain; elevationLoss = gpxData.elevation.loss; diff --git a/apps/journal/app/lib/routes.server.ts b/apps/journal/app/lib/routes.server.ts index c9f1b45..e8bd8d3 100644 --- a/apps/journal/app/lib/routes.server.ts +++ b/apps/journal/app/lib/routes.server.ts @@ -145,11 +145,7 @@ async function computeRouteStats(gpxString: string) { try { const gpxData = await parseGpxAsync(gpxString); return { - distance: Math.round( - gpxData.elevation.profile.length > 0 - ? gpxData.elevation.profile[gpxData.elevation.profile.length - 1]!.distance - : 0, - ), + distance: gpxData.distance, elevationGain: gpxData.elevation.gain, elevationLoss: gpxData.elevation.loss, }; diff --git a/packages/gpx/src/parse.ts b/packages/gpx/src/parse.ts index d1b118b..70a3a0e 100644 --- a/packages/gpx/src/parse.ts +++ b/packages/gpx/src/parse.ts @@ -46,9 +46,9 @@ function parseGpxWithParser(parser: DOMParser, xml: string): GpxData { const name = doc.querySelector("metadata > name")?.textContent ?? undefined; const waypoints = parseWaypoints(doc); const tracks = parseTracks(doc); - const elevation = computeElevation(tracks); + const { totalDistance, ...elevation } = computeElevation(tracks); - return { name, waypoints, tracks, elevation }; + return { name, waypoints, tracks, distance: totalDistance, elevation }; } function parseWaypoints(doc: Document): Waypoint[] { @@ -85,7 +85,7 @@ function parseTracks(doc: Document): TrackPoint[][] { return tracks; } -function computeElevation(tracks: TrackPoint[][]): GpxData["elevation"] { +function computeElevation(tracks: TrackPoint[][]): GpxData["elevation"] & { totalDistance: number } { let gain = 0; let loss = 0; const profile: ElevationProfile[] = []; @@ -112,7 +112,7 @@ function computeElevation(tracks: TrackPoint[][]): GpxData["elevation"] { } } - return { gain: Math.round(gain), loss: Math.round(loss), profile }; + return { totalDistance: Math.round(totalDistance), gain: Math.round(gain), loss: Math.round(loss), profile }; } /** Haversine distance between two points in meters */ diff --git a/packages/gpx/src/types.ts b/packages/gpx/src/types.ts index d7861cc..bb7d0a2 100644 --- a/packages/gpx/src/types.ts +++ b/packages/gpx/src/types.ts @@ -18,6 +18,8 @@ export interface GpxData { name?: string; waypoints: Waypoint[]; tracks: TrackPoint[][]; + /** Total distance in meters (haversine, works with or without elevation data) */ + distance: number; elevation: { gain: number; loss: number;