Multiple improvements
This commit is contained in:
+71
-1
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { parseGpx } from '../src/services/gpx.js';
|
||||
import { parseGpx, gpxWithWaypoints } from '../src/services/gpx.js';
|
||||
import { SAMPLE_GPX } from '../test-support/gpx-fixtures.js';
|
||||
|
||||
test('parseGpx reads synthetic track data without repository example files', () => {
|
||||
@@ -11,3 +11,73 @@ test('parseGpx reads synthetic track data without repository example files', ()
|
||||
assert.equal(parsed.points[0].lat, 52.5200);
|
||||
assert.equal(parsed.points[2].lon, 13.4090);
|
||||
});
|
||||
|
||||
const point = (tag, lat, lon, content = '') => `<${tag} lat="${lat}" lon="${lon}">${content}</${tag}>`;
|
||||
const gpx = (waypoints, path = '', extra = '') => `<gpx version="1.1" creator="Test" xmlns:wegwichtel="urn:wegwichtel:poi">${waypoints}${path}${extra}</gpx>`;
|
||||
const track = points => `<trk><trkseg>${points}</trkseg></trk>`;
|
||||
const locations = points => points.map(({ lat, lon }) => [lat, lon]);
|
||||
|
||||
test('waypoints alone become route points in station order and contribute to distance and bounds', () => {
|
||||
const parsed = parseGpx(gpx(point('wpt', 50, 10) + point('wpt', 50.01, 10.02)));
|
||||
assert.deepEqual(locations(parsed.points), [[50, 10], [50.01, 10.02]]);
|
||||
assert.equal(parsed.metrics.pointCount, 2);
|
||||
assert.ok(parsed.metrics.distanceM > 0);
|
||||
assert.equal(parsed.metrics.maxLon, 10.02);
|
||||
assert.match(parsed.normalizedXml, /<wpt\b/);
|
||||
assert.match(parsed.normalizedXml, /<rtept\b/);
|
||||
assert.deepEqual(locations(parseGpx(parsed.normalizedXml).points), locations(parsed.points));
|
||||
});
|
||||
|
||||
test('track points and stations both shape the path without dropping nearby station anchors', () => {
|
||||
const wp = point('wpt', 50, 10.00001) + point('wpt', 50.005, 10.015);
|
||||
const recorded = point('trkpt', 50, 10) + point('trkpt', 50, 10.00002)
|
||||
+ point('trkpt', 50, 10.01) + point('trkpt', 50, 10.02);
|
||||
const parsed = parseGpx(gpx(wp, track(recorded)));
|
||||
assert.deepEqual(locations(parsed.points), [[50, 10], [50, 10.00001], [50, 10.01], [50.005, 10.015], [50, 10.02]]);
|
||||
assert.equal(parsed.metrics.maxLat, 50.005);
|
||||
assert.equal(parsed.points.length, 5);
|
||||
assert.deepEqual(parsed.points.map(p => p.sequence), [0, 1, 2, 3, 4]);
|
||||
assert.deepEqual(locations(parseGpx(parsed.normalizedXml).points), locations(parsed.points));
|
||||
});
|
||||
|
||||
test('duplicate station locations do not multiply route points and station order is retained on loops', () => {
|
||||
const stations = point('wpt', 50, 10.02) + point('wpt', 50, 10.02) + point('wpt', 50, 10.01);
|
||||
const recorded = [10, 10.01, 10.02, 10.01, 10].map(lon => point('trkpt', 50, lon)).join('');
|
||||
const parsed = parseGpx(gpx(stations, track(recorded)));
|
||||
assert.deepEqual(locations(parsed.points), [[50, 10], [50, 10.01], [50, 10.02], [50, 10.01], [50, 10]]);
|
||||
assert.deepEqual(locations(parseGpx(parsed.normalizedXml).points), locations(parsed.points));
|
||||
});
|
||||
|
||||
test('existing routes and replacement points incorporate stations, including stations before and after the path', () => {
|
||||
const stations = [9.99, 10.005, 10.02].map(lon => point('wpt', 50, lon)).join('');
|
||||
const route = `<rte>${point('rtept', 50, 10)}${point('rtept', 50, 10.01)}</rte>`;
|
||||
const xml = gpx(stations, route);
|
||||
const parsed = parseGpx(xml);
|
||||
assert.deepEqual(locations(parsed.points), [[50, 9.99], [50, 10], [50, 10.005], [50, 10.01], [50, 10.02]]);
|
||||
assert.deepEqual(locations(parseGpx(xml, [{ lat: 50, lon: 10 }, { lat: 50, lon: 10.01 }]).points), locations(parsed.points));
|
||||
});
|
||||
|
||||
test('missing and invalid station coordinates are excluded while GPS gaps remain available for editing', () => {
|
||||
const stations = '<wpt><name>Ohne Ort</name></wpt>' + point('wpt', 91, 10) + point('wpt', 50, 181) + point('wpt', 50, 10);
|
||||
const extra = '<extensions><wegwichtel:gpsGap time="2026-10-04T10:00:00Z"/><wegwichtel:pendingWaypoint><name>Später</name></wegwichtel:pendingWaypoint></extensions>';
|
||||
const parsed = parseGpx(gpx(stations, '', extra));
|
||||
assert.deepEqual(locations(parsed.points), [[50, 10]]);
|
||||
assert.match(parsed.normalizedXml, /wegwichtel:gpsGap/);
|
||||
assert.match(parsed.normalizedXml, /wegwichtel:pendingWaypoint/);
|
||||
const empty = parseGpx(gpx('', '', extra));
|
||||
assert.equal(empty.metrics.pointCount, 0);
|
||||
assert.equal(empty.metrics.minLat, null);
|
||||
});
|
||||
|
||||
|
||||
test('moving or deleting a station replaces its generated route anchor without removing recorded points', () => {
|
||||
const original = parseGpx(gpx(point('wpt', 50.01, 10.005), track(point('trkpt', 50, 10) + point('trkpt', 50, 10.01))));
|
||||
assert.match(original.normalizedXml, /wegwichtel:waypointRoutePoint/);
|
||||
const updated = parseGpx(gpxWithWaypoints(original.normalizedXml, [{ id: 1, title: 'Neu', lat: 50.02, lon: 10.006 }]));
|
||||
assert.equal(updated.points.length, 3);
|
||||
assert.equal(updated.metrics.maxLat, 50.02);
|
||||
assert.equal(updated.points.some(p => p.lat === 50.01), false);
|
||||
const removed = parseGpx(gpxWithWaypoints(updated.normalizedXml, []));
|
||||
assert.deepEqual(locations(removed.points), [[50, 10], [50, 10.01]]);
|
||||
assert.deepEqual(locations(parseGpx(updated.normalizedXml).points), locations(updated.points));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user