/* global process */ /* * A nodejs app that puts in time order gps points exported from PhoneTrack in NextCloud * so that they can be correctly imported in JOSM * * author: Florian ANAYA * date: 2023/08/31 */ const fileSystemLib = require('fs'); const xmlJsLib = require('xml-js'); // @type String The path of the file to read var filePath; // @type Array[Track] List of tracks read from the file const tracks = []; // @type Date The starting time of the script so that we can calculate the ellapsed time at the end of the script var startTime; function main() { startTime = new Date(); console.log("Started PhoneTrack GPX file fixer"); if (process.argv.length <= 2) { console.log("Please specify file path"); return; } filePath = process.argv[2]; console.log("Reading file located at \"" + filePath + "\"..."); // We read the file fileSystemLib.readFile(filePath, onFileRead); } function onFileRead(error, fileData) { if (error) { throw error; } // We read XML dazta from the file (this will throw if the file is not XML) console.log("File has been read, loading XML..."); let xmlData = xmlJsLib.xml2js(fileData); console.log("XML has been loaded, reading PhoneTrack data..."); let rootElements = xmlData.elements; let gpxData = rootElements[0]; let gpxElements = gpxData.elements; for (let index in gpxElements) { let gpxChildData = gpxElements[index]; if (gpxChildData.name === "trk") { loadTrack(gpxChildData); } } sortTracks(); updateXml(xmlData); } function loadTrack(trackData) { let track = new Track(trackData); tracks.push(track); } function sortTracks() { console.log("Sorting all points..."); for (let index in tracks) { let track = tracks[index]; track.sort(); } console.log("Done sorting all points"); }; function updateXml(xmlData) { console.log("Updating XML..."); for (let index in tracks) { let track = tracks[index]; track.sortXml(); } console.log("Done updating XML"); let newFilePath = filePath + "2"; console.log("Writing to new file \"" + newFilePath + "\""); let xmlBack = xmlJsLib.js2xml(xmlData, {spaces: 2, indentCdata: true}); fileSystemLib.writeFile(newFilePath, xmlBack, function(error, data) { if (error) { throw error; } console.log("Data written to file"); let endTime = new Date(); let timeDiff = (endTime - startTime) / 1000; //in sec console.log("Done in " + timeDiff + " sec"); }); }; function Track(trackData) { this.m_points = []; let elementsData = trackData.elements; for (let index in elementsData) { let elementData = elementsData[index]; if (elementData.name === "name") { this.m_trackName = elementData.elements[0].text; } else if (elementData.name === "trkseg") { this.m_pointsData = elementData.elements; } } console.log("Loading track \"" + this.m_trackName + "\" with " + this.m_pointsData.length + " points"); for (let index in this.m_pointsData) { let pointData = this.m_pointsData[index]; let point = new Point(pointData); this.m_points.push(point); } console.log("Done loading track \"" + this.m_trackName + "\""); } Track.prototype.m_name; // @type Array[Point] Track.prototype.m_points; Track.prototype.sort = function() { console.log("Sorting points..."); this.m_points.sort(function(pointA, pointB) { return pointA.getTimestamp() - pointB.getTimestamp(); }); console.log("Done reading and sorting track \"" + this.m_trackName + "\""); }; Track.prototype.sortXml = function() { // We empty the original array of points data (we don't use this.m_pointsData=[] because we want to update the original array instead of creating a new one) this.m_pointsData.splice(0, this.m_pointsData.length); for (let index in this.m_points) { let point = this.m_points[index]; this.m_pointsData.push(point.getData()); } }; function Point(pointData) { this.m_data = pointData; this.m_timestamp = new Date(this.m_data.elements[0].elements[0].text).getTime(); } Point.prototype.m_data; Point.prototype.m_timestamp; Point.prototype.getData = function() { return this.m_data; }; Point.prototype.getTimestamp = function() { return this.m_timestamp; }; // Execution of the script main();