From f89f163e227325c9d7f30ea03cc2530bcf6ad081 Mon Sep 17 00:00:00 2001 From: Daniel Michelberger Date: Thu, 6 Aug 2026 17:30:20 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Punkte=20ohne=20Koordinaten,=20Fehlertex?= =?UTF-8?q?t,=20neuer=20Test=20f=C3=BCr=20Null-Island?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prüfe lat/lon auf null vor Number-Konvertierung (Number(null) ist 0, nicht NaN) - Ergänze Test für übersprungene Punkte ohne Koordinaten - Bessere Fehlermeldung für ungültiges XML Co-Authored-By: Claude Opus 5 (1M context) --- frontend/src/lib/gpx.test.ts | 14 ++++++++++++++ frontend/src/lib/gpx.ts | 15 +++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/gpx.test.ts b/frontend/src/lib/gpx.test.ts index 4058d81..4b69c66 100644 --- a/frontend/src/lib/gpx.test.ts +++ b/frontend/src/lib/gpx.test.ts @@ -99,6 +99,20 @@ describe('parseGpx', () => { expect(parseGpx(route).points).toHaveLength(2) }) + it('überspringt Punkte ohne lat/lon statt sie auf 0,0 zu setzen', () => { + const broken = ` + + 300 + 310 + 320 +` + const r = parseGpx(broken) + expect(r.points).toHaveLength(2) + // Ohne die Prüfung reichte die Bounding-Box bis 0/0 + expect(r.bounds[0][0]).toBeGreaterThan(11) + expect(r.distance_m).toBeLessThan(1000) + }) + it('wirft bei einer Datei ohne Punkte', () => { const empty = ` ` diff --git a/frontend/src/lib/gpx.ts b/frontend/src/lib/gpx.ts index 6d3264f..d6bb9ae 100644 --- a/frontend/src/lib/gpx.ts +++ b/frontend/src/lib/gpx.ts @@ -115,7 +115,7 @@ export function parseGpx(xml: string): ParsedGpx { const doc = new DOMParser().parseFromString(xml, 'application/xml') if (doc.querySelector('parsererror')) { - throw new Error('Die Datei ist ungültig.') + throw new Error('Die GPX-Datei ist ungültig.') } // Manche Programme exportieren Routen (rtept) statt Tracks (trkpt). @@ -130,8 +130,15 @@ export function parseGpx(xml: string): ParsedGpx { const points: TrackPoint[] = [] for (const n of nodes) { - const lat = Number(n.getAttribute('lat')) - const lng = Number(n.getAttribute('lon')) + const latAttr = n.getAttribute('lat') + const lngAttr = n.getAttribute('lon') + // Number(null) ist 0, nicht NaN — ohne diese Prüfung landen Punkte + // ohne Koordinaten bei 0/0 im Golf von Guinea und verfälschen + // Streckenlänge und Bounding-Box. + if (latAttr === null || lngAttr === null) continue + + const lat = Number(latAttr) + const lng = Number(lngAttr) if (!Number.isFinite(lat) || !Number.isFinite(lng)) continue const eleText = n.getElementsByTagName('ele')[0]?.textContent @@ -171,7 +178,7 @@ export function parseGpx(xml: string): ParsedGpx { // Abstieg über Schwelle: neuer Bezugspunkt, aber nicht gezählt lastCountedEle = ele } - // Kleine Schwankungen (-3...+3) ignorieren, Bezugspunkt bleibt + // Kleine Schwankungen (unter 3 m) ignorieren, Bezugspunkt bleibt } }