You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.6 KiB
123 lines
3.6 KiB
/** @module gpx */
|
|
/// <reference path="result.js" />
|
|
|
|
/**
|
|
* @typedef {[String, String, String]} Segment
|
|
* A tuple of latitude, longtitude and time.
|
|
*/
|
|
|
|
import { parseXml } from "@rgrove/parse-xml";
|
|
import { ERROR, OK } from "./result.js";
|
|
|
|
/**
|
|
* Checks if the given JSON object is an element. Utility function for working
|
|
* with JsonObject converted from XML.
|
|
*
|
|
* @param {JsonObject} object object to check.
|
|
* @returns {boolean} True if the object.type is an element, false otherwise.
|
|
*/
|
|
const isElement = (object) => object?.type === "element";
|
|
|
|
/**
|
|
* Checks if the given JSON object is an GPS track object.
|
|
* Utility function for working with JsonObject converted from XML.
|
|
*
|
|
* @param {JsonObject} object to check.
|
|
* @returns {boolean} True if the object.name is trk, false otherwise.
|
|
*/
|
|
const isTrack = (object) => object?.name === "trk";
|
|
|
|
/**
|
|
* Checks if the given JSON object is an GPS track segment object.
|
|
* Utility function for working with JsonObject converted from XML.
|
|
*
|
|
* @param {JsonObject} object to check.
|
|
* @returns {boolean} True if the object.name is trkseg, false otherwise.
|
|
*/
|
|
const isTrackSegment = (object) => object?.name === "trkseg";
|
|
|
|
/**
|
|
* Checks if the given JSON object is an GPS track segment time object.
|
|
* Utility function for working with JsonObject converted from XML.
|
|
*
|
|
* @param {JsonObject} object to check.
|
|
* @returns {boolean} True if the object.name is time, false otherwise.
|
|
*/
|
|
const isSegmentTime = (object) => object?.name === "time";
|
|
|
|
/**
|
|
* Checks if the given JSON object have all nesessary data.
|
|
* Utility function for working with JsonObject converted from XML.
|
|
*
|
|
* @param {JsonObject} object to check.
|
|
* @returns {boolean} True if the object have latitude longtitude and time.
|
|
*/
|
|
const isProperSegment = (object) => {
|
|
const haveLat = object?.attributes?.lat !== undefined;
|
|
const haveLon = object?.attributes?.lon !== undefined;
|
|
const children = object?.children ?? [];
|
|
const timeObject = children.filter(isSegmentTime)?.[0];
|
|
const haveTime =
|
|
timeObject !== undefined && timeObject.children[0].text.length > 0;
|
|
|
|
return haveLat && haveLon && haveTime;
|
|
};
|
|
|
|
/**
|
|
* Take longtitude, latitude and time from JSON object
|
|
* and return it as Segment tuple.
|
|
*
|
|
* @param {JsonObject} object to check.
|
|
* @returns {Segment} True if the object have latitude longtitude and time.
|
|
*/
|
|
const getSegmentData = (object) => {
|
|
const segmentTime = object.children.filter(isSegmentTime)[0].children[0].text;
|
|
return [object.attributes.lat, object.attributes.lon, segmentTime];
|
|
};
|
|
|
|
/**
|
|
* Parse XML document, turn it into JSON and find track segments,
|
|
* then transform every object to Segment tuple
|
|
*
|
|
* @param {String} xmlDoc string representation of XML document.
|
|
* @returns {Result<[Segment]>}
|
|
*/
|
|
function parse(xmlDoc) {
|
|
// Check input argument
|
|
if (!xmlDoc || xmlDoc.length === 0) {
|
|
return [ERROR, "Can not parse empty document"];
|
|
}
|
|
|
|
const gpxObject = parseXml(xmlDoc);
|
|
const root = gpxObject.toJSON().children?.[0];
|
|
|
|
if (!root) {
|
|
return [ERROR, "Document doesn't contain root element"];
|
|
}
|
|
|
|
const track = root.children.filter(isElement).filter(isTrack)?.[0];
|
|
|
|
if (!track) {
|
|
return [ERROR, "Document doesn't contain track element"];
|
|
}
|
|
|
|
const segments = track.children
|
|
.filter(isElement)
|
|
.filter(isTrackSegment)
|
|
.reduce(
|
|
(seg1, seg2) => [...(seg1?.children ?? []), ...(seg2?.children ?? [])],
|
|
[],
|
|
)
|
|
.filter(isElement)
|
|
.filter(isProperSegment)
|
|
.map(getSegmentData);
|
|
|
|
if (segments.length === 0) {
|
|
return [ERROR, "There are no track segments to process"];
|
|
}
|
|
|
|
return [OK, segments];
|
|
}
|
|
|
|
export { parse };
|