@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
133 lines (132 loc) • 7.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RouteImporterJSON = void 0;
const activity_types_1 = require("../../../../activities/activity.types");
const creator_1 = require("../../../../creators/creator");
const data_altitude_1 = require("../../../../data/data.altitude");
const data_latitude_degrees_1 = require("../../../../data/data.latitude-degrees");
const data_longitude_degrees_1 = require("../../../../data/data.longitude-degrees");
const data_store_1 = require("../../../../data/data.store");
const importer_json_1 = require("../../../../events/adapters/importers/json/importer.json");
const parsing_event_lib_error_1 = require("../../../../errors/parsing-event-lib.error");
const route_1 = require("../../../route");
const route_file_1 = require("../../../route-file");
const route_file_utilities_1 = require("../../../route-file.utilities");
const route_stream_1 = require("../../../route-stream");
class RouteImporterJSON {
static getRouteFileFromJSON(json) {
const creator = json.creator ? importer_json_1.EventImporterJSON.getCreatorFromJSON(json.creator) : new creator_1.Creator('Unknown Device');
const routeFile = new route_file_1.RouteFile(json.name, json.srcFileType, creator, [], json.waypoints || [], json.createdAt !== null && json.createdAt !== undefined ? new Date(json.createdAt) : null);
if (json.id) {
routeFile.setID(json.id);
}
(json.routes || []).forEach(routeJSON => {
routeFile.addRoute(this.getRouteFromJSON(routeJSON, creator));
});
if (this.hasStats(json.stats)) {
this.addStatsFromJSON(json.stats, routeFile);
}
else {
route_file_utilities_1.RouteFileUtilities.reGenerateStatsForRouteFile(routeFile);
}
return routeFile;
}
static getRouteFromJSON(json, routeFileCreator) {
var _a;
const creator = json.creator
? importer_json_1.EventImporterJSON.getCreatorFromJSON(json.creator)
: routeFileCreator || new creator_1.Creator('Unknown Device');
const streams = this.getStreamsFromJSON(json.streams || []);
const points = this.getValidatedPoints(json.points || [], streams);
this.addMissingGeometryStreamsFromPoints(points, streams);
const route = new route_1.Route(creator, undefined, json.name || null, activity_types_1.ActivityTypesHelper.resolveActivityType(json.activityType) || null, points, {
comment: json.comment || null,
description: json.description || null,
number: (_a = json.number) !== null && _a !== void 0 ? _a : null,
links: json.links || [],
extensions: json.extensions
});
if (json.id) {
route.setID(json.id);
}
this.addStatsFromJSON(json.stats, route);
streams.forEach(stream => route.addStream(stream));
return route;
}
static addStatsFromJSON(jsonStats, target) {
Object.keys(jsonStats || {}).forEach(statName => {
target.addStat(data_store_1.DynamicDataLoader.getDataInstanceFromDataType(statName, (jsonStats || {})[statName]));
});
}
static hasStats(jsonStats) {
return !!jsonStats && Object.keys(jsonStats).length > 0;
}
static getStreamsFromJSON(streamsJSON) {
if (Array.isArray(streamsJSON)) {
return streamsJSON.map(streamJson => new route_stream_1.RouteStream(streamJson.type, streamJson.data));
}
return Object.keys(streamsJSON || {}).map(streamKey => new route_stream_1.RouteStream(streamKey, streamsJSON[streamKey]));
}
static getValidatedPoints(points, streams) {
if (!points.length) {
return points;
}
const streamsByType = new Map(streams.map(stream => [stream.type, stream]));
const latitudeStream = streamsByType.get(data_latitude_degrees_1.DataLatitudeDegrees.type);
const longitudeStream = streamsByType.get(data_longitude_degrees_1.DataLongitudeDegrees.type);
const altitudeStream = streamsByType.get(data_altitude_1.DataAltitude.type);
this.validateGeometryStream(points, latitudeStream, point => point.latitudeDegrees, data_latitude_degrees_1.DataLatitudeDegrees.type, true);
this.validateGeometryStream(points, longitudeStream, point => point.longitudeDegrees, data_longitude_degrees_1.DataLongitudeDegrees.type, true);
this.validateGeometryStream(points, altitudeStream, point => point.altitude, data_altitude_1.DataAltitude.type, false);
return points.map((point, index) => (Object.assign(Object.assign({}, point), { altitude: point.altitude !== undefined
? point.altitude
: Number.isFinite(altitudeStream === null || altitudeStream === void 0 ? void 0 : altitudeStream.getData()[index])
? altitudeStream.getData()[index]
: null })));
}
static validateGeometryStream(points, stream, getPointValue, streamType, requiredPointValue) {
points.forEach((point, index) => {
const pointValue = getPointValue(point);
if (requiredPointValue && !Number.isFinite(pointValue)) {
throw new parsing_event_lib_error_1.ParsingEventLibError(`Invalid route point ${streamType} at index ${index}`);
}
});
if (!stream) {
return;
}
const streamData = stream.getData();
if (streamData.length !== points.length) {
throw new parsing_event_lib_error_1.ParsingEventLibError(`Route JSON geometry conflict: ${streamType} stream length ${streamData.length} does not match points length ${points.length}`);
}
streamData.forEach((streamValue, index) => {
const pointValue = getPointValue(points[index]);
if (!Number.isFinite(pointValue) && !Number.isFinite(streamValue)) {
return;
}
if (!Number.isFinite(pointValue) && Number.isFinite(streamValue)) {
return;
}
if (!Number.isFinite(pointValue) ||
!Number.isFinite(streamValue) ||
Math.abs(pointValue - streamValue) > 1e-9) {
throw new parsing_event_lib_error_1.ParsingEventLibError(`Route JSON geometry conflict: ${streamType} mismatch at index ${index}`);
}
});
}
static addMissingGeometryStreamsFromPoints(points, streams) {
if (!points.length) {
return;
}
const existingStreamTypes = new Set(streams.map(stream => stream.type));
if (!existingStreamTypes.has(data_latitude_degrees_1.DataLatitudeDegrees.type)) {
streams.push(new route_stream_1.RouteStream(data_latitude_degrees_1.DataLatitudeDegrees.type, points.map(point => point.latitudeDegrees)));
}
if (!existingStreamTypes.has(data_longitude_degrees_1.DataLongitudeDegrees.type)) {
streams.push(new route_stream_1.RouteStream(data_longitude_degrees_1.DataLongitudeDegrees.type, points.map(point => point.longitudeDegrees)));
}
if (!existingStreamTypes.has(data_altitude_1.DataAltitude.type) && points.some(point => Number.isFinite(point.altitude))) {
streams.push(new route_stream_1.RouteStream(data_altitude_1.DataAltitude.type, points.map(point => (Number.isFinite(point.altitude) ? point.altitude : null))));
}
}
}
exports.RouteImporterJSON = RouteImporterJSON;