|
@@ -0,0 +1,195 @@
|
|
|
+/* 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();
|