From 6180a04782acd1ca0e28b79bcdbce929f181986d Mon Sep 17 00:00:00 2001 From: Franzz Date: Mon, 28 Dec 2020 19:34:41 +0100 Subject: [PATCH] Gaia Updater fix (new Gaia GPS upload process) --- script/gaia_upload.js | 508 +++++++++++++++++++++++++++++++----------- 1 file changed, 374 insertions(+), 134 deletions(-) diff --git a/script/gaia_upload.js b/script/gaia_upload.js index 440dbfb..35c6fc0 100644 --- a/script/gaia_upload.js +++ b/script/gaia_upload.js @@ -1,11 +1,14 @@ // ==UserScript== -// @name GaiaGps Uploader v2 -// @version 2.0 -// @grant none -// @match https://www.gaiagps.com/upload/ -// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min.js +// @name GaiaGps Uploader +// @version 3.0 +// @grant none +// @match https://www.gaiagps.com/map/* +// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min.js +// @namespace https://greasyfork.org/users/583371 // ==/UserScript== +//Ctrl+Alt+Shift+I to open network tab on addon + var gpxParser = function () { this.xmlSource = ""; this.metadata = {}; @@ -151,7 +154,7 @@ gpxParser.prototype.parse = function (gpxstring) { gpxParser.prototype.getElementValue = function(parent, needle){ let elem = parent.querySelector(needle); if(elem != null){ - return elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data; + return $('
').html(elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data).text(); } return elem; } @@ -175,6 +178,117 @@ gpxParser.prototype.queryDirectSelector = function(parent, needle) { return finalElem; } +/* +gpxParser.prototype.toGeoJSON = function () { + var GeoJSON = { + "type": "FeatureCollection", + "features": [], + "properties": { + "name": this.metadata.name, + "desc": this.metadata.desc, + "time": this.metadata.time, + "author": this.metadata.author, + "link": this.metadata.link, + }, + }; + + for(idx in this.tracks) { + let track = this.tracks[idx]; + + var feature = { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [] + }, + "properties": { + } + }; + + feature.properties.name = track.name; + feature.properties.cmt = track.cmt; + feature.properties.desc = track.desc; + feature.properties.src = track.src; + feature.properties.number = track.number; + feature.properties.link = track.link; + feature.properties.type = track.type; + + for(idx in track.points) { + let pt = track.points[idx]; + + var geoPt = []; + geoPt.push(pt.lon); + geoPt.push(pt.lat); + geoPt.push(pt.ele); + + feature.geometry.coordinates.push(geoPt); + } + + GeoJSON.features.push(feature); + } + + for(idx in this.routes) { + let track = this.routes[idx]; + + var feature = { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [] + }, + "properties": { + } + }; + + feature.properties.name = track.name; + feature.properties.cmt = track.cmt; + feature.properties.desc = track.desc; + feature.properties.src = track.src; + feature.properties.number = track.number; + feature.properties.link = track.link; + feature.properties.type = track.type; + + + for(idx in track.points) { + let pt = track.points[idx]; + + var geoPt = []; + geoPt.push(pt.lon); + geoPt.push(pt.lat); + geoPt.push(pt.ele); + + feature.geometry.coordinates.push(geoPt); + } + + GeoJSON.features.push(feature); + } + + for(idx in this.waypoints) { + let pt = this.waypoints[idx]; + + var feature = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [] + }, + "properties": { + } + }; + + feature.properties.name = pt.name; + feature.properties.cmt = pt.cmt; + feature.properties.desc = pt.desc; + + feature.geometry.coordinates = [pt.lon, pt.lat, pt.ele]; + + GeoJSON.features.push(feature); + } + + return GeoJSON; +} +*/ + gpxParser.prototype.getGPX = function(sName) { var sTrack = ''; @@ -206,21 +320,30 @@ class Gaia { static get URL() { return 'https://www.gaiagps.com'; } static get API() { return Gaia.URL+'/api/objects'; } - constructor($Form) { + constructor($InputFile, $InputButton) { + this.asFiles = []; this.aoWaypoints = []; this.aoTracks = []; this.sFolderId = ''; + this.asFolder = {}; this.sFolderName = ''; + this.$Feedback = $('
', {'style':'position:fixed;width:100%;height:25%;bottom:0;background:white;z-index:10000;border-top:1px solid #CCC;box-sizing:border-box;font-size:1.2em;overflow:auto;padding:0 1em;'}); + this.$InputName = $('', {'type':'text', 'name':'folder_name', 'placeholder':'Folder Name', 'style': 'border-width:2px; border-color:rgba(0, 0, 0, 0.1); border-radius:4px; font-family:Inter,Helvetica Neue,Helvetica,Arial; font-size:15px; padding:8px 12px; margin-bottom:12px;'}); + + this.$InputBox = $InputButton.parent(); + + //Reboot object to remove events + this.$InputFile = $InputFile.clone().insertAfter($InputFile); + $InputFile.remove(); + + this.$InputButton = $InputButton.clone().insertAfter($InputButton); + $InputButton.remove(); - this.$Form = $Form; - this.$InputName = $Form.find('input[name=name]'); - this.$InputFile = $Form.find('input[type=file]'); - this.$Feedback = $Form.after($('
')); this.setLayout(); } feedback(sType, sMsg) { - var sFormattedMsg = sType.charAt(0).toUpperCase()+sType.slice(1)+': '+sMsg+'.'; + var sFormattedMsg = sType.charAt(0).toUpperCase()+sType.slice(1)+': '+sMsg+(sMsg.slice(-1)=='.'?'':'.'); console.log(sFormattedMsg); let sColor = 'black'; @@ -230,97 +353,29 @@ class Gaia { case 'info': sColor = 'green'; break; } this.$Feedback.append($('

', {'style': 'color: '+sColor+';'}).text(sFormattedMsg)); + + this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight")); } //Modify Gaia DOM Interface setLayout() { + //Add Feedback box + this.$Feedback.appendTo($('body')); + + //Add Event on button + this.$InputButton.click(() => {this.$InputFile.click();}); + //Set event on file selection this.$InputFile .attr('multiple', 'multiple') .attr('name', 'files[]') .change(() => {this.readInputFiles();}); - //Remove submit button & edit label - this.$Form.find('#fileuploadtext').text('Select files'); - this.$Form.find('button[type=submit]').hide(); - //Set default Name - this.$InputName.val('PCT'); + this.$InputName.val('PCT').prependTo(this.$InputBox); - //Clear all upload notifications - this.resetNotif(); - } - - //Parse files from input (multiple) - readInputFiles() { - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { - this.feedback('error', 'File APIs are not fully supported in this browser'); - return; - } - else this.feedback('info', 'Parsing input files'); - - //Get Folder Name (text input) - this.sFolderName = this.$InputName.val(); - - let aoReaders = []; - let asFiles = this.$InputFile.prop('files'); - let asContents = []; - for(var i=0 ; i < asFiles.length ; i++) { - aoReaders[i] = new FileReader(); - aoReaders[i].onload = ((asResult) => { - asContents.push(asResult.target.result); - this.feedback('info', 'Reading file '+asContents.length+'/'+asFiles.length); - if(asContents.length == asFiles.length) this.parseFile(asContents); - }); - aoReaders[i].readAsText(asFiles[i]); - } - } - - //Parse GPX files to consolidate tracks & waypoints - parseFile(asContents) { - this.feedback('info', 'Merging files'); - for(var i in asContents) { - var oGPX = new gpxParser(); - oGPX.parse(asContents[i]); - - //Waypoints - for(var w in oGPX.waypoints) { - var sWaypointName = oGPX.waypoints[w].name; - if(sWaypointName.indexOf('Milestone - ') != -1 && sWaypointName.substr(-2) != '00') { //1 milestone every 100 miles - this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"'); - } - else this.aoWaypoints.push(oGPX.waypoints[w]); - } - - //Tracks - for(var t in oGPX.tracks) { - this.aoTracks.push(oGPX.tracks[t]); - } - } - - this.deleteFolder(); - } - - //Delete existing folder with same name - deleteFolder() { - this.feedback('info', 'Looking for existing "'+this.sFolderName+'" folder...'); - - $.get(Gaia.API+'/folder/?routepoints=false&show_archived=true&show_filed=true').done((asFolders) => { - var bCalled = false; - for(var f in asFolders) { - if(asFolders[f].title == this.sFolderName) { - bCalled = true; - this.feedback('info', 'Deleting "'+this.sFolderName+'" folder'); - $.ajax({ - url: Gaia.API+'/folder/'+asFolders[f].id+'/', - type: 'DELETE' - }); - } - } - if(!bCalled) this.feedback('info', 'No folder named "'+this.sFolderName+'" found'); - - this.uploadTrack(); - }); + //Clear all upload notifications + this.resetNotif(); } //Marking all upload notifications as read @@ -335,8 +390,170 @@ class Gaia { }); } + //Parse files from input (multiple) + readInputFiles() { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + this.feedback('error', 'File APIs are not fully supported in this browser'); + return; + } + else this.feedback('info', 'Parsing input files'); + + //Get Folder Name (text input) + this.sFolderName = this.$InputName.val(); + let aoReaders = []; + let iCount = 0; + this.asFiles = this.$InputFile.prop('files'); + for(var i=0 ; i < this.asFiles.length ; i++) { + aoReaders[i] = new FileReader(); + aoReaders[i].onload = ((oFileReader) => { + return (asResult) => { + iCount++; + this.feedback('info', 'Reading file "'+oFileReader.name+'" ('+iCount+'/'+this.asFiles.length+')'); + this.parseFile(oFileReader.name, asResult.target.result); + if(iCount == this.asFiles.length) this.createFolder(); + }; + })(this.asFiles[i]); + aoReaders[i].readAsText(this.asFiles[i]); + } + } + + //Parse GPX files to consolidate tracks & waypoints + parseFile(sFileName, oContent) { + this.feedback('info', 'Parsing file "'+sFileName+'"'); + + var oGPX = new gpxParser(); + oGPX.parse(oContent); + + //Waypoints + for(var w in oGPX.waypoints) { + oGPX.waypoints[w].filename = sFileName; + var sWaypointName = oGPX.waypoints[w].name; + if(sWaypointName.indexOf('Milestone - ') != -1 && sWaypointName.substr(-2) != '00') { //1 milestone every 100 miles + this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"'); + } + else this.aoWaypoints.push(oGPX.waypoints[w]); + } + + //Tracks + for(var t in oGPX.tracks) { + oGPX.tracks[t].filename = sFileName; + this.aoTracks.push(oGPX.tracks[t]); + } + } + + //Delete existing folder with same name & recreating it + createFolder() { + this.feedback('info', 'Looking for existing folder "'+this.sFolderName+'"...'); + + $.get(Gaia.API+'/folder/?search='+this.sFolderName).done((asFolders) => { + if(asFolders != '' && !$.isEmptyObject(asFolders)) { + for(var f in asFolders) { + this.feedback('info', 'Deleting "'+asFolders[f].title+'" folder'); + $.ajax({ + url: Gaia.API+'/folder/'+asFolders[f].id+'/', + type: 'DELETE' + }); + } + } + else this.feedback('info', 'No folder named "'+this.sFolderName+'" found'); + + this.feedback('info', 'Creating folder "'+this.sFolderName+'"'); + $.post({ + url: Gaia.API+'/folder/', + contentType: 'application/json', + data: JSON.stringify({imported: true, title: this.sFolderName}) + }).done((asFolder) => { + this.feedback('info', 'Folder "'+asFolder.properties.name+'" created'); + this.asFolder = asFolder; + this.sFolderId = asFolder.id; + this.sFolderName = asFolder.properties.name; + this.uploadTrack(); + }); + }); + } + //Build & Upload Track File uploadTrack() { + this.feedback('info', 'Uploading tracks...'); + + //Convert to geojson + let iPostedTracks = 0; + $.each(this.aoTracks, (iIndex, aoTrack) => { + + //Set color + let sColor = '#4ABD32'; + switch(aoTrack.color) { + case 'DarkBlue': sColor = '#2D3FC7'; break; + case 'Magenta': sColor = '#B60DC3'; break; + } + + //Add track points + let aoCoords = []; + for(var p in aoTrack.points) { + let pt = aoTrack.points[p]; + aoCoords.push([pt.lon, pt.lat, pt.ele, 0]); + } + + //Upload + let sPostedData = JSON.stringify({ + type: 'feature', + properties: { + color: sColor, + title: aoTrack.name, + time_created: '2020-12-27T11:34:03.537Z', + routing_mode: null, + notes: aoTrack.desc, + //distance: 168405.62350073704, + isValid: true, + isLatestImport: true, + filename: aoTrack.filename, + localId: 'track'+(iIndex + 1), + writable: true, + archived: false, + isPublicTrack: false, + isLocallyCreated: true, + type: 'track', + parent_folder_id: this.sFolderId, + hexcolor: sColor + }, + geometry: { + type: 'MultiLineString', + coordinates: [aoCoords] + } + }); + + var self = this; + this.feedback('info', 'Uploading track "'+aoTrack.name+'"'); + $.post({ + url: Gaia.API+'/track/', + contentType: 'application/json', + data: sPostedData, + postedData: sPostedData, + trackName: aoTrack.name + }).done(function(asTrack) { + self.aoTracks[iIndex] = asTrack; + + $.ajax({ + url: Gaia.API+'/track/'+asTrack.features[0].id+'/', + type: 'PUT', + contentType: 'application/json', + data: this.postedData, + trackName: this.trackName + }).done(function() { + iPostedTracks++; + self.feedback('info', 'Track "'+this.trackName+'" uploaded'); + if(iPostedTracks == self.aoTracks.length) { + self.feedback('info', 'All tracks uploaded'); + self.uploadWayPoints(); + } + }).fail(function() { + self.feedback('error', 'Track "'+this.trackName+'" failed to upload'); + }); + }); + }); + + /* Legacy lethod: through form submit + //Build track file this.feedback('info', 'Building consolidated track'); var oGPX = new gpxParser(); @@ -358,8 +575,10 @@ class Gaia { contentType: false, cache: false }).done(() => {this.checkNotif();}); + */ } + /* //Wait for file to be processed by Gaia checkNotif() { this.feedback('info', 'Waiting for Gaia to process consolidated track'); @@ -424,6 +643,7 @@ class Gaia { } }); } + */ uploadWayPoints(iIndex, bSecondTry) { iIndex = iIndex || 0; @@ -433,7 +653,7 @@ class Gaia { if(iIndex < this.aoWaypoints.length) { var sWaypointName = this.aoWaypoints[iIndex].name; - this.feedback('info', 'Waypoints Upload - Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+sWaypointName+')'); + this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+sWaypointName+')'); var asPost = { type: 'Feature', geometry: { @@ -448,61 +668,77 @@ class Gaia { title: sWaypointName, time_created: "2020-06-07T14:01:03.944Z", icon: Gaia.getIconName(this.aoWaypoints[iIndex].sym), - writable: true, - localId: iIndex+'', - archived: false, - isTitleLoaded: true, - isLocallyCreated: true, - type: 'waypoint' + isValid: true, + isLatestImport: true, + filename: this.aoWaypoints[iIndex].filename, + writable: true, + localId: iIndex+'', + notes: this.aoWaypoints[iIndex].desc, + archived: false, + isLocallyCreated: true, + type: 'waypoint', + parent_folder_id: this.sFolderId } }; - if(this.aoWaypoints[iIndex].desc) asPost.properties.notes = this.aoWaypoints[iIndex].desc; - $.ajax({ + let sData = JSON.stringify(asPost); + var self = this; + $.post({ url: Gaia.API+'/waypoint/', - type: 'POST', contentType: 'application/json', - data: JSON.stringify(asPost) - }).done((asWaypoint) => { + data: sData, + postedData: sData + }).done(function(asWaypoint) { //Update local waypoint with all server info (including ID) - this.aoWaypoints[iIndex] = asWaypoint; + self.aoWaypoints[iIndex] = asWaypoint; - $.get(Gaia.URL+'/public/generate?type=waypoint&objectid='+asWaypoint.properties.id).always(() => { - this.uploadWayPoints(++iIndex); + $.ajax({ + url: Gaia.API+'/waypoint/'+asWaypoint.properties.id+'/', + type: 'PUT', + contentType: 'application/json', + data: this.postedData + }).always(() => { + self.uploadWayPoints(++iIndex); }); }).fail(function(){ - this.feedback('error', 'Waypoints Upload - Failed to upload waypoint #'+(iIndex + 1)+' ('+sWaypointName+'). Trying again...'); - this.uploadWayPoints(iIndex, true); + self.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' ('+sWaypointName+'). Trying again...'); + self.uploadWayPoints(iIndex, true); }); } - //Done uploading, assigning waypoints to folder - else { - this.feedback('info', 'Waypoints Upload - Getting folders list'); - $.get(Gaia.API+'/folder/').done((asFolders) => { - //Find Folder - for(var f in asFolders) { - if(asFolders[f].id == this.sFolderId) { - var asFolder = asFolders[f]; - } - } + //Done uploading, assigning waypoints & tracks to folder + else this.assignElementsToFolder(); + } - //Assign waypoints to folder - for(var i in this.aoWaypoints) asFolder.waypoints.push(this.aoWaypoints[i].properties.id); + assignElementsToFolder() { + this.feedback('info', 'Assigning all elements to folder "'+this.asFolder.properties.name+'"'); - this.feedback('info', 'Waypoints Upload - Assigning waypoints to folder '+this.sFolderId); - $.ajax({ - url: Gaia.API+'/folder/'+this.sFolderId+'/', - type: 'PUT', - contentType: 'application/json', - data: JSON.stringify(asFolder) - }).done(() => { - this.feedback('info', 'Waypoints Upload - Finished successfully'); - }).fail(() => { - this.feedback('error', 'Waypoints Upload - Failed to assign waypoints to folder "'+sFolderId+'"'); - }); - }); + let asFolder = { + cover_photo_id: this.asFolder.properties.cover_photo_id, + id: this.asFolder.id, + name: this.asFolder.properties.name, + notes: this.asFolder.properties.notes, + time_created: this.asFolder.properties.time_created, + updated_date: this.asFolder.properties.updated_date } + //Assign waypoints to folder + asFolder.waypoints = []; + for(var w in this.aoWaypoints) asFolder.waypoints.push(this.aoWaypoints[w].properties.id); + + //Assign tracks to folder + asFolder.tracks = []; + for(var t in this.aoTracks) asFolder.tracks.push(this.aoTracks[t].features[0].id); + + $.ajax({ + url: Gaia.API+'/folder/'+asFolder.id+'/', + type: 'PUT', + contentType: 'application/json', + data: JSON.stringify(asFolder) + }).done(() => { + this.feedback('info', 'Done'); + }).fail(() => { + this.feedback('error', 'WFailed to assign waypoints & tracks to folder ID "'+asFolder.id+'"'); + }); } static getIconName(sGarminName) { @@ -540,6 +776,10 @@ class Gaia { } } -console.log('computes'); +console.log('Loading GaiaGps Uploader...'); -let oGaia = new Gaia($('#uploadForm')); +//To be adjusted regularly +$FileInput = $('input[type=file]') +$FileButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button'); + +let oGaia = new Gaia($FileInput, $FileButton);