| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- const assert = require('assert');
- const { parseAndCompress } = require('../src/utils/gpx-processor');
- function makeGPX(points, name = 'Test Track') {
- const trkpts = points.map(p => {
- const timeTag = p.time ? `<time>${p.time}</time>` : '';
- const eleTag = p.ele != null ? `<ele>${p.ele}</ele>` : '';
- return `<trkpt lat="${p.lat}" lon="${p.lon}">${eleTag}${timeTag}</trkpt>`;
- }).join('\n');
- return `<?xml version="1.0"?>
- <gpx version="1.1">
- <trk>
- <name>${name}</name>
- <trkseg>
- ${trkpts}
- </trkseg>
- </trk>
- </gpx>`;
- }
- describe('GPX processor', () => {
- describe('parseAndCompress', () => {
- it('parses a minimal GPX and returns correct structure', async () => {
- const gpx = makeGPX([
- { lat: 51.5, lon: -0.1, ele: 10, time: '2024-01-01T10:00:00Z' },
- { lat: 51.501, lon: -0.1, ele: 11, time: '2024-01-01T10:01:00Z' },
- ], 'My Track');
- const result = await parseAndCompress(gpx);
- assert.strictEqual(result.trackName, 'My Track');
- assert.ok(result.segments.length > 0);
- assert.ok(result.pointCount >= 2);
- assert.ok(result.totalDistance > 0);
- });
- it('sets trackDate from first point timestamp', async () => {
- const gpx = makeGPX([
- { lat: 51.5, lon: -0.1, time: '2024-06-15T08:30:00Z' },
- { lat: 51.51, lon: -0.1, time: '2024-06-15T09:00:00Z' },
- ]);
- const result = await parseAndCompress(gpx);
- assert.ok(result.trackDate instanceof Date);
- assert.strictEqual(result.trackDate.getFullYear(), 2024);
- assert.strictEqual(result.trackDate.getMonth(), 5); // June = 5 (0-indexed)
- });
- it('skips points closer than 2 m', async () => {
- // Points almost on top of each other (< 2m apart)
- const gpx = makeGPX([
- { lat: 51.5000000, lon: -0.1, time: '2024-01-01T10:00:00Z' },
- { lat: 51.5000001, lon: -0.1, time: '2024-01-01T10:00:30Z' }, // ~1 cm away
- { lat: 51.5000002, lon: -0.1, time: '2024-01-01T10:01:00Z' }, // still < 2m from first
- { lat: 51.5010000, lon: -0.1, time: '2024-01-01T10:01:30Z' }, // ~111 m away
- ]);
- const result = await parseAndCompress(gpx);
- // Should keep first, skip tiny points, keep last far point
- assert.ok(result.pointCount < 4, `Expected fewer than 4 points, got ${result.pointCount}`);
- assert.ok(result.pointCount >= 2);
- });
- it('skips points with < 30s gap (no sharp turn)', async () => {
- // Points 10s apart in a straight line - should be skipped
- const gpx = makeGPX([
- { lat: 51.500, lon: -0.100, time: '2024-01-01T10:00:00Z' },
- { lat: 51.501, lon: -0.100, time: '2024-01-01T10:00:10Z' }, // 10s gap, straight line
- { lat: 51.502, lon: -0.100, time: '2024-01-01T10:00:20Z' }, // 10s gap, straight line
- { lat: 51.503, lon: -0.100, time: '2024-01-01T10:01:00Z' }, // 40s gap, should keep
- ]);
- const result = await parseAndCompress(gpx);
- // Middle points should be mostly skipped due to < 30s and no sharp turn
- assert.ok(result.pointCount <= 3, `Expected ≤3 points, got ${result.pointCount}`);
- });
- it('keeps points with < 30s gap if sharp turn', async () => {
- // U-turn: going north then suddenly south
- const gpx = makeGPX([
- { lat: 51.500, lon: -0.100, time: '2024-01-01T10:00:00Z' },
- { lat: 51.510, lon: -0.100, time: '2024-01-01T10:00:10Z' }, // 10s, going north
- { lat: 51.500, lon: -0.100, time: '2024-01-01T10:00:20Z' }, // 10s, sharp turn back south
- { lat: 51.490, lon: -0.100, time: '2024-01-01T10:01:00Z' }, // continuing south
- ]);
- const result = await parseAndCompress(gpx);
- // The middle point (apex of U-turn) should be kept
- assert.ok(result.pointCount >= 3, `Expected ≥3 points (turn apex kept), got ${result.pointCount}`);
- });
- it('keeps points at >= 30s intervals regardless of distance', async () => {
- const gpx = makeGPX([
- { lat: 51.500, lon: -0.100, time: '2024-01-01T10:00:00Z' },
- { lat: 51.501, lon: -0.100, time: '2024-01-01T10:00:30Z' }, // exactly 30s
- { lat: 51.502, lon: -0.100, time: '2024-01-01T10:01:00Z' }, // 30s later
- ]);
- const result = await parseAndCompress(gpx);
- assert.strictEqual(result.pointCount, 3);
- });
- it('handles GPX with no timestamps', async () => {
- const gpx = makeGPX([
- { lat: 51.500, lon: -0.100 },
- { lat: 51.510, lon: -0.100 },
- { lat: 51.520, lon: -0.100 },
- ]);
- const result = await parseAndCompress(gpx);
- assert.ok(result.pointCount >= 2);
- assert.strictEqual(result.trackDate, null);
- });
- it('calculates total distance correctly', async () => {
- // Two points ~111m apart (0.001 degree lat)
- const gpx = makeGPX([
- { lat: 51.5000, lon: 0, time: '2024-01-01T10:00:00Z' },
- { lat: 51.5010, lon: 0, time: '2024-01-01T10:01:00Z' },
- ]);
- const result = await parseAndCompress(gpx);
- assert.ok(result.totalDistance > 100 && result.totalDistance < 120,
- `Expected ~111 m, got ${result.totalDistance.toFixed(1)} m`);
- });
- it('handles multiple segments', async () => {
- const xml = `<?xml version="1.0"?>
- <gpx version="1.1">
- <trk>
- <name>Multi Segment</name>
- <trkseg>
- <trkpt lat="51.500" lon="0.000"><time>2024-01-01T10:00:00Z</time></trkpt>
- <trkpt lat="51.510" lon="0.000"><time>2024-01-01T10:01:00Z</time></trkpt>
- </trkseg>
- <trkseg>
- <trkpt lat="52.000" lon="0.000"><time>2024-01-01T11:00:00Z</time></trkpt>
- <trkpt lat="52.010" lon="0.000"><time>2024-01-01T11:01:00Z</time></trkpt>
- </trkseg>
- </trk>
- </gpx>`;
- const result = await parseAndCompress(xml);
- assert.strictEqual(result.segments.length, 2);
- assert.ok(result.pointCount >= 4);
- });
- });
- });
|