@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
JavaScript
"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)
}));
}
});