UNPKG

@sports-alliance/sports-lib

Version:

A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc

122 lines (121 loc) 6.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const creator_1 = require("../creators/creator"); const route_1 = require("./route"); const route_file_1 = require("./route-file"); const route_preview_utilities_1 = require("./route-preview.utilities"); describe('RoutePreviewUtilities', () => { it('encodes and decodes standard Google polyline5 fixtures', () => { const points = [ { latitudeDegrees: 38.5, longitudeDegrees: -120.2 }, { latitudeDegrees: 40.7, longitudeDegrees: -120.95 }, { latitudeDegrees: 43.252, longitudeDegrees: -126.453 } ]; const encoded = (0, route_preview_utilities_1.encodeRoutePolyline5)(points); expect(encoded).toBe('_p~iF~ps|U_ulLnnqC_mqNvxq`@'); expect((0, route_preview_utilities_1.decodeRoutePolyline5)(encoded)).toEqual(points); }); it('simplifies dense geometry while preserving endpoints', () => { const points = buildSineRoutePoints(200); const simplified = route_preview_utilities_1.RoutePreviewUtilities.simplifyPolyline(points, { maxPoints: 20 }); expect(simplified).toHaveLength(20); expect(simplified[0]).toEqual(points[0]); expect(simplified[simplified.length - 1]).toEqual(points[points.length - 1]); }); it('drops invalid and zero coordinates before simplifying', () => { const simplified = route_preview_utilities_1.RoutePreviewUtilities.simplifyPolyline([ { latitudeDegrees: 0, longitudeDegrees: 0 }, { latitudeDegrees: Number.NaN, longitudeDegrees: 1 }, { latitudeDegrees: 39.1, longitudeDegrees: 20.1 }, { latitudeDegrees: 39.2, longitudeDegrees: 20.2 }, { latitudeDegrees: 95, longitudeDegrees: 20.3 }, { latitudeDegrees: 39.3, longitudeDegrees: 20.3 } ], { maxPoints: 10 }); expect(simplified).toEqual([ { latitudeDegrees: 39.1, longitudeDegrees: 20.1 }, { latitudeDegrees: 39.2, longitudeDegrees: 20.2 }, { latitudeDegrees: 39.3, longitudeDegrees: 20.3 } ]); }); it('builds multi-segment route previews with capped point counts and bounds', () => { var _a, _b; const firstRoute = createRoute('route-a', 'First', buildSineRoutePoints(500, 39, 20)); const secondRoute = createRoute('route-b', 'Second', buildSineRoutePoints(500, 40, 21)); const routeFile = new route_file_1.RouteFile('Routes', undefined, new creator_1.Creator('test'), [firstRoute, secondRoute]); const preview = route_preview_utilities_1.RoutePreviewUtilities.buildRouteFilePreview(routeFile, { maxPointsPerSegment: 100, maxPointsPerRoute: 150 }); expect(preview).toMatchObject({ version: 1, encoding: 'polyline5', precision: 5, sourcePointCount: 1000, pointCount: 150 }); expect(preview === null || preview === void 0 ? void 0 : preview.segments).toHaveLength(2); expect(preview === null || preview === void 0 ? void 0 : preview.segments.map(segment => segment.pointCount)).toEqual([75, 75]); expect(preview === null || preview === void 0 ? void 0 : preview.segments[0]).toMatchObject({ id: 'route-a', name: 'First', sourcePointCount: 500 }); expect((_a = preview === null || preview === void 0 ? void 0 : preview.segments[0].bounds) === null || _a === void 0 ? void 0 : _a.minLatitudeDegrees).toBeGreaterThan(38.9); expect((_b = preview === null || preview === void 0 ? void 0 : preview.bounds) === null || _b === void 0 ? void 0 : _b.minLongitudeDegrees).toBeLessThan(20.1); expect((0, route_preview_utilities_1.decodeRoutePolyline5)(preview === null || preview === void 0 ? void 0 : preview.segments[0].encodedPolyline)).toHaveLength(75); }); it('returns null when no route segment has at least two valid coordinates', () => { const route = createRoute('route-a', 'First', [ { latitudeDegrees: 0, longitudeDegrees: 0 }, { latitudeDegrees: Number.NaN, longitudeDegrees: 20 } ]); const routeFile = new route_file_1.RouteFile('Routes', undefined, new creator_1.Creator('test'), [route]); expect(route_preview_utilities_1.RoutePreviewUtilities.buildRouteFilePreview(routeFile)).toBeNull(); }); it('simplifies generic coordinate pairs while preserving coordinate order', () => { const coordinates = Array.from({ length: 200 }, (_value, index) => [ 20 + (Math.sin(index / 12) * 0.05), 39 + (index * 0.001) ]); const result = (0, route_preview_utilities_1.simplifyCoordinatePairsVisvalingamWhyatt)(coordinates, { maxPoints: 20, minInputPoints: 20, minPointsToKeep: 8 }); expect(result).toMatchObject({ inputPointCount: 200, outputPointCount: 20, simplified: true }); expect(result.coordinates[0]).toEqual(coordinates[0]); expect(result.coordinates[result.coordinates.length - 1]).toEqual(coordinates[coordinates.length - 1]); }); it('keeps generic coordinate pairs unchanged below the minimum input threshold', () => { const coordinates = [ [20, 39], [20.1, 39.1], [20.2, 39.2] ]; const result = (0, route_preview_utilities_1.simplifyCoordinatePairsVisvalingamWhyatt)(coordinates, { maxPoints: 2, minInputPoints: 10 }); expect(result).toEqual({ coordinates, inputPointCount: 3, outputPointCount: 3, simplified: false }); }); function createRoute(id, name, points) { const route = new route_1.Route(new creator_1.Creator('test'), undefined, name, null, points); route.setID(id); return route; } function buildSineRoutePoints(totalPoints, latitudeStart = 39, longitudeStart = 20) { return Array.from({ length: totalPoints }, (_value, index) => ({ latitudeDegrees: latitudeStart + (index * 0.001), longitudeDegrees: longitudeStart + (Math.sin(index / 12) * 0.05) })); } });