| 
					
				 | 
			
			
				@@ -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(); 
			 |