From 840f4daf72c70ac3d32ca217b89889a8672de03f Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sun, 27 Sep 2020 19:20:10 -0700 Subject: [PATCH 01/20] Use Heightgraph in lieu of Elevation --- js/index.js | 5 +- js/plugin/Elevation.js | 116 -------------- js/plugin/Heightgraph.js | 329 +++++++++++++++++++++++++++++++++++++++ package.json | 10 +- yarn.lock | 11 +- 5 files changed, 345 insertions(+), 126 deletions(-) delete mode 100644 js/plugin/Elevation.js create mode 100644 js/plugin/Heightgraph.js diff --git a/js/index.js b/js/index.js index 476e588..fbb0997 100644 --- a/js/index.js +++ b/js/index.js @@ -198,7 +198,10 @@ } else { stats = new BR.TrackStats(); } - elevation = new BR.Elevation(); + + // remove the old dep + // elevation = new BR.Elevation(); + elevation = new BR.Heightgraph(); profile = new BR.Profile(); profile.on('update', function(evt) { diff --git a/js/plugin/Elevation.js b/js/plugin/Elevation.js deleted file mode 100644 index cc10843..0000000 --- a/js/plugin/Elevation.js +++ /dev/null @@ -1,116 +0,0 @@ -BR.Elevation = L.Control.Elevation.extend({ - options: { - width: $('#map').outerWidth(), - margins: { - top: 20, - right: 30, - bottom: 30, - left: 60 - }, - theme: 'steelblue-theme', - shortcut: { - toggle: 69 // char code for 'e' - } - }, - - onAdd: function(map) { - var container = L.Control.Elevation.prototype.onAdd.call(this, map); - - // revert registering touch events when touch screen detection is available and negative - // see https://github.com/MrMufflon/Leaflet.Elevation/issues/67 - if (L.Browser.touch && BR.Browser.touchScreenDetectable && !BR.Browser.touchScreen) { - this._background - .on('touchmove.drag', null) - .on('touchstart.drag', null) - .on('touchstart.focus', null); - L.DomEvent.off(this._container, 'touchend', this._dragEndHandler, this); - - this._background - .on('mousemove.focus', this._mousemoveHandler.bind(this)) - .on('mouseout.focus', this._mouseoutHandler.bind(this)) - .on('mousedown.drag', this._dragStartHandler.bind(this)) - .on('mousemove.drag', this._dragHandler.bind(this)); - L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, this); - } - - L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); - - return container; - }, - - addBelow: function(map) { - // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 - // this.width($('#map').outerWidth()); - this.options.width = $('#content').outerWidth(); - - if (this.getContainer() != null) { - this.remove(map); - } - - function setParent(el, newParent) { - newParent.appendChild(el); - } - this.addTo(map); - // move elevation graph outside of the map - setParent(this.getContainer(), document.getElementById('elevation-chart')); - }, - - initCollapse: function(map) { - var self = this; - var onHide = function() { - $('#elevation-btn').removeClass('active'); - // we must fetch tiles that are located behind elevation-chart - map._onResize(); - - if (this.id && BR.Util.localStorageAvailable() && !self.shouldRestoreChart) { - localStorage.removeItem(this.id); - } - }; - var onShow = function() { - $('#elevation-btn').addClass('active'); - - if (this.id && BR.Util.localStorageAvailable()) { - localStorage[this.id] = 'true'; - } - }; - // on page load, we want to restore collapse state from previous usage - $('#elevation-chart') - .on('hidden.bs.collapse', onHide) - .on('shown.bs.collapse', onShow) - .each(function() { - if (this.id && BR.Util.localStorageAvailable() && localStorage[this.id] === 'true') { - self.shouldRestoreChart = true; - } - }); - }, - - update: function(track, layer) { - this.clear(); - - // bring height indicator to front, because of track casing in BR.Routing - if (this._mouseHeightFocus) { - var g = this._mouseHeightFocus[0][0].parentNode; - g.parentNode.appendChild(g); - } - - if (track && track.getLatLngs().length > 0) { - if (this.shouldRestoreChart === true) $('#elevation-chart').collapse('show'); - this.shouldRestoreChart = undefined; - - this.addData(track.toGeoJSON(), layer); - - layer.on('mouseout', this._hidePositionMarker.bind(this)); - } else { - if ($('#elevation-chart').hasClass('show')) { - this.shouldRestoreChart = true; - } - $('#elevation-chart').collapse('hide'); - } - }, - - _keydownListener: function(e) { - if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggle) { - $('#elevation-btn').click(); - } - } -}); diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js new file mode 100644 index 0000000..f3fa51c --- /dev/null +++ b/js/plugin/Heightgraph.js @@ -0,0 +1,329 @@ +BR.Heightgraph = L.Control.Heightgraph.extend({ + options: { + width: $('#map').outerWidth(), + margins: { + top: 20, + right: 30, + bottom: 30, + left: 60 + }, + expandControls: false, + mappings: { + steepness: { + '-5': { + text: '16%+', + color: '#028306' + }, + '-4': { + text: '10-15%', + color: '#2AA12E' + }, + '-3': { + text: '7-9%', + color: '#53BF56' + }, + '-2': { + text: '4-6%', + color: '#7BDD7E' + }, + '-1': { + text: '1-3%', + color: '#A4FBA6' + }, + '0': { + text: '0%', + color: '#ffcc99' + }, + '1': { + text: '1-3%', + color: '#F29898' + }, + '2': { + text: '4-6%', + color: '#E07575' + }, + '3': { + text: '7-9%', + color: '#CF5352' + }, + '4': { + text: '10-15%', + color: '#BE312F' + }, + '5': { + text: '16%+', + color: '#AD0F0C' + } + }, + waytypes: { + '0': { + text: 'Other', + color: '#30959e' + }, + '1': { + text: 'StateRoad', + color: '#3f9da6' + }, + '2': { + text: 'Road', + color: '#4ea5ae' + }, + '3': { + text: 'Street', + color: '#5baeb5' + }, + '4': { + text: 'Path', + color: '#67b5bd' + }, + '5': { + text: 'Track', + color: '#73bdc4' + }, + '6': { + text: 'Cycleway', + color: '#7fc7cd' + }, + '7': { + text: 'Footway', + color: '#8acfd5' + }, + '8': { + text: 'Steps', + color: '#96d7dc' + }, + '9': { + text: 'Ferry', + color: '#a2dfe5' + }, + '10': { + text: 'Construction', + color: '#ade8ed' + } + }, + surface: { + '0': { + text: 'Other', + color: '#ddcdeb' + }, + '1': { + text: 'Paved', + color: '#cdb8df' + }, + '2': { + text: 'Unpaved', + color: '#d2c0e3' + }, + '3': { + text: 'Asphalt', + color: '#bca4d3' + }, + '4': { + text: 'Concrete', + color: '#c1abd7' + }, + '5': { + text: 'Cobblestone', + color: '#c7b2db' + }, + '6': { + text: 'Metal', + color: '#e8dcf3' + }, + '7': { + text: 'Wood', + color: '#eee3f7' + }, + '8': { + text: 'Compacted Gravel', + color: '#d8c6e7' + }, + '9': { + text: 'Fine Gravel', + color: '#8f9de4' + }, + '10': { + text: 'Gravel', + color: '#e3d4ef' + }, + '11': { + text: 'Dirt', + color: '#99a6e7' + }, + '12': { + text: 'Ground', + color: '#a3aeeb' + }, + '13': { + text: 'Ice', + color: '#acb6ee' + }, + '14': { + text: 'Salt', + color: '#b6c0f2' + }, + '15': { + text: 'Sand', + color: '#c9d1f8' + }, + '16': { + text: 'Woodchips', + color: '#c0c8f5' + }, + '17': { + text: 'Grass', + color: '#d2dafc' + }, + '18': { + text: 'Grass Paver', + color: '#dbe3ff' + } + }, + suitability: { + '3': { + text: '3/10', + color: '#3D3D3D' + }, + '4': { + text: '4/10', + color: '#4D4D4D' + }, + '5': { + text: '5/10', + color: '#5D5D5D' + }, + '6': { + text: '6/10', + color: '#6D6D6D' + }, + '7': { + text: '7/10', + color: '#7C7C7C' + }, + '8': { + text: '8/10', + color: '#8D8D8D' + }, + '9': { + text: '9/10', + color: '#9D9D9D' + }, + '10': { + text: '10/10', + color: '#ADADAD' + } + } + } + }, + + onAdd: function(map) { + var container = L.Control.Heightgraph.prototype.onAdd.call(this, map); + + // revert registering touch events when touch screen detection is available and negative + // see https://github.com/MrMufflon/Leaflet.Elevation/issues/67 + if (L.Browser.touch && BR.Browser.touchScreenDetectable && !BR.Browser.touchScreen) { + // heightgraph registers its own event handlers in _appendBackground() + // + // this._background + // .on('touchmove.drag', null) + // .on('touchstart.drag', null) + // .on('touchstart.focus', null); + // L.DomEvent.off(this._container, 'touchend', this._dragEndHandler, this); + // + // this._background + // .on('mousemove.focus', this._mousemoveHandler.bind(this)) + // .on('mouseout.focus', this._mouseoutHandler.bind(this)) + // .on('mousedown.drag', this._dragStartHandler.bind(this)) + // .on('mousemove.drag', this._dragHandler.bind(this)); + // L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, this); + } + + return container; + }, + + initialized: false, + + addBelow: function(map) { + // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 + // this.width($('#map').outerWidth()); + this.options.width = $('#content').outerWidth(); + + if (this.getContainer() != null) { + this.remove(map); + } + + function setParent(el, newParent) { + newParent.appendChild(el); + } + this.addTo(map); + + // move elevation graph outside of the map + setParent(this.getContainer(), document.getElementById('elevation-chart')); + + // this function is also executed on window resize; hence, + // initialize the internal state on first call, + // otherwise reset it + if (this.initialized === true) { + this._removeMarkedSegmentsOnMap(); + this._resetDrag(); + this._onAddData(); + } else { + // bind the the mouse move and mouse out handlers, I'll reuse them later on + this._mouseMoveHandlerBound = this._mapMousemoveHandler.bind(this); + this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); + + this.initialized = true; + + // and render the chart + this.update(); + } + }, + + update: function(track, layer) { + // bring height indicator to front, because of track casing in BR.Routing + if (this._mouseHeightFocus) { + var g = this._mouseHeightFocus[0][0].parentNode; + g.parentNode.appendChild(g); + } + + if (track && track.getLatLngs().length > 0) { + // TODO fix the geojson + // https://leafletjs.com/reference-1.6.0.html#layer + var geojson = track.toGeoJSON(); + geojson.properties = { attributeType: 0 }; + var data = [ + { + type: 'FeatureCollection', + features: [geojson], + properties: { + Creator: 'OpenRouteService.org', + records: 1, + summary: 'steepness' + } + } + ]; + this.addData(data); + + // this adds a blue track on the map, which only overwrites the pink brouter track + // L.geoJson(geojson).addTo(this._map); + + // re-add handlers + if (layer) { + layer.on('mousemove', this._mouseMoveHandlerBound); + layer.on('mouseout', this._mouseoutHandlerBound); + } + } else { + this._removeMarkedSegmentsOnMap(); + this._resetDrag(); + + // clear chart by passing an empty dataset + this.addData([]); + + // and remove handlers + if (layer) { + layer.off('mousemove', this._mouseMoveHandlerBound); + layer.off('mouseout', this._mouseoutHandlerBound); + } + } + } +}); diff --git a/package.json b/package.json index 015a18b..2d1fafb 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "leaflet-control-geocoder": "^1.13.0", "leaflet-easybutton": "*", "leaflet-editable": "^1.1.0", - "leaflet-elevation": "nrenner/Leaflet.Elevation#dev", "leaflet-filelayer": "^1.2.0", "leaflet-geometryutil": "^0.9.1", "leaflet-hotline": "^0.4.0", @@ -68,6 +67,7 @@ "leaflet-routing": "nrenner/leaflet-routing#e94e153", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", + "leaflet.heightgraph": "^1.3.2", "leaflet.locatecontrol": "^0.60.0", "leaflet.snogylop": "^0.4.0", "leaflet.stravasegments": "2.3.2", @@ -173,11 +173,11 @@ "dist/css/bootstrap.css" ] }, - "leaflet-elevation": { + "leaflet.heightgraph": { "main": [ - "src/L.Control.Elevation.js", - "dist/leaflet.elevation-0.0.4.css", - "dist/images/*.png" + "src/L.Control.Heightgraph.js", + "src/L.Control.Heightgraph.css", + "src/img/*.svg" ], "dependencies": null }, diff --git a/yarn.lock b/yarn.lock index 3f448d4..81b9477 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4886,10 +4886,6 @@ leaflet-editable@^1.1.0: resolved "https://registry.yarnpkg.com/leaflet-editable/-/leaflet-editable-1.2.0.tgz#a3a01001764ba58ea923381ee6a1c814708a0b84" integrity sha512-wG11JwpL8zqIbypTop6xCRGagMuWw68ihYu4uqrqc5Ep0wnEJeyob7NB2Rt5t74Oih4rwJ3OfwaGbzdowOGfYQ== -leaflet-elevation@nrenner/Leaflet.Elevation#dev: - version "0.0.4" - resolved "https://codeload.github.com/nrenner/Leaflet.Elevation/tar.gz/9ae6a3caef5f01abb3e55e05376df2e0046f7449" - leaflet-filelayer@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/leaflet-filelayer/-/leaflet-filelayer-1.2.0.tgz#9f822e68a06072b0b0a8f328ba9419ba96bbccb1" @@ -4935,6 +4931,13 @@ leaflet-triangle-marker@^1.0.2: resolved "https://registry.yarnpkg.com/leaflet-triangle-marker/-/leaflet-triangle-marker-1.0.2.tgz#e4c5f1d09d10ae078f8fba87aa32e5884017ad96" integrity sha512-J2Xa5UgUV6rU04bcwAdQRptnLm7nSxDtxqfX0Nzb+tz6p3buyEEBkXdOvEgzD6ghuII6BVSXFb4QP8cyVKigXg== +leaflet.heightgraph@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/leaflet.heightgraph/-/leaflet.heightgraph-1.3.2.tgz#efec1c8c3cf2dad6c97761d8954043ff8f7ded27" + integrity sha512-GdNQdkxJziBItFwWPN8teeg4UUzQWEizh5w7VU9JJ8VNMSfqVCvk/D4aOFL6OhTAamUbFSYn6FWQAuyOJvQlSg== + dependencies: + leaflet "^1.6.0" + leaflet.locatecontrol@^0.60.0: version "0.60.0" resolved "https://registry.yarnpkg.com/leaflet.locatecontrol/-/leaflet.locatecontrol-0.60.0.tgz#fc7be657ca9b7e8b8ba7263e52b0bb902b7cd965" From c17e6ed711b650a559d9636c1ad792c780a9054d Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sat, 10 Oct 2020 18:29:42 -0700 Subject: [PATCH 02/20] Use Heightgraph in lieu of Elevation (part II) - Heightgraph supports resizing; remove the Elevation specific workaround which was readding the data - resize the elevation chart on window resize and chart show --- js/index.js | 20 ++++++++++++-------- js/plugin/Heightgraph.js | 40 +++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/js/index.js b/js/index.js index fbb0997..740c089 100644 --- a/js/index.js +++ b/js/index.js @@ -199,9 +199,19 @@ stats = new BR.TrackStats(); } - // remove the old dep - // elevation = new BR.Elevation(); elevation = new BR.Heightgraph(); + // Trigger the chart resize after the toggle animation is complete, + // in case the window was resized while the chart was not visible. + // The resize must be called after the animation (i.e. 'shown.bs.collapse') + // and cannot be called before the animation (i.e. 'show.bs.collapse'), + // for the container has the old width pre animation and new width post animation. + var container = $('#elevation-chart'); + container.on('shown.bs.collapse', function() { + elevation.resize({ + width: container.width(), + height: container.height() + }); + }); profile = new BR.Profile(); profile.on('update', function(evt) { @@ -422,12 +432,6 @@ }, urlHash ); - - // listener and initCollapse here and not in onAdd, as addBelow calls addTo (-> onAdd) on resize - $(window).resize(function() { - elevation.addBelow(map); - }); - elevation.initCollapse(map); } i18next.on('languageChanged', function(detectedLanguage) { diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index f3fa51c..9655069 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -216,6 +216,7 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ } }, + /* onAdd: function(map) { var container = L.Control.Heightgraph.prototype.onAdd.call(this, map); @@ -240,8 +241,9 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ return container; }, + */ - initialized: false, + // initialized: false, addBelow: function(map) { // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 @@ -263,20 +265,32 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ // this function is also executed on window resize; hence, // initialize the internal state on first call, // otherwise reset it - if (this.initialized === true) { - this._removeMarkedSegmentsOnMap(); - this._resetDrag(); - this._onAddData(); - } else { - // bind the the mouse move and mouse out handlers, I'll reuse them later on - this._mouseMoveHandlerBound = this._mapMousemoveHandler.bind(this); - this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); + // if (this.initialized === true) { + // this._removeMarkedSegmentsOnMap(); + // this._resetDrag(); + // this._onAddData(); + // } else { + // bind the the mouse move and mouse out handlers, I'll reuse them later on + this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); + this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); - this.initialized = true; + var self = this; + var container = $('#elevation-chart'); + $(window).resize(function() { + // avoid useless computations if the chart is not visible + if (container.is(':visible')) { + self.resize({ + width: container.width(), + height: container.height() + }); + } + }); - // and render the chart - this.update(); - } + // this.initialized = true; + + // and render the chart + this.update(); + // } }, update: function(track, layer) { From 37a0f9b97206b76bbeda58e240d513fff276cdca Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sat, 10 Oct 2020 18:40:06 -0700 Subject: [PATCH 03/20] Use Heightgraph in lieu of Elevation (part III) - remove old comments and unusable commented out code --- js/plugin/Heightgraph.js | 43 ---------------------------------------- 1 file changed, 43 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 9655069..18c773e 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -216,35 +216,6 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ } }, - /* - onAdd: function(map) { - var container = L.Control.Heightgraph.prototype.onAdd.call(this, map); - - // revert registering touch events when touch screen detection is available and negative - // see https://github.com/MrMufflon/Leaflet.Elevation/issues/67 - if (L.Browser.touch && BR.Browser.touchScreenDetectable && !BR.Browser.touchScreen) { - // heightgraph registers its own event handlers in _appendBackground() - // - // this._background - // .on('touchmove.drag', null) - // .on('touchstart.drag', null) - // .on('touchstart.focus', null); - // L.DomEvent.off(this._container, 'touchend', this._dragEndHandler, this); - // - // this._background - // .on('mousemove.focus', this._mousemoveHandler.bind(this)) - // .on('mouseout.focus', this._mouseoutHandler.bind(this)) - // .on('mousedown.drag', this._dragStartHandler.bind(this)) - // .on('mousemove.drag', this._dragHandler.bind(this)); - // L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, this); - } - - return container; - }, - */ - - // initialized: false, - addBelow: function(map) { // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 // this.width($('#map').outerWidth()); @@ -262,14 +233,6 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ // move elevation graph outside of the map setParent(this.getContainer(), document.getElementById('elevation-chart')); - // this function is also executed on window resize; hence, - // initialize the internal state on first call, - // otherwise reset it - // if (this.initialized === true) { - // this._removeMarkedSegmentsOnMap(); - // this._resetDrag(); - // this._onAddData(); - // } else { // bind the the mouse move and mouse out handlers, I'll reuse them later on this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); @@ -286,11 +249,8 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ } }); - // this.initialized = true; - // and render the chart this.update(); - // } }, update: function(track, layer) { @@ -318,9 +278,6 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ ]; this.addData(data); - // this adds a blue track on the map, which only overwrites the pink brouter track - // L.geoJson(geojson).addTo(this._map); - // re-add handlers if (layer) { layer.on('mousemove', this._mouseMoveHandlerBound); From 8fcec8ec86edd25e47f55ae4c867261d479113ec Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sun, 11 Oct 2020 19:09:18 -0700 Subject: [PATCH 04/20] Use Heightgraph in lieu of Elevation (part IV) Build the GeoJSON object manually. --- js/plugin/Heightgraph.js | 271 ++++++++++++++------------------------- 1 file changed, 93 insertions(+), 178 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 18c773e..3ef45c5 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -2,14 +2,14 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ options: { width: $('#map').outerWidth(), margins: { - top: 20, + top: 15, right: 30, - bottom: 30, - left: 60 + bottom: 40, + left: 70 }, expandControls: false, mappings: { - steepness: { + gradient: { '-5': { text: '16%+', color: '#028306' @@ -54,164 +54,6 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ text: '16%+', color: '#AD0F0C' } - }, - waytypes: { - '0': { - text: 'Other', - color: '#30959e' - }, - '1': { - text: 'StateRoad', - color: '#3f9da6' - }, - '2': { - text: 'Road', - color: '#4ea5ae' - }, - '3': { - text: 'Street', - color: '#5baeb5' - }, - '4': { - text: 'Path', - color: '#67b5bd' - }, - '5': { - text: 'Track', - color: '#73bdc4' - }, - '6': { - text: 'Cycleway', - color: '#7fc7cd' - }, - '7': { - text: 'Footway', - color: '#8acfd5' - }, - '8': { - text: 'Steps', - color: '#96d7dc' - }, - '9': { - text: 'Ferry', - color: '#a2dfe5' - }, - '10': { - text: 'Construction', - color: '#ade8ed' - } - }, - surface: { - '0': { - text: 'Other', - color: '#ddcdeb' - }, - '1': { - text: 'Paved', - color: '#cdb8df' - }, - '2': { - text: 'Unpaved', - color: '#d2c0e3' - }, - '3': { - text: 'Asphalt', - color: '#bca4d3' - }, - '4': { - text: 'Concrete', - color: '#c1abd7' - }, - '5': { - text: 'Cobblestone', - color: '#c7b2db' - }, - '6': { - text: 'Metal', - color: '#e8dcf3' - }, - '7': { - text: 'Wood', - color: '#eee3f7' - }, - '8': { - text: 'Compacted Gravel', - color: '#d8c6e7' - }, - '9': { - text: 'Fine Gravel', - color: '#8f9de4' - }, - '10': { - text: 'Gravel', - color: '#e3d4ef' - }, - '11': { - text: 'Dirt', - color: '#99a6e7' - }, - '12': { - text: 'Ground', - color: '#a3aeeb' - }, - '13': { - text: 'Ice', - color: '#acb6ee' - }, - '14': { - text: 'Salt', - color: '#b6c0f2' - }, - '15': { - text: 'Sand', - color: '#c9d1f8' - }, - '16': { - text: 'Woodchips', - color: '#c0c8f5' - }, - '17': { - text: 'Grass', - color: '#d2dafc' - }, - '18': { - text: 'Grass Paver', - color: '#dbe3ff' - } - }, - suitability: { - '3': { - text: '3/10', - color: '#3D3D3D' - }, - '4': { - text: '4/10', - color: '#4D4D4D' - }, - '5': { - text: '5/10', - color: '#5D5D5D' - }, - '6': { - text: '6/10', - color: '#6D6D6D' - }, - '7': { - text: '7/10', - color: '#7C7C7C' - }, - '8': { - text: '8/10', - color: '#8D8D8D' - }, - '9': { - text: '9/10', - color: '#9D9D9D' - }, - '10': { - text: '10/10', - color: '#ADADAD' - } } } }, @@ -261,22 +103,8 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ } if (track && track.getLatLngs().length > 0) { - // TODO fix the geojson - // https://leafletjs.com/reference-1.6.0.html#layer - var geojson = track.toGeoJSON(); - geojson.properties = { attributeType: 0 }; - var data = [ - { - type: 'FeatureCollection', - features: [geojson], - properties: { - Creator: 'OpenRouteService.org', - records: 1, - summary: 'steepness' - } - } - ]; - this.addData(data); + var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); + this.addData(geojsonFeatures); // re-add handlers if (layer) { @@ -296,5 +124,92 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ layer.off('mouseout', this._mouseoutHandlerBound); } } + }, + + /** + * @param {LatLng[]} latLngs an array of LatLng objects, guaranteed to be not empty + */ + _buildGeojsonFeatures: function(latLngs) { + var features = []; + + // this is going to be initialized in the first iteration + var currentFeature; + // undefined is fine, as it will be different than the next gradient + var previousGradient; + for (var i = 1; i < latLngs.length; i++) { + var previousPoint = latLngs[i - 1]; + var currentPoint = latLngs[i]; + + var dist = currentPoint.distanceTo(previousPoint); // always > 0 + var altDelta = currentPoint.alt - previousPoint.alt; + var currentGradient = this._mapGradient((altDelta * 100) / dist); + + var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; + + if (currentGradient == previousGradient) { + currentFeature.geometry.coordinates.push(coordinate); + } else { + currentFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [coordinate] + }, + properties: { + attributeType: currentGradient + } + }; + features.push(currentFeature); + } + + // prepare for the next iteration + previousGradient = currentGradient; + } + + // insert the first coordinate in pole position, + // and give it the same gradient as the next one + features[0].geometry.coordinates.splice(0, 0, [latLngs[0].lng, latLngs[0].lat, latLngs[0].alt]); + + return [ + { + type: 'FeatureCollection', + features: features, + properties: { + Creator: 'OpenRouteService.org', + records: features.length, + summary: 'gradient' + } + } + ]; + }, + + /** + * Map a gradient percentage to one of the codes defined + * in options.mappings.gradient. + */ + _mapGradient: function(gradientPercentage) { + if (gradientPercentage <= -16) { + return -5; + } else if (gradientPercentage > -16 && gradientPercentage <= -10) { + return -4; + } else if (gradientPercentage > -10 && gradientPercentage <= -7) { + return -3; + } else if (gradientPercentage > -7 && gradientPercentage <= -4) { + return -2; + } else if (gradientPercentage > -4 && gradientPercentage <= -1) { + return -1; + } else if (gradientPercentage > -1 && gradientPercentage < 1) { + return 0; + } else if (gradientPercentage >= 1 && gradientPercentage < 4) { + return 1; + } else if (gradientPercentage >= 4 && gradientPercentage < 7) { + return 2; + } else if (gradientPercentage >= 7 && gradientPercentage < 10) { + return 3; + } else if (gradientPercentage >= 10 && gradientPercentage < 16) { + return 4; + } else if (gradientPercentage >= 16) { + return 5; + } } }); From 5fd3977768e8dd1370246478363f616a6c275132 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 12 Oct 2020 11:53:07 -0700 Subject: [PATCH 05/20] Use Heightgraph in lieu of Elevation (part V) - fix the grade calculation - don't show the grade labels, as they are all over (should be normalized) - fix the display issues by overridding the heightgraph CSS --- css/style.css | 12 ++++++ js/index.js | 12 ------ js/plugin/Heightgraph.js | 80 ++++++++++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/css/style.css b/css/style.css index aeea5d3..da4a4dd 100644 --- a/css/style.css +++ b/css/style.css @@ -743,3 +743,15 @@ table.dataTable.display tbody tr:hover.selected { so we ignore pointer events in this situation to avoid that*/ pointer-events: none; } + +/* + * Heightgraph customizations; + * since the gradient types are empty, hide the legend as it's useless; + * since there's only the gradient layer, hide the layer selector. + */ +.legend-hover { + display: none; +} +#selectionText { + display: none; +} diff --git a/js/index.js b/js/index.js index 740c089..b9bba2f 100644 --- a/js/index.js +++ b/js/index.js @@ -200,18 +200,6 @@ } elevation = new BR.Heightgraph(); - // Trigger the chart resize after the toggle animation is complete, - // in case the window was resized while the chart was not visible. - // The resize must be called after the animation (i.e. 'shown.bs.collapse') - // and cannot be called before the animation (i.e. 'show.bs.collapse'), - // for the container has the old width pre animation and new width post animation. - var container = $('#elevation-chart'); - container.on('shown.bs.collapse', function() { - elevation.resize({ - width: container.width(), - height: container.height() - }); - }); profile = new BR.Profile(); profile.on('update', function(evt) { diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 3ef45c5..a5b0287 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -4,54 +4,54 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ margins: { top: 15, right: 30, - bottom: 40, + bottom: 30, left: 70 }, expandControls: false, mappings: { gradient: { '-5': { - text: '16%+', + text: '', color: '#028306' }, '-4': { - text: '10-15%', + text: '', color: '#2AA12E' }, '-3': { - text: '7-9%', + text: '', color: '#53BF56' }, '-2': { - text: '4-6%', + text: '', color: '#7BDD7E' }, '-1': { - text: '1-3%', + text: '', color: '#A4FBA6' }, '0': { - text: '0%', + text: '', color: '#ffcc99' }, '1': { - text: '1-3%', + text: '', color: '#F29898' }, '2': { - text: '4-6%', + text: '', color: '#E07575' }, '3': { - text: '7-9%', + text: '', color: '#CF5352' }, '4': { - text: '10-15%', + text: '', color: '#BE312F' }, '5': { - text: '16%+', + text: '', color: '#AD0F0C' } } @@ -90,6 +90,17 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ }); } }); + // Trigger the chart resize after the toggle animation is complete, + // in case the window was resized while the chart was not visible. + // The resize must be called after the animation (i.e. 'shown.bs.collapse') + // and cannot be called before the animation (i.e. 'show.bs.collapse'), + // for the container has the old width pre animation and new width post animation. + container.on('shown.bs.collapse', function() { + self.resize({ + width: container.width(), + height: container.height() + }); + }); // and render the chart this.update(); @@ -105,6 +116,23 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ if (track && track.getLatLngs().length > 0) { var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); this.addData(geojsonFeatures); + // TODO + /* +var geojson = track.toGeoJSON(); +geojson.properties = { attributeType: 0 }; +var data = [ + { + type: 'FeatureCollection', + features: [geojson], + properties: { + Creator: 'OpenRouteService.org', + records: 1, + summary: 'gradient' + } + } +]; +this.addData(data); +*/ // re-add handlers if (layer) { @@ -140,9 +168,19 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ var previousPoint = latLngs[i - 1]; var currentPoint = latLngs[i]; - var dist = currentPoint.distanceTo(previousPoint); // always > 0 + var dist = currentPoint.distanceTo(previousPoint); // never negative var altDelta = currentPoint.alt - previousPoint.alt; - var currentGradient = this._mapGradient((altDelta * 100) / dist); + var currentGradientPercentage = (altDelta * 100) / dist; + var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); + // TODO + /* +console.log("gradient %:", currentGradientPercentage, + "; gradient level:", currentGradient, + "; dist:", dist, + "; alt:", altDelta, + "; previous point:", previousPoint.lng, previousPoint.lat, previousPoint.alt, + "; current point:", currentPoint.lng, currentPoint.lat, currentPoint.alt); +*/ var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; @@ -153,7 +191,13 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ type: 'Feature', geometry: { type: 'LineString', - coordinates: [coordinate] + coordinates: [ + // each feature starts with the last point on the previous feature; + // that will also take care of inserting the firstmost point + // (latLngs[0]) at position 0 into the first feature in the list + [previousPoint.lng, previousPoint.lat, previousPoint.alt], + coordinate + ] }, properties: { attributeType: currentGradient @@ -166,10 +210,6 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ previousGradient = currentGradient; } - // insert the first coordinate in pole position, - // and give it the same gradient as the next one - features[0].geometry.coordinates.splice(0, 0, [latLngs[0].lng, latLngs[0].lat, latLngs[0].alt]); - return [ { type: 'FeatureCollection', @@ -184,7 +224,7 @@ BR.Heightgraph = L.Control.Heightgraph.extend({ }, /** - * Map a gradient percentage to one of the codes defined + * Map a gradient percentage to one of the levels defined * in options.mappings.gradient. */ _mapGradient: function(gradientPercentage) { From 77ed28cb7635ced6a0d54c90a884e1bb6a78e807 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Fri, 23 Oct 2020 13:02:46 -0700 Subject: [PATCH 06/20] Use Heightgraph transpiled --- js/plugin/Heightgraph.js | 461 ++++++++++++++++++++------------------- package.json | 8 +- yarn.lock | 5 - 3 files changed, 236 insertions(+), 238 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index a5b0287..ecc94e8 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -1,255 +1,260 @@ -BR.Heightgraph = L.Control.Heightgraph.extend({ - options: { - width: $('#map').outerWidth(), - margins: { - top: 15, - right: 30, - bottom: 30, - left: 70 - }, - expandControls: false, - mappings: { - gradient: { - '-5': { - text: '', - color: '#028306' - }, - '-4': { - text: '', - color: '#2AA12E' - }, - '-3': { - text: '', - color: '#53BF56' - }, - '-2': { - text: '', - color: '#7BDD7E' - }, - '-1': { - text: '', - color: '#A4FBA6' - }, - '0': { - text: '', - color: '#ffcc99' - }, - '1': { - text: '', - color: '#F29898' - }, - '2': { - text: '', - color: '#E07575' - }, - '3': { - text: '', - color: '#CF5352' - }, - '4': { - text: '', - color: '#BE312F' - }, - '5': { - text: '', - color: '#AD0F0C' +BR.Heightgraph = function(map, layersControl, routing, pois) { + Heightgraph = L.Control.Heightgraph.extend({ + options: { + width: $('#map').outerWidth(), + margins: { + top: 15, + right: 30, + bottom: 30, + left: 70 + }, + expandControls: false, + mappings: { + gradient: { + '-5': { + text: '', + color: '#028306' + }, + '-4': { + text: '', + color: '#2AA12E' + }, + '-3': { + text: '', + color: '#53BF56' + }, + '-2': { + text: '', + color: '#7BDD7E' + }, + '-1': { + text: '', + color: '#A4FBA6' + }, + '0': { + text: '', + color: '#ffcc99' + }, + '1': { + text: '', + color: '#F29898' + }, + '2': { + text: '', + color: '#E07575' + }, + '3': { + text: '', + color: '#CF5352' + }, + '4': { + text: '', + color: '#BE312F' + }, + '5': { + text: '', + color: '#AD0F0C' + } } } - } - }, + }, - addBelow: function(map) { - // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 - // this.width($('#map').outerWidth()); - this.options.width = $('#content').outerWidth(); + addBelow: function(map) { + // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 + // this.width($('#map').outerWidth()); + this.options.width = $('#content').outerWidth(); - if (this.getContainer() != null) { - this.remove(map); - } + if (this.getContainer() != null) { + this.remove(map); + } - function setParent(el, newParent) { - newParent.appendChild(el); - } - this.addTo(map); + function setParent(el, newParent) { + newParent.appendChild(el); + } + this.addTo(map); - // move elevation graph outside of the map - setParent(this.getContainer(), document.getElementById('elevation-chart')); + // move elevation graph outside of the map + setParent(this.getContainer(), document.getElementById('elevation-chart')); - // bind the the mouse move and mouse out handlers, I'll reuse them later on - this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); - this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); + // bind the the mouse move and mouse out handlers, I'll reuse them later on + this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); + this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); - var self = this; - var container = $('#elevation-chart'); - $(window).resize(function() { - // avoid useless computations if the chart is not visible - if (container.is(':visible')) { + var self = this; + var container = $('#elevation-chart'); + $(window).resize(function() { + // avoid useless computations if the chart is not visible + if (container.is(':visible')) { + self.resize({ + width: container.width(), + height: container.height() + }); + } + }); + // Trigger the chart resize after the toggle animation is complete, + // in case the window was resized while the chart was not visible. + // The resize must be called after the animation (i.e. 'shown.bs.collapse') + // and cannot be called before the animation (i.e. 'show.bs.collapse'), + // for the container has the old width pre animation and new width post animation. + container.on('shown.bs.collapse', function() { self.resize({ width: container.width(), height: container.height() }); - } - }); - // Trigger the chart resize after the toggle animation is complete, - // in case the window was resized while the chart was not visible. - // The resize must be called after the animation (i.e. 'shown.bs.collapse') - // and cannot be called before the animation (i.e. 'show.bs.collapse'), - // for the container has the old width pre animation and new width post animation. - container.on('shown.bs.collapse', function() { - self.resize({ - width: container.width(), - height: container.height() }); - }); - // and render the chart - this.update(); - }, + // and render the chart + this.update(); + }, - update: function(track, layer) { - // bring height indicator to front, because of track casing in BR.Routing - if (this._mouseHeightFocus) { - var g = this._mouseHeightFocus[0][0].parentNode; - g.parentNode.appendChild(g); - } - - if (track && track.getLatLngs().length > 0) { - var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); - this.addData(geojsonFeatures); - // TODO - /* -var geojson = track.toGeoJSON(); -geojson.properties = { attributeType: 0 }; -var data = [ - { - type: 'FeatureCollection', - features: [geojson], - properties: { - Creator: 'OpenRouteService.org', - records: 1, - summary: 'gradient' - } - } -]; -this.addData(data); -*/ - - // re-add handlers - if (layer) { - layer.on('mousemove', this._mouseMoveHandlerBound); - layer.on('mouseout', this._mouseoutHandlerBound); + update: function(track, layer) { + // bring height indicator to front, because of track casing in BR.Routing + if (this._mouseHeightFocus) { + var g = this._mouseHeightFocus[0][0].parentNode; + g.parentNode.appendChild(g); } - } else { - this._removeMarkedSegmentsOnMap(); - this._resetDrag(); - // clear chart by passing an empty dataset - this.addData([]); - - // and remove handlers - if (layer) { - layer.off('mousemove', this._mouseMoveHandlerBound); - layer.off('mouseout', this._mouseoutHandlerBound); + if (track && track.getLatLngs().length > 0) { + var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); + this.addData(geojsonFeatures); + // TODO + /* + var geojson = track.toGeoJSON(); + geojson.properties = { attributeType: 0 }; + var data = [ + { + type: 'FeatureCollection', + features: [geojson], + properties: { + Creator: 'OpenRouteService.org', + records: 1, + summary: 'gradient' } } - }, + ]; + this.addData(data); + */ - /** - * @param {LatLng[]} latLngs an array of LatLng objects, guaranteed to be not empty - */ - _buildGeojsonFeatures: function(latLngs) { - var features = []; - - // this is going to be initialized in the first iteration - var currentFeature; - // undefined is fine, as it will be different than the next gradient - var previousGradient; - for (var i = 1; i < latLngs.length; i++) { - var previousPoint = latLngs[i - 1]; - var currentPoint = latLngs[i]; - - var dist = currentPoint.distanceTo(previousPoint); // never negative - var altDelta = currentPoint.alt - previousPoint.alt; - var currentGradientPercentage = (altDelta * 100) / dist; - var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); - // TODO - /* -console.log("gradient %:", currentGradientPercentage, - "; gradient level:", currentGradient, - "; dist:", dist, - "; alt:", altDelta, - "; previous point:", previousPoint.lng, previousPoint.lat, previousPoint.alt, - "; current point:", currentPoint.lng, currentPoint.lat, currentPoint.alt); -*/ - - var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; - - if (currentGradient == previousGradient) { - currentFeature.geometry.coordinates.push(coordinate); + // re-add handlers + if (layer) { + layer.on('mousemove', this._mouseMoveHandlerBound); + layer.on('mouseout', this._mouseoutHandlerBound); + } } else { - currentFeature = { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: [ - // each feature starts with the last point on the previous feature; - // that will also take care of inserting the firstmost point - // (latLngs[0]) at position 0 into the first feature in the list - [previousPoint.lng, previousPoint.lat, previousPoint.alt], - coordinate - ] - }, - properties: { - attributeType: currentGradient - } - }; - features.push(currentFeature); - } + this._removeMarkedSegmentsOnMap(); + this._resetDrag(); - // prepare for the next iteration - previousGradient = currentGradient; - } + // clear chart by passing an empty dataset + this.addData([]); - return [ - { - type: 'FeatureCollection', - features: features, - properties: { - Creator: 'OpenRouteService.org', - records: features.length, - summary: 'gradient' + // and remove handlers + if (layer) { + layer.off('mousemove', this._mouseMoveHandlerBound); + layer.off('mouseout', this._mouseoutHandlerBound); } } - ]; - }, + }, - /** - * Map a gradient percentage to one of the levels defined - * in options.mappings.gradient. - */ - _mapGradient: function(gradientPercentage) { - if (gradientPercentage <= -16) { - return -5; - } else if (gradientPercentage > -16 && gradientPercentage <= -10) { - return -4; - } else if (gradientPercentage > -10 && gradientPercentage <= -7) { - return -3; - } else if (gradientPercentage > -7 && gradientPercentage <= -4) { - return -2; - } else if (gradientPercentage > -4 && gradientPercentage <= -1) { - return -1; - } else if (gradientPercentage > -1 && gradientPercentage < 1) { - return 0; - } else if (gradientPercentage >= 1 && gradientPercentage < 4) { - return 1; - } else if (gradientPercentage >= 4 && gradientPercentage < 7) { - return 2; - } else if (gradientPercentage >= 7 && gradientPercentage < 10) { - return 3; - } else if (gradientPercentage >= 10 && gradientPercentage < 16) { - return 4; - } else if (gradientPercentage >= 16) { - return 5; + /** + * @param {LatLng[]} latLngs an array of LatLng objects, guaranteed to be not empty + */ + _buildGeojsonFeatures: function(latLngs) { + var features = []; + + // this is going to be initialized in the first iteration + var currentFeature; + // undefined is fine, as it will be different than the next gradient + var previousGradient; + for (var i = 1; i < latLngs.length; i++) { + var previousPoint = latLngs[i - 1]; + var currentPoint = latLngs[i]; + + var dist = currentPoint.distanceTo(previousPoint); // never negative + var altDelta = currentPoint.alt - previousPoint.alt; + var currentGradientPercentage = (altDelta * 100) / dist; + var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); + // TODO + /* + console.log("gradient %:", currentGradientPercentage, + "; gradient level:", currentGradient, + "; dist:", dist, + "; alt:", altDelta, + "; previous point:", previousPoint.lng, previousPoint.lat, previousPoint.alt, + "; current point:", currentPoint.lng, currentPoint.lat, currentPoint.alt); + */ + + var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; + + if (currentGradient == previousGradient) { + currentFeature.geometry.coordinates.push(coordinate); + } else { + currentFeature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + // each feature starts with the last point on the previous feature; + // that will also take care of inserting the firstmost point + // (latLngs[0]) at position 0 into the first feature in the list + [previousPoint.lng, previousPoint.lat, previousPoint.alt], + coordinate + ] + }, + properties: { + attributeType: currentGradient + } + }; + features.push(currentFeature); + } + + // prepare for the next iteration + previousGradient = currentGradient; + } + + return [ + { + type: 'FeatureCollection', + features: features, + properties: { + Creator: 'OpenRouteService.org', + records: features.length, + summary: 'gradient' + } + } + ]; + }, + + /** + * Map a gradient percentage to one of the levels defined + * in options.mappings.gradient. + */ + _mapGradient: function(gradientPercentage) { + if (gradientPercentage <= -16) { + return -5; + } else if (gradientPercentage > -16 && gradientPercentage <= -10) { + return -4; + } else if (gradientPercentage > -10 && gradientPercentage <= -7) { + return -3; + } else if (gradientPercentage > -7 && gradientPercentage <= -4) { + return -2; + } else if (gradientPercentage > -4 && gradientPercentage <= -1) { + return -1; + } else if (gradientPercentage > -1 && gradientPercentage < 1) { + return 0; + } else if (gradientPercentage >= 1 && gradientPercentage < 4) { + return 1; + } else if (gradientPercentage >= 4 && gradientPercentage < 7) { + return 2; + } else if (gradientPercentage >= 7 && gradientPercentage < 10) { + return 3; + } else if (gradientPercentage >= 10 && gradientPercentage < 16) { + return 4; + } else if (gradientPercentage >= 16) { + return 5; + } } - } -}); + }); + + var heightgraphControl = new Heightgraph(); + return heightgraphControl; +}; diff --git a/package.json b/package.json index 2d1fafb..839da45 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "bootstrap-select": "1.13.6", "bootstrap-slider": "^9.8.1", "codemirror": "^5.35.0", - "d3": "~3.5.5", "datatables": "~1.10.18", "font-awesome": "^4.7.0", "i18next": "^15.0.4", @@ -175,11 +174,10 @@ }, "leaflet.heightgraph": { "main": [ - "src/L.Control.Heightgraph.js", + "dist/L.Control.Heightgraph.js", "src/L.Control.Heightgraph.css", - "src/img/*.svg" - ], - "dependencies": null + "dist/img/*.svg" + ] }, "leaflet-control-geocoder": { "main": [ diff --git a/yarn.lock b/yarn.lock index 81b9477..3ecff54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2552,11 +2552,6 @@ d3-voronoi@1.1.2: resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" integrity sha1-Fodmfo8TotFYyAwUgMWinLDYlzw= -d3@~3.5.5: - version "3.5.17" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" - integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g= - d@1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" From bd0679d15c9558bcce97d1ddcc96164633a78deb Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Fri, 23 Oct 2020 18:57:07 -0700 Subject: [PATCH 07/20] Fix conflicts in Heightgraph integration --- js/plugin/Heightgraph.js | 2 +- package.json | 7 ++- patches/leaflet.heightgraph+1.3.3.patch | 28 +++++++++ yarn.lock | 83 +++++++++++++++++++++++-- 4 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 patches/leaflet.heightgraph+1.3.3.patch diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index ecc94e8..c911ffd 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -110,7 +110,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { update: function(track, layer) { // bring height indicator to front, because of track casing in BR.Routing if (this._mouseHeightFocus) { - var g = this._mouseHeightFocus[0][0].parentNode; + var g = this._mouseHeightFocus._groups[0][0].parentNode; g.parentNode.appendChild(g); } diff --git a/package.json b/package.json index 839da45..0fcdac6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "lint": "eslint .", "prettier": "prettier --write '**/*'", "serve": "gulp serve watch", - "standalone": "mvn clean install -f ../brouter -pl '!brouter-routing-app' -Dmaven.javadoc.skip=true && ../brouter/misc/scripts/generate_profile_variants.sh && gulp release:zip_standalone" + "standalone": "mvn clean install -f ../brouter -pl '!brouter-routing-app' -Dmaven.javadoc.skip=true && ../brouter/misc/scripts/generate_profile_variants.sh && gulp release:zip_standalone", + "postinstall": "patch-package" }, "husky": { "hooks": { @@ -66,7 +67,7 @@ "leaflet-routing": "nrenner/leaflet-routing#e94e153", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", - "leaflet.heightgraph": "^1.3.2", + "leaflet.heightgraph": "1.3.3", "leaflet.locatecontrol": "^0.60.0", "leaflet.snogylop": "^0.4.0", "leaflet.stravasegments": "2.3.2", @@ -108,6 +109,8 @@ "merge-stream": "^2.0.0", "node-fetch": "^2.6.1", "npmfiles": "^0.1.1", + "patch-package": "^6.2.2", + "postinstall-postinstall": "^2.1.0", "prettier": "^1.17.1", "pretty-quick": "^1.10.0" }, diff --git a/patches/leaflet.heightgraph+1.3.3.patch b/patches/leaflet.heightgraph+1.3.3.patch new file mode 100644 index 0000000..40cd1ea --- /dev/null +++ b/patches/leaflet.heightgraph+1.3.3.patch @@ -0,0 +1,28 @@ +diff --git a/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js b/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js +index 122c9ac..1a966af 100644 +--- a/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js ++++ b/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js +@@ -1282,10 +1282,10 @@ function schedule(node, name, id, index, group, timing) { + group: group, // For context during callback. + on: emptyOn, + tween: emptyTween, +- time: timing.time, +- delay: timing.delay, +- duration: timing.duration, +- ease: timing.ease, ++ time: timing ? timing.time : 0, ++ delay: timing ? timing.delay : 0, ++ duration: timing ? timing.duration : 0, ++ ease: timing ? timing.ease : null, + timer: null, + state: CREATED + }); +@@ -3062,7 +3062,7 @@ function initRange(domain, range) { + + var prefix = "$"; + +-function Map() {} ++// function Map() {} + + Map.prototype = map.prototype = { + constructor: Map, diff --git a/yarn.lock b/yarn.lock index 3ecff54..56b74d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1236,6 +1236,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + JSV@^4.0.x: version "4.0.2" resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" @@ -3362,6 +3367,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-yarn-workspace-root@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" + integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== + dependencies: + fs-extra "^4.0.3" + micromatch "^3.1.4" + findup-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" @@ -3496,6 +3509,24 @@ fs-extra@3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -4764,6 +4795,13 @@ jsonfile@^3.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsonlint@^1.6.2: version "1.6.3" resolved "https://registry.yarnpkg.com/jsonlint/-/jsonlint-1.6.3.tgz#cb5e31efc0b78291d0d862fbef05900adf212988" @@ -4823,6 +4861,13 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + last-run@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" @@ -4926,10 +4971,10 @@ leaflet-triangle-marker@^1.0.2: resolved "https://registry.yarnpkg.com/leaflet-triangle-marker/-/leaflet-triangle-marker-1.0.2.tgz#e4c5f1d09d10ae078f8fba87aa32e5884017ad96" integrity sha512-J2Xa5UgUV6rU04bcwAdQRptnLm7nSxDtxqfX0Nzb+tz6p3buyEEBkXdOvEgzD6ghuII6BVSXFb4QP8cyVKigXg== -leaflet.heightgraph@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/leaflet.heightgraph/-/leaflet.heightgraph-1.3.2.tgz#efec1c8c3cf2dad6c97761d8954043ff8f7ded27" - integrity sha512-GdNQdkxJziBItFwWPN8teeg4UUzQWEizh5w7VU9JJ8VNMSfqVCvk/D4aOFL6OhTAamUbFSYn6FWQAuyOJvQlSg== +leaflet.heightgraph@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/leaflet.heightgraph/-/leaflet.heightgraph-1.3.3.tgz#14b8bac70ac16863991c16f5d6b772aad8034fbd" + integrity sha512-jr6xDmiTBvIIu1WLI0q0rt9E30H1VVTxncHAaojcpsZeDvYyBZyBsgoeEi8ouJVP9OolCfNQTCyPbNR73tKgqg== dependencies: leaflet "^1.6.0" @@ -5931,6 +5976,24 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +patch-package@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.2.2.tgz#71d170d650c65c26556f0d0fbbb48d92b6cc5f39" + integrity sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^1.2.1" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -6154,6 +6217,11 @@ postcss@^6.0.0, postcss@^6.0.23: source-map "^0.6.1" supports-color "^5.4.0" +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -6705,7 +6773,7 @@ rimraf@2.6.3, rimraf@^2.2.8: dependencies: glob "^7.1.3" -rimraf@^2.6.1: +rimraf@^2.6.1, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -6946,6 +7014,11 @@ skmeans@0.9.7: resolved "https://registry.yarnpkg.com/skmeans/-/skmeans-0.9.7.tgz#72670cebb728508f56e29c0e10d11e623529ce5d" integrity sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" From 75b68ebfe1984b567b4d4101193b0abeecc1f382 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sun, 25 Oct 2020 20:11:33 -0700 Subject: [PATCH 08/20] add comments; clean up --- js/plugin/Heightgraph.js | 65 +++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index c911ffd..71004c5 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -163,8 +163,12 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // this is going to be initialized in the first iteration var currentFeature; - // undefined is fine, as it will be different than the next gradient + // undefined is fine, as it will be different than the current gradient + // in the first iteration var previousGradient; + // each feature starts with the last point on the previous feature; + // this will also take care of inserting the firstmost point + // (latLngs[0]) at position 0 into the first feature in the list for (var i = 1; i < latLngs.length; i++) { var previousPoint = latLngs[i - 1]; var currentPoint = latLngs[i]; @@ -174,36 +178,30 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var currentGradientPercentage = (altDelta * 100) / dist; var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); // TODO - /* - console.log("gradient %:", currentGradientPercentage, - "; gradient level:", currentGradient, - "; dist:", dist, - "; alt:", altDelta, - "; previous point:", previousPoint.lng, previousPoint.lat, previousPoint.alt, - "; current point:", currentPoint.lng, currentPoint.lat, currentPoint.alt); - */ - - var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; + console.log( + 'gradient %:', + currentGradientPercentage, + '; gradient level:', + currentGradient, + '; dist:', + dist, + '; alt:', + altDelta, + '; previous point:', + previousPoint.lng, + previousPoint.lat, + previousPoint.alt, + '; current point:', + currentPoint.lng, + currentPoint.lat, + currentPoint.alt + ); if (currentGradient == previousGradient) { + var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; currentFeature.geometry.coordinates.push(coordinate); } else { - currentFeature = { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: [ - // each feature starts with the last point on the previous feature; - // that will also take care of inserting the firstmost point - // (latLngs[0]) at position 0 into the first feature in the list - [previousPoint.lng, previousPoint.lat, previousPoint.alt], - coordinate - ] - }, - properties: { - attributeType: currentGradient - } - }; + currentFeature = this._buildFeature(previousPoint, currentPoint, currentGradient); features.push(currentFeature); } @@ -224,6 +222,19 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { ]; }, + _buildFeature: function(point1, point2, gradient) { + return { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [[point1.lng, point1.lat, point1.alt], [point2.lng, point2.lat, point2.alt]] + }, + properties: { + attributeType: gradient + } + }; + }, + /** * Map a gradient percentage to one of the levels defined * in options.mappings.gradient. From 0adc28d5df398c4fdec311282da4e464241e4579 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sun, 25 Oct 2020 22:08:46 -0700 Subject: [PATCH 09/20] normalize gradient --- js/plugin/Heightgraph.js | 124 +++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 17 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 71004c5..905edb0 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -12,47 +12,47 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { mappings: { gradient: { '-5': { - text: '', + text: '16%+', color: '#028306' }, '-4': { - text: '', + text: '10-15%', color: '#2AA12E' }, '-3': { - text: '', + text: '7-9%', color: '#53BF56' }, '-2': { - text: '', + text: '4-6%', color: '#7BDD7E' }, '-1': { - text: '', + text: '1-3%', color: '#A4FBA6' }, '0': { - text: '', + text: '0%', color: '#ffcc99' }, '1': { - text: '', + text: '1-3%', color: '#F29898' }, '2': { - text: '', + text: '4-6%', color: '#E07575' }, '3': { - text: '', + text: '7-9%', color: '#CF5352' }, '4': { - text: '', + text: '10-15%', color: '#BE312F' }, '5': { - text: '', + text: '16%+', color: '#AD0F0C' } } @@ -159,13 +159,97 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { * @param {LatLng[]} latLngs an array of LatLng objects, guaranteed to be not empty */ _buildGeojsonFeatures: function(latLngs) { + var self = this; + var features = []; - // this is going to be initialized in the first iteration + // this is going to be initialized on the first buffer flush var currentFeature; + // undefined is fine, as it will be different than the current gradient - // in the first iteration + // in the first buffer flush var previousGradient; + + // since the altitude coordinate on points is not very reliable, let's normalize it + // by averaging the gradient over several point (within the min distance defined below) + var buffer = []; + var bufferDistance = 0; + + // each subsequent feature starts with the last point on the previous features, + // and hence keep track of it + var lastFeaturePoint; + + // the minimum distance (in meters) between the points in the buffer; + // once reached, the buffer is flushed + var bufferMinDistance = 200; + // TODO calculate min distance based on the total route distance + + if (latLngs.length > 0) { + buffer.push(latLngs[0]); + } + for (var i = 1; i < latLngs.length; i++) { + buffer.push(latLngs[i]); // the buffer contains at least 2 points by now + bufferDistance = + bufferDistance + + // never negative + buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); + + console.log('point:', latLngs[i], 'bufferDistance:', bufferDistance); + // if we reached the tipping point, + // each point in the buffer gets the same gradient rating, + // and flush the buffer + if (bufferDistance >= bufferMinDistance) { + var altDelta = buffer[buffer.length - 1].alt - buffer[0].alt; + var currentGradientPercentage = (altDelta * 100) / bufferDistance; // never division by 0 + var currentGradient = self._mapGradient(currentGradientPercentage); + console.log('currentGradient:', currentGradient); + + if (currentGradient == previousGradient) { + // the gradient hasn't changed; flush into the last feature + console.log('adding points in buffer to the current feature:', buffer); + buffer.forEach(function(point) { + var coordinate = [point.lng, point.lat, point.alt]; + currentFeature.geometry.coordinates.push(coordinate); + }); + } else { + // the gradient has changed; flush into a new feature + currentFeature = self._buildFeature(buffer, currentGradient); + console.log('building new feature:', currentFeature); + features.push(currentFeature); + } + + // prepare for the next iteration + previousGradient = currentGradient; + lastFeaturePoint = buffer[buffer.length - 1]; // before clearing the buffer + // TODO this is wrong, as on line 209 and 249 it will be repeated + buffer = [lastFeaturePoint]; + bufferDistance = 0; + } + } + + // handle the remaining points in the buffer + if (typeof currentFeature === 'undefined') { + if (buffer.length > 1) { + // TODO remove duplication + // building a feature with the few points on the route + console.log('building a feature with the few points on the route'); + var altDelta = buffer[buffer.length - 1].alt - buffer[0].alt; + // bufferDistance as already initialized in the main for loop + var currentGradientPercentage = (altDelta * 100) / bufferDistance; // never division by 0 + var currentGradient = self._mapGradient(currentGradientPercentage); + + currentFeature = self._buildFeature(buffer, currentGradient); + features.push(currentFeature); + } + } else { + // adding a few more points to the last feature + console.log('adding a few more points to the last feature; point count:', buffer.length); + buffer.forEach(function(point) { + var coordinate = [point.lng, point.lat, point.alt]; + currentFeature.geometry.coordinates.push(coordinate); + }); + } + /* // each feature starts with the last point on the previous feature; // this will also take care of inserting the firstmost point // (latLngs[0]) at position 0 into the first feature in the list @@ -201,14 +285,14 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; currentFeature.geometry.coordinates.push(coordinate); } else { - currentFeature = this._buildFeature(previousPoint, currentPoint, currentGradient); + currentFeature = this._buildFeature([previousPoint, currentPoint], currentGradient); features.push(currentFeature); } // prepare for the next iteration previousGradient = currentGradient; } - +*/ return [ { type: 'FeatureCollection', @@ -222,12 +306,18 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { ]; }, - _buildFeature: function(point1, point2, gradient) { + _buildFeature: function(subsequentPoints, gradient) { + var coordinates = []; + subsequentPoints.forEach(function(point) { + coordinates.push([point.lng, point.lat, point.alt]); + }); + return { type: 'Feature', geometry: { type: 'LineString', - coordinates: [[point1.lng, point1.lat, point1.alt], [point2.lng, point2.lat, point2.alt]] + coordinates: coordinates + // coordinates: [[point1.lng, point1.lat, point1.alt], [point2.lng, point2.lat, point2.alt]] }, properties: { attributeType: gradient From 73ad89e7277b8d6cbda5950032a29993f5a7c71c Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 26 Oct 2020 19:55:55 -0700 Subject: [PATCH 10/20] improvements to gradient normalization --- js/plugin/Heightgraph.js | 111 ++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 905edb0..cba178b 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -12,23 +12,23 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { mappings: { gradient: { '-5': { - text: '16%+', + text: '- 16%+', color: '#028306' }, '-4': { - text: '10-15%', + text: '- 10-15%', color: '#2AA12E' }, '-3': { - text: '7-9%', + text: '- 7-9%', color: '#53BF56' }, '-2': { - text: '4-6%', + text: '- 4-6%', color: '#7BDD7E' }, '-1': { - text: '1-3%', + text: '- 1-3%', color: '#A4FBA6' }, '0': { @@ -117,23 +117,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { if (track && track.getLatLngs().length > 0) { var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); this.addData(geojsonFeatures); - // TODO - /* - var geojson = track.toGeoJSON(); - geojson.properties = { attributeType: 0 }; - var data = [ - { - type: 'FeatureCollection', - features: [geojson], - properties: { - Creator: 'OpenRouteService.org', - records: 1, - summary: 'gradient' - } - } - ]; - this.addData(data); - */ // re-add handlers if (layer) { @@ -163,11 +146,11 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var features = []; - // this is going to be initialized on the first buffer flush + // this is going to be initialized on the first buffer flush, no need to initialize now var currentFeature; // undefined is fine, as it will be different than the current gradient - // in the first buffer flush + // when the buffer is flushed for the first time var previousGradient; // since the altitude coordinate on points is not very reliable, let's normalize it @@ -180,9 +163,13 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var lastFeaturePoint; // the minimum distance (in meters) between the points in the buffer; - // once reached, the buffer is flushed - var bufferMinDistance = 200; - // TODO calculate min distance based on the total route distance + // once reached, the buffer is flushed; + // for short routes, make sure we still have enough of a distance to normalize over; + // for long routes, we can afford to normalized over a longer distance, + // hence increasing the accuracy + var totalDistance = self._calculateDistance(latLngs); + var bufferMinDistance = Math.max(totalDistance / 200, 200); + console.log('using buffer min distance:', bufferMinDistance); if (latLngs.length > 0) { buffer.push(latLngs[0]); @@ -197,20 +184,18 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { console.log('point:', latLngs[i], 'bufferDistance:', bufferDistance); // if we reached the tipping point, // each point in the buffer gets the same gradient rating, - // and flush the buffer + // and the buffer is flushed into the existing feature or a new one if (bufferDistance >= bufferMinDistance) { - var altDelta = buffer[buffer.length - 1].alt - buffer[0].alt; - var currentGradientPercentage = (altDelta * 100) / bufferDistance; // never division by 0 - var currentGradient = self._mapGradient(currentGradientPercentage); + var currentGradient = self._calculateGradient(buffer); console.log('currentGradient:', currentGradient); if (currentGradient == previousGradient) { - // the gradient hasn't changed; flush into the last feature + // the gradient hasn't changed, we can flush the buffer into the last feature; + // since the buffer contains, at index 0, + // the last point on the feature (it was pushed into it on buffer reset), + // add only points from index 1 onward console.log('adding points in buffer to the current feature:', buffer); - buffer.forEach(function(point) { - var coordinate = [point.lng, point.lat, point.alt]; - currentFeature.geometry.coordinates.push(coordinate); - }); + self._addPointsToFeature(currentFeature, buffer.slice(1)); } else { // the gradient has changed; flush into a new feature currentFeature = self._buildFeature(buffer, currentGradient); @@ -218,10 +203,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { features.push(currentFeature); } - // prepare for the next iteration + // reset to prepare for the next iteration previousGradient = currentGradient; lastFeaturePoint = buffer[buffer.length - 1]; // before clearing the buffer - // TODO this is wrong, as on line 209 and 249 it will be repeated buffer = [lastFeaturePoint]; bufferDistance = 0; } @@ -229,26 +213,23 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // handle the remaining points in the buffer if (typeof currentFeature === 'undefined') { + // no feature was build so far if (buffer.length > 1) { - // TODO remove duplication // building a feature with the few points on the route console.log('building a feature with the few points on the route'); - var altDelta = buffer[buffer.length - 1].alt - buffer[0].alt; - // bufferDistance as already initialized in the main for loop - var currentGradientPercentage = (altDelta * 100) / bufferDistance; // never division by 0 - var currentGradient = self._mapGradient(currentGradientPercentage); - + var currentGradient = self._calculateGradient(buffer); currentFeature = self._buildFeature(buffer, currentGradient); features.push(currentFeature); } } else { - // adding a few more points to the last feature + // adding the extra points to the last feature; + // since the buffer contains, at index 0, + // the last point on the feature (it was pushed into it on buffer reset), + // add only points from index 1 onward console.log('adding a few more points to the last feature; point count:', buffer.length); - buffer.forEach(function(point) { - var coordinate = [point.lng, point.lat, point.alt]; - currentFeature.geometry.coordinates.push(coordinate); - }); + self._addPointsToFeature(currentFeature, buffer.slice(1)); } + /* // each feature starts with the last point on the previous feature; // this will also take care of inserting the firstmost point @@ -261,7 +242,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var altDelta = currentPoint.alt - previousPoint.alt; var currentGradientPercentage = (altDelta * 100) / dist; var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); - // TODO console.log( 'gradient %:', currentGradientPercentage, @@ -306,10 +286,35 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { ]; }, - _buildFeature: function(subsequentPoints, gradient) { + _calculateDistance: function(latLngs) { + var distance = 0; + for (var i = 1; i < latLngs.length; i++) { + distance += latLngs[i].distanceTo(latLngs[i - 1]); // never negative + } + return distance; + }, + + _calculateGradient: function(latLngs) { + // the array is guaranteed to have 2+ elements + var altDelta = latLngs[latLngs.length - 1].alt - latLngs[0].alt; + var distance = this._calculateDistance(latLngs); + + var currentGradientPercentage = distance == 0 ? 0 : (altDelta * 100) / distance; + var currentGradient = this._mapGradient(currentGradientPercentage); + return currentGradient; + }, + + _addPointsToFeature: function(feature, latLngs) { + latLngs.forEach(function(point) { + var coordinate = [point.lng, point.lat, point.alt]; + feature.geometry.coordinates.push(coordinate); + }); + }, + + _buildFeature: function(latLngs, gradient) { var coordinates = []; - subsequentPoints.forEach(function(point) { - coordinates.push([point.lng, point.lat, point.alt]); + latLngs.forEach(function(latLng) { + coordinates.push([latLng.lng, latLng.lat, latLng.alt]); }); return { From 6b5912e16a36514dfccc9a9d01cd1e5cb8cfd659 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 26 Oct 2020 20:05:26 -0700 Subject: [PATCH 11/20] clean up; prod ready --- js/plugin/Heightgraph.js | 69 ++++++++-------------------------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index cba178b..db755f4 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -158,10 +158,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { var buffer = []; var bufferDistance = 0; - // each subsequent feature starts with the last point on the previous features, - // and hence keep track of it - var lastFeaturePoint; - // the minimum distance (in meters) between the points in the buffer; // once reached, the buffer is flushed; // for short routes, make sure we still have enough of a distance to normalize over; @@ -169,7 +165,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // hence increasing the accuracy var totalDistance = self._calculateDistance(latLngs); var bufferMinDistance = Math.max(totalDistance / 200, 200); - console.log('using buffer min distance:', bufferMinDistance); if (latLngs.length > 0) { buffer.push(latLngs[0]); @@ -181,32 +176,28 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // never negative buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); - console.log('point:', latLngs[i], 'bufferDistance:', bufferDistance); // if we reached the tipping point, // each point in the buffer gets the same gradient rating, // and the buffer is flushed into the existing feature or a new one if (bufferDistance >= bufferMinDistance) { var currentGradient = self._calculateGradient(buffer); - console.log('currentGradient:', currentGradient); if (currentGradient == previousGradient) { // the gradient hasn't changed, we can flush the buffer into the last feature; // since the buffer contains, at index 0, // the last point on the feature (it was pushed into it on buffer reset), // add only points from index 1 onward - console.log('adding points in buffer to the current feature:', buffer); self._addPointsToFeature(currentFeature, buffer.slice(1)); } else { // the gradient has changed; flush into a new feature currentFeature = self._buildFeature(buffer, currentGradient); - console.log('building new feature:', currentFeature); features.push(currentFeature); } // reset to prepare for the next iteration previousGradient = currentGradient; - lastFeaturePoint = buffer[buffer.length - 1]; // before clearing the buffer - buffer = [lastFeaturePoint]; + var lastPoint = buffer[buffer.length - 1]; // before clearing the buffer + buffer = [lastPoint]; bufferDistance = 0; } } @@ -216,7 +207,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // no feature was build so far if (buffer.length > 1) { // building a feature with the few points on the route - console.log('building a feature with the few points on the route'); var currentGradient = self._calculateGradient(buffer); currentFeature = self._buildFeature(buffer, currentGradient); features.push(currentFeature); @@ -226,53 +216,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // since the buffer contains, at index 0, // the last point on the feature (it was pushed into it on buffer reset), // add only points from index 1 onward - console.log('adding a few more points to the last feature; point count:', buffer.length); self._addPointsToFeature(currentFeature, buffer.slice(1)); } - /* - // each feature starts with the last point on the previous feature; - // this will also take care of inserting the firstmost point - // (latLngs[0]) at position 0 into the first feature in the list - for (var i = 1; i < latLngs.length; i++) { - var previousPoint = latLngs[i - 1]; - var currentPoint = latLngs[i]; - - var dist = currentPoint.distanceTo(previousPoint); // never negative - var altDelta = currentPoint.alt - previousPoint.alt; - var currentGradientPercentage = (altDelta * 100) / dist; - var currentGradient = dist == 0 ? 0 : this._mapGradient(currentGradientPercentage); - console.log( - 'gradient %:', - currentGradientPercentage, - '; gradient level:', - currentGradient, - '; dist:', - dist, - '; alt:', - altDelta, - '; previous point:', - previousPoint.lng, - previousPoint.lat, - previousPoint.alt, - '; current point:', - currentPoint.lng, - currentPoint.lat, - currentPoint.alt - ); - - if (currentGradient == previousGradient) { - var coordinate = [currentPoint.lng, currentPoint.lat, currentPoint.alt]; - currentFeature.geometry.coordinates.push(coordinate); - } else { - currentFeature = this._buildFeature([previousPoint, currentPoint], currentGradient); - features.push(currentFeature); - } - - // prepare for the next iteration - previousGradient = currentGradient; - } -*/ return [ { type: 'FeatureCollection', @@ -286,6 +232,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { ]; }, + /** + * Calculate the distance between all LatLng points in the given array. + */ _calculateDistance: function(latLngs) { var distance = 0; for (var i = 1; i < latLngs.length; i++) { @@ -294,6 +243,11 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { return distance; }, + /** + * Calculate the gradient between the first and last point in the LatLng array, + * and map it to a gradient level. + * The array must have at least 2 elements. + */ _calculateGradient: function(latLngs) { // the array is guaranteed to have 2+ elements var altDelta = latLngs[latLngs.length - 1].alt - latLngs[0].alt; @@ -304,6 +258,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { return currentGradient; }, + /** + * Add the given array of LatLng points to the end of the provided feature. + */ _addPointsToFeature: function(feature, latLngs) { latLngs.forEach(function(point) { var coordinate = [point.lng, point.lat, point.alt]; From dff71c5e44b890dbc8fb21fbc5e10ccb72b73b09 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 26 Oct 2020 22:34:49 -0700 Subject: [PATCH 12/20] clarify comment --- css/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/style.css b/css/style.css index da4a4dd..379b6e8 100644 --- a/css/style.css +++ b/css/style.css @@ -746,7 +746,7 @@ table.dataTable.display tbody tr:hover.selected { /* * Heightgraph customizations; - * since the gradient types are empty, hide the legend as it's useless; + * since the legend and the gradient types are in the way, hide them; * since there's only the gradient layer, hide the layer selector. */ .legend-hover { From 0005752ceeb268b7c30ee2391639daac2fd8f194 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 2 Nov 2020 21:14:11 -0800 Subject: [PATCH 13/20] Redo the Heightgraph patch as IIFE --- patches/leaflet.heightgraph+1.3.3.patch | 32 +++++++------------------ 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/patches/leaflet.heightgraph+1.3.3.patch b/patches/leaflet.heightgraph+1.3.3.patch index 40cd1ea..b4ec4ad 100644 --- a/patches/leaflet.heightgraph+1.3.3.patch +++ b/patches/leaflet.heightgraph+1.3.3.patch @@ -1,28 +1,14 @@ diff --git a/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js b/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js -index 122c9ac..1a966af 100644 +index 122c9ac..1048691 100644 --- a/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js +++ b/node_modules/leaflet.heightgraph/dist/L.Control.Heightgraph.js -@@ -1282,10 +1282,10 @@ function schedule(node, name, id, index, group, timing) { - group: group, // For context during callback. - on: emptyOn, - tween: emptyTween, -- time: timing.time, -- delay: timing.delay, -- duration: timing.duration, -- ease: timing.ease, -+ time: timing ? timing.time : 0, -+ delay: timing ? timing.delay : 0, -+ duration: timing ? timing.duration : 0, -+ ease: timing ? timing.ease : null, - timer: null, - state: CREATED - }); -@@ -3062,7 +3062,7 @@ function initRange(domain, range) { +@@ -1,3 +1,4 @@ ++(function() { + 'use strict'; - var prefix = "$"; + function _typeof(obj) { +@@ -5792,3 +5793,4 @@ var schemeSet3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9b --function Map() {} -+// function Map() {} - - Map.prototype = map.prototype = { - constructor: Map, + return L.Control.Heightgraph; + }, window); ++}()); From 6bb769c2da32df27f4d383c3a07209d4c9faaec7 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 2 Nov 2020 21:14:49 -0800 Subject: [PATCH 14/20] Handle undefined altitude coordinate on latLng points --- js/plugin/Heightgraph.js | 160 ++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index db755f4..cbc5052 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -143,81 +143,62 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { */ _buildGeojsonFeatures: function(latLngs) { var self = this; - - var features = []; - - // this is going to be initialized on the first buffer flush, no need to initialize now - var currentFeature; - - // undefined is fine, as it will be different than the current gradient - // when the buffer is flushed for the first time - var previousGradient; + // TODO set the alt to undefined on the first few points + // TODO set the alt to undefined on the last few points + // TODO set the alt to undefined on the first and last few points + // TODO set the alt to undefined on all but first point + // TODO set the alt to undefined on all but last point + // TODO set the alt to undefined on all points + // TODO set the alt to undefined on all between first and last points + // TODO set the alt to undefined on all between first and middle point, and on all between middle and last point // since the altitude coordinate on points is not very reliable, let's normalize it - // by averaging the gradient over several point (within the min distance defined below) - var buffer = []; - var bufferDistance = 0; + // by taking into account only the altitude on points at a given min distance - // the minimum distance (in meters) between the points in the buffer; - // once reached, the buffer is flushed; + // the minimum distance (in meters) between points which we consider + // for the purpose of calculating altitudes and gradients; + // consider 200 segments on the route, at least 200m long each // for short routes, make sure we still have enough of a distance to normalize over; // for long routes, we can afford to normalized over a longer distance, // hence increasing the accuracy var totalDistance = self._calculateDistance(latLngs); var bufferMinDistance = Math.max(totalDistance / 200, 200); - if (latLngs.length > 0) { - buffer.push(latLngs[0]); - } - for (var i = 1; i < latLngs.length; i++) { - buffer.push(latLngs[i]); // the buffer contains at least 2 points by now - bufferDistance = - bufferDistance + - // never negative - buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); + var segments = self._partitionByMinDistance(latLngs, bufferMinDistance); - // if we reached the tipping point, - // each point in the buffer gets the same gradient rating, - // and the buffer is flushed into the existing feature or a new one - if (bufferDistance >= bufferMinDistance) { - var currentGradient = self._calculateGradient(buffer); + var features = []; - if (currentGradient == previousGradient) { - // the gradient hasn't changed, we can flush the buffer into the last feature; - // since the buffer contains, at index 0, - // the last point on the feature (it was pushed into it on buffer reset), - // add only points from index 1 onward - self._addPointsToFeature(currentFeature, buffer.slice(1)); - } else { - // the gradient has changed; flush into a new feature - currentFeature = self._buildFeature(buffer, currentGradient); - features.push(currentFeature); - } + // this is going to be initialized in the first loop, no need to initialize now + var currentFeature; - // reset to prepare for the next iteration - previousGradient = currentGradient; - var lastPoint = buffer[buffer.length - 1]; // before clearing the buffer - buffer = [lastPoint]; - bufferDistance = 0; - } - } + // undefined is fine, as it will be different to the current gradient in the first loop + var previousGradient; - // handle the remaining points in the buffer - if (typeof currentFeature === 'undefined') { - // no feature was build so far - if (buffer.length > 1) { - // building a feature with the few points on the route - var currentGradient = self._calculateGradient(buffer); - currentFeature = self._buildFeature(buffer, currentGradient); + segments.forEach(function(segment) { + var currentGradient = self._calculateGradient(segment); + + if (typeof currentGradient === 'undefined') { + // not enough points on the segment to calculate the gradient + currentFeature = self._buildFeature(segment, currentGradient); + features.push(currentFeature); + } else if (currentGradient == previousGradient) { + // the gradient hasn't changed, we can append this segment to the last feature; + // since the segment contains, at index 0 the last point on the feature, + // add only points from index 1 onward + self._addPointsToFeature(currentFeature, segment.slice(1)); + } else { + // the gradient has changed; create a new feature + currentFeature = self._buildFeature(segment, currentGradient); features.push(currentFeature); } - } else { - // adding the extra points to the last feature; - // since the buffer contains, at index 0, - // the last point on the feature (it was pushed into it on buffer reset), - // add only points from index 1 onward - self._addPointsToFeature(currentFeature, buffer.slice(1)); - } + + // reset to prepare for the next iteration + previousGradient = currentGradient; + }); + // TODO at the end of 3rd render (breakpoint on line 203), feature 37 has undefined points; + // test with previous working version for errors in console + + // TODO when elevation profile is open, the toggle button should be blue, not gray return [ { @@ -232,6 +213,63 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { ]; }, + /** + * Given the list of latLng points, partition them into segments + * at least _minDistance__ meters long, + * where the first and last points always have a valid altitude. + * NOTE: Given that some of the given points might not have a valid altitude, + * the first point(s) in the first buffer, as well as the last point(s) + * in the last buffer, might not have a valid altitude. + */ + _partitionByMinDistance: function(latLngs, minDistance) { + var segments = []; + + // temporary buffer where we add points + // until the distance between them is at least minDistance + var buffer = []; + + // push all points up to (and including) the first one with a valid altitude + var index = 0; + for (; index < latLngs.length; index++) { + var latLng = latLngs[index]; + buffer.push(latLng); + if (typeof latLng.alt !== 'undefined') { + break; + } + } + + var bufferDistance = this._calculateDistance(buffer); + for (; index < latLngs.length; index++) { + var latLng = latLngs[index]; + buffer.push(latLng); // the buffer contains at least 2 points by now + bufferDistance = + bufferDistance + + // never negative + buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); + + // if we reached the tipping point, add the buffer to segments, then flush it; + // if this point doesn't have a valid alt, continue to the next one + if (bufferDistance >= minDistance && typeof latLng.alt !== 'undefined') { + segments.push(buffer); + // re-init the buffer with the last point from the previous buffer + buffer = [buffer[buffer.length - 1]]; + bufferDistance = 0; + } + } + + // if the buffer is not empty, add all points from it into the last segment + if (segments.length === 0) { + segments.push(buffer); + } else if (buffer.length > 0) { + var lastSegment = segments[segments.length - 1]; + buffer.forEach(function(p) { + lastSegment.push(p); + }); + } + + return segments; + }, + /** * Calculate the distance between all LatLng points in the given array. */ @@ -249,6 +287,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { * The array must have at least 2 elements. */ _calculateGradient: function(latLngs) { + // TODO what if .alt is undefined on the heading or trailing points // the array is guaranteed to have 2+ elements var altDelta = latLngs[latLngs.length - 1].alt - latLngs[0].alt; var distance = this._calculateDistance(latLngs); @@ -279,7 +318,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { geometry: { type: 'LineString', coordinates: coordinates - // coordinates: [[point1.lng, point1.lat, point1.alt], [point2.lng, point2.lat, point2.alt]] }, properties: { attributeType: gradient From 0fd13b0e8fc9dd06bf8d8745b0083075cc48c37f Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Mon, 2 Nov 2020 22:42:27 -0800 Subject: [PATCH 15/20] Testing... --- js/plugin/Heightgraph.js | 88 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index cbc5052..7114c5c 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -152,6 +152,22 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // TODO set the alt to undefined on all between first and last points // TODO set the alt to undefined on all between first and middle point, and on all between middle and last point +var mockLatLngs = []; +for (var i = 0; i < 15; i++) { + var p = latLngs[i]; + var alt = i >= 2 && i < 13 ? p.alt : undefined; + mockLatLngs.push(L.latLng(p.lng, p.lat, alt)); + console.log('' + mockLatLngs[i].lng + ', ' + mockLatLngs[i].lat + ', ' + mockLatLngs[i].alt); +} +latLngs = mockLatLngs; +/* +var j = '['; +for (var i = 0; i < latLngs.length; i++) j+=(i>0?',':'')+'{lng:'+latLngs[i].lng+',lat:'+latLngs[i].lat+',alt:'+latLngs[i].alt+'}'; +j+=']'; +console.log(j); +*/ + + // since the altitude coordinate on points is not very reliable, let's normalize it // by taking into account only the altitude on points at a given min distance @@ -162,7 +178,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // for long routes, we can afford to normalized over a longer distance, // hence increasing the accuracy var totalDistance = self._calculateDistance(latLngs); +console.log('totalDistance:', totalDistance); var bufferMinDistance = Math.max(totalDistance / 200, 200); +console.log('bufferMinDistance:', bufferMinDistance); var segments = self._partitionByMinDistance(latLngs, bufferMinDistance); @@ -176,27 +194,32 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { segments.forEach(function(segment) { var currentGradient = self._calculateGradient(segment); +console.log('currentGradient: ' + currentGradient); if (typeof currentGradient === 'undefined') { // not enough points on the segment to calculate the gradient currentFeature = self._buildFeature(segment, currentGradient); +console.log('new currentFeature for no gradient: ' + currentFeature); features.push(currentFeature); +console.log('features so far for no gradient: ' + features); } else if (currentGradient == previousGradient) { // the gradient hasn't changed, we can append this segment to the last feature; // since the segment contains, at index 0 the last point on the feature, // add only points from index 1 onward self._addPointsToFeature(currentFeature, segment.slice(1)); +console.log('existing currentFeature: ' + currentFeature); } else { // the gradient has changed; create a new feature currentFeature = self._buildFeature(segment, currentGradient); +console.log('new currentFeature: ' + currentFeature); features.push(currentFeature); +console.log('features so far for no gradient: ' + features); } // reset to prepare for the next iteration previousGradient = currentGradient; }); - // TODO at the end of 3rd render (breakpoint on line 203), feature 37 has undefined points; - // test with previous working version for errors in console +console.log('final features: ' + features); // TODO when elevation profile is open, the toggle button should be blue, not gray @@ -237,8 +260,10 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { break; } } +console.log('buffer init: ' + buffer); var bufferDistance = this._calculateDistance(buffer); +console.log('buffer distance init: ' + bufferDistance); for (; index < latLngs.length; index++) { var latLng = latLngs[index]; buffer.push(latLng); // the buffer contains at least 2 points by now @@ -246,16 +271,23 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { bufferDistance + // never negative buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); +console.log('buffer distance: ' + bufferDistance); +console.log('latLng.alt: ' + latLng.alt); // if we reached the tipping point, add the buffer to segments, then flush it; // if this point doesn't have a valid alt, continue to the next one if (bufferDistance >= minDistance && typeof latLng.alt !== 'undefined') { +console.log('wrapping up'); segments.push(buffer); +console.log('segments so far: ' + segments); // re-init the buffer with the last point from the previous buffer buffer = [buffer[buffer.length - 1]]; +console.log('buffer re-init: ' + buffer); bufferDistance = 0; } } +console.log('complete segments: ' + segments); +console.log('remaining buffer: ' + buffer); // if the buffer is not empty, add all points from it into the last segment if (segments.length === 0) { @@ -266,6 +298,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { lastSegment.push(p); }); } +console.log('final segments: ' + segments); return segments; }, @@ -284,16 +317,57 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { /** * Calculate the gradient between the first and last point in the LatLng array, * and map it to a gradient level. - * The array must have at least 2 elements. + * If less than 2 points have an altitude coordinate, the 0 gradient is returned. */ _calculateGradient: function(latLngs) { - // TODO what if .alt is undefined on the heading or trailing points - // the array is guaranteed to have 2+ elements - var altDelta = latLngs[latLngs.length - 1].alt - latLngs[0].alt; - var distance = this._calculateDistance(latLngs); +console.log('calculating gradient for latLngs: ' + latLngs); + if (latLngs.length < 2) { +console.log('too few points'); + return this._mapGradient(0); + } + + // find the index of the first point with a valid altitude + var firstIndex = -1; + for (var i = 0; i < latLngs.length; i++) { + if (typeof latLngs[i].alt !== 'undefined') { + firstIndex = i; + break; + } + } +console.log('firstIndex:', firstIndex); + // if no point with a valid altitude was found, there's not much to do here + if (firstIndex == -1) { + return this._mapGradient(0); + } + + // find the index of the last point with a valid altitude + var lastIndex = latLngs.length; + for (var i = latLngs.length - 1; i > firstIndex; i--) { + if (typeof latLngs[i].alt !== 'undefined') { + lastIndex = i; + break; + } + } +console.log('lastIndex:', lastIndex); + // if no point with a valid altitude was found between firstIndex and end of array, + // there's not much else to do + if (lastIndex == latLngs.length) { + return this._mapGradient(0); + } + + var altDelta = latLngs[lastIndex].alt - latLngs[firstIndex].alt; +console.log('altDelta:', altDelta); + + // calculate the distance only from firstIndex to lastIndex; + // points before or after don't have a valid altitude, + // hence they are not included in the gradient calculation + var distance = this._calculateDistance(latLngs.slice(firstIndex, lastIndex + 1)); +console.log('distance', distance); var currentGradientPercentage = distance == 0 ? 0 : (altDelta * 100) / distance; +console.log('currentGradientPercentage:', currentGradientPercentage); var currentGradient = this._mapGradient(currentGradientPercentage); +console.log('currentGradient:', currentGradient); return currentGradient; }, From dd3c9921e231b40501be582a83301caa0b8f60c9 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Tue, 3 Nov 2020 19:34:26 -0800 Subject: [PATCH 16/20] More testing... --- js/plugin/Heightgraph.js | 60 ++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 7114c5c..5dfdfba 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -143,19 +143,19 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { */ _buildGeojsonFeatures: function(latLngs) { var self = this; - // TODO set the alt to undefined on the first few points - // TODO set the alt to undefined on the last few points - // TODO set the alt to undefined on the first and last few points - // TODO set the alt to undefined on all but first point - // TODO set the alt to undefined on all but last point - // TODO set the alt to undefined on all points - // TODO set the alt to undefined on all between first and last points - // TODO set the alt to undefined on all between first and middle point, and on all between middle and last point + // TODO set the alt to undefined on the first few points + // TODO set the alt to undefined on the last few points + // TODO set the alt to undefined on the first and last few points + // TODO set the alt to undefined on all but first point + // TODO set the alt to undefined on all but last point + // TODO set the alt to undefined on all points + // TODO set the alt to undefined on all between first and last points + // TODO set the alt to undefined on all between first and middle point, and on all between middle and last point var mockLatLngs = []; -for (var i = 0; i < 15; i++) { +for (var i = 0; i < 5; i++) { var p = latLngs[i]; - var alt = i >= 2 && i < 13 ? p.alt : undefined; + var alt = i == 0 || i == 2 || i == 4 ? p.alt : undefined; mockLatLngs.push(L.latLng(p.lng, p.lat, alt)); console.log('' + mockLatLngs[i].lng + ', ' + mockLatLngs[i].lat + ', ' + mockLatLngs[i].alt); } @@ -201,7 +201,8 @@ console.log('currentGradient: ' + currentGradient); currentFeature = self._buildFeature(segment, currentGradient); console.log('new currentFeature for no gradient: ' + currentFeature); features.push(currentFeature); -console.log('features so far for no gradient: ' + features); +console.log('features so far for no gradient: '); +for (var k = 0; k < features.length; k++) console.log('feature: ' + features[k]); } else if (currentGradient == previousGradient) { // the gradient hasn't changed, we can append this segment to the last feature; // since the segment contains, at index 0 the last point on the feature, @@ -213,7 +214,8 @@ console.log('existing currentFeature: ' + currentFeature); currentFeature = self._buildFeature(segment, currentGradient); console.log('new currentFeature: ' + currentFeature); features.push(currentFeature); -console.log('features so far for no gradient: ' + features); +console.log('features so far for no gradient:'); +for (var k = 0; k < features.length; k++) console.log('feature: ' + features[k]); } // reset to prepare for the next iteration @@ -254,17 +256,23 @@ console.log('final features: ' + features); // push all points up to (and including) the first one with a valid altitude var index = 0; for (; index < latLngs.length; index++) { +console.log('testing point at index: ' + index); var latLng = latLngs[index]; buffer.push(latLng); if (typeof latLng.alt !== 'undefined') { break; } } -console.log('buffer init: ' + buffer); +console.log('buffer init: ' + buffer + ', index: ' + index); - var bufferDistance = this._calculateDistance(buffer); + // since the segments are used for gradient calculation (hence alt is needed), + // consider 0 length so far, + // for all points so far, except for the last one, don't have an altitude + var bufferDistance = 0; console.log('buffer distance init: ' + bufferDistance); - for (; index < latLngs.length; index++) { + // since index was already used, start at the next one + for (index = index + 1; index < latLngs.length; index++) { +console.log('using point at index: ' + index); var latLng = latLngs[index]; buffer.push(latLng); // the buffer contains at least 2 points by now bufferDistance = @@ -279,26 +287,30 @@ console.log('latLng.alt: ' + latLng.alt); if (bufferDistance >= minDistance && typeof latLng.alt !== 'undefined') { console.log('wrapping up'); segments.push(buffer); -console.log('segments so far: ' + segments); +console.log('segments so far:'); +for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); // re-init the buffer with the last point from the previous buffer buffer = [buffer[buffer.length - 1]]; console.log('buffer re-init: ' + buffer); bufferDistance = 0; } } -console.log('complete segments: ' + segments); +console.log('complete segments: '); +for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); console.log('remaining buffer: ' + buffer); - // if the buffer is not empty, add all points from it into the last segment + // if the buffer is not empty, add all points from it (except for the first one) + // to the last segment if (segments.length === 0) { segments.push(buffer); } else if (buffer.length > 0) { var lastSegment = segments[segments.length - 1]; - buffer.forEach(function(p) { - lastSegment.push(p); - }); + for (var i = 1; i < buffer.length; i++) { + lastSegment.push(buffer[i]); + }; } -console.log('final segments: ' + segments); +console.log('final segments:'); +for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); return segments; }, @@ -341,7 +353,7 @@ console.log('firstIndex:', firstIndex); } // find the index of the last point with a valid altitude - var lastIndex = latLngs.length; + var lastIndex = -1; for (var i = latLngs.length - 1; i > firstIndex; i--) { if (typeof latLngs[i].alt !== 'undefined') { lastIndex = i; @@ -351,7 +363,7 @@ console.log('firstIndex:', firstIndex); console.log('lastIndex:', lastIndex); // if no point with a valid altitude was found between firstIndex and end of array, // there's not much else to do - if (lastIndex == latLngs.length) { + if (lastIndex == -1) { return this._mapGradient(0); } From 0a67545e639b247bf529190c43ca3efdc4208caf Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Tue, 3 Nov 2020 19:39:08 -0800 Subject: [PATCH 17/20] Clean up logging --- js/plugin/Heightgraph.js | 61 ++-------------------------------------- 1 file changed, 2 insertions(+), 59 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 5dfdfba..d50a098 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -143,30 +143,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { */ _buildGeojsonFeatures: function(latLngs) { var self = this; - // TODO set the alt to undefined on the first few points - // TODO set the alt to undefined on the last few points - // TODO set the alt to undefined on the first and last few points - // TODO set the alt to undefined on all but first point - // TODO set the alt to undefined on all but last point - // TODO set the alt to undefined on all points - // TODO set the alt to undefined on all between first and last points - // TODO set the alt to undefined on all between first and middle point, and on all between middle and last point - -var mockLatLngs = []; -for (var i = 0; i < 5; i++) { - var p = latLngs[i]; - var alt = i == 0 || i == 2 || i == 4 ? p.alt : undefined; - mockLatLngs.push(L.latLng(p.lng, p.lat, alt)); - console.log('' + mockLatLngs[i].lng + ', ' + mockLatLngs[i].lat + ', ' + mockLatLngs[i].alt); -} -latLngs = mockLatLngs; -/* -var j = '['; -for (var i = 0; i < latLngs.length; i++) j+=(i>0?',':'')+'{lng:'+latLngs[i].lng+',lat:'+latLngs[i].lat+',alt:'+latLngs[i].alt+'}'; -j+=']'; -console.log(j); -*/ - // since the altitude coordinate on points is not very reliable, let's normalize it // by taking into account only the altitude on points at a given min distance @@ -178,9 +154,7 @@ console.log(j); // for long routes, we can afford to normalized over a longer distance, // hence increasing the accuracy var totalDistance = self._calculateDistance(latLngs); -console.log('totalDistance:', totalDistance); var bufferMinDistance = Math.max(totalDistance / 200, 200); -console.log('bufferMinDistance:', bufferMinDistance); var segments = self._partitionByMinDistance(latLngs, bufferMinDistance); @@ -194,34 +168,25 @@ console.log('bufferMinDistance:', bufferMinDistance); segments.forEach(function(segment) { var currentGradient = self._calculateGradient(segment); -console.log('currentGradient: ' + currentGradient); if (typeof currentGradient === 'undefined') { // not enough points on the segment to calculate the gradient currentFeature = self._buildFeature(segment, currentGradient); -console.log('new currentFeature for no gradient: ' + currentFeature); features.push(currentFeature); -console.log('features so far for no gradient: '); -for (var k = 0; k < features.length; k++) console.log('feature: ' + features[k]); } else if (currentGradient == previousGradient) { // the gradient hasn't changed, we can append this segment to the last feature; // since the segment contains, at index 0 the last point on the feature, // add only points from index 1 onward self._addPointsToFeature(currentFeature, segment.slice(1)); -console.log('existing currentFeature: ' + currentFeature); } else { // the gradient has changed; create a new feature currentFeature = self._buildFeature(segment, currentGradient); -console.log('new currentFeature: ' + currentFeature); features.push(currentFeature); -console.log('features so far for no gradient:'); -for (var k = 0; k < features.length; k++) console.log('feature: ' + features[k]); } // reset to prepare for the next iteration previousGradient = currentGradient; }); -console.log('final features: ' + features); // TODO when elevation profile is open, the toggle button should be blue, not gray @@ -256,48 +221,36 @@ console.log('final features: ' + features); // push all points up to (and including) the first one with a valid altitude var index = 0; for (; index < latLngs.length; index++) { -console.log('testing point at index: ' + index); var latLng = latLngs[index]; buffer.push(latLng); if (typeof latLng.alt !== 'undefined') { break; } } -console.log('buffer init: ' + buffer + ', index: ' + index); // since the segments are used for gradient calculation (hence alt is needed), // consider 0 length so far, // for all points so far, except for the last one, don't have an altitude var bufferDistance = 0; -console.log('buffer distance init: ' + bufferDistance); + // since index was already used, start at the next one for (index = index + 1; index < latLngs.length; index++) { -console.log('using point at index: ' + index); var latLng = latLngs[index]; buffer.push(latLng); // the buffer contains at least 2 points by now bufferDistance = bufferDistance + // never negative buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); -console.log('buffer distance: ' + bufferDistance); -console.log('latLng.alt: ' + latLng.alt); // if we reached the tipping point, add the buffer to segments, then flush it; // if this point doesn't have a valid alt, continue to the next one if (bufferDistance >= minDistance && typeof latLng.alt !== 'undefined') { -console.log('wrapping up'); segments.push(buffer); -console.log('segments so far:'); -for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); // re-init the buffer with the last point from the previous buffer buffer = [buffer[buffer.length - 1]]; -console.log('buffer re-init: ' + buffer); bufferDistance = 0; } } -console.log('complete segments: '); -for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); -console.log('remaining buffer: ' + buffer); // if the buffer is not empty, add all points from it (except for the first one) // to the last segment @@ -307,10 +260,8 @@ console.log('remaining buffer: ' + buffer); var lastSegment = segments[segments.length - 1]; for (var i = 1; i < buffer.length; i++) { lastSegment.push(buffer[i]); - }; + } } -console.log('final segments:'); -for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]); return segments; }, @@ -332,9 +283,7 @@ for (var k = 0; k < segments.length; k++) console.log('segment: ' + segments[k]) * If less than 2 points have an altitude coordinate, the 0 gradient is returned. */ _calculateGradient: function(latLngs) { -console.log('calculating gradient for latLngs: ' + latLngs); if (latLngs.length < 2) { -console.log('too few points'); return this._mapGradient(0); } @@ -346,7 +295,6 @@ console.log('too few points'); break; } } -console.log('firstIndex:', firstIndex); // if no point with a valid altitude was found, there's not much to do here if (firstIndex == -1) { return this._mapGradient(0); @@ -360,7 +308,6 @@ console.log('firstIndex:', firstIndex); break; } } -console.log('lastIndex:', lastIndex); // if no point with a valid altitude was found between firstIndex and end of array, // there's not much else to do if (lastIndex == -1) { @@ -368,18 +315,14 @@ console.log('lastIndex:', lastIndex); } var altDelta = latLngs[lastIndex].alt - latLngs[firstIndex].alt; -console.log('altDelta:', altDelta); // calculate the distance only from firstIndex to lastIndex; // points before or after don't have a valid altitude, // hence they are not included in the gradient calculation var distance = this._calculateDistance(latLngs.slice(firstIndex, lastIndex + 1)); -console.log('distance', distance); var currentGradientPercentage = distance == 0 ? 0 : (altDelta * 100) / distance; -console.log('currentGradientPercentage:', currentGradientPercentage); var currentGradient = this._mapGradient(currentGradientPercentage); -console.log('currentGradient:', currentGradient); return currentGradient; }, From f1d2855dcab4c0a0a7c3ad5b279a1b44c33a3b1a Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Wed, 4 Nov 2020 20:08:31 -0800 Subject: [PATCH 18/20] Add back the collapse/expand and resize improvements --- js/plugin/Heightgraph.js | 52 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index d50a098..81d0813 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -56,6 +56,10 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { color: '#AD0F0C' } } + }, + // extra options + shortcut: { + toggle: 69 // char code for 'e' } }, @@ -80,6 +84,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); + this.initCollapse(map); + var self = this; var container = $('#elevation-chart'); $(window).resize(function() { @@ -107,6 +114,41 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { this.update(); }, + initCollapse: function(map) { + var self = this; + var onHide = function() { + $('#elevation-btn').removeClass('active'); + // we must fetch tiles that are located behind elevation-chart + map._onResize(); + + if (this.id && BR.Util.localStorageAvailable() && !self.shouldRestoreChart) { + localStorage.removeItem(this.id); + } + }; + var onShow = function() { + $('#elevation-btn').addClass('active'); + + if (this.id && BR.Util.localStorageAvailable()) { + localStorage[this.id] = 'true'; + } + }; + // on page load, we want to restore collapse state from previous usage + $('#elevation-chart') + .on('hidden.bs.collapse', onHide) + .on('shown.bs.collapse', onShow) + .each(function() { + if (this.id && BR.Util.localStorageAvailable() && localStorage[this.id] === 'true') { + self.shouldRestoreChart = true; + } + }); + }, + + _keydownListener: function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggle) { + $('#elevation-btn').click(); + } + }, + update: function(track, layer) { // bring height indicator to front, because of track casing in BR.Routing if (this._mouseHeightFocus) { @@ -123,6 +165,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { layer.on('mousemove', this._mouseMoveHandlerBound); layer.on('mouseout', this._mouseoutHandlerBound); } + + if (this.shouldRestoreChart === true) $('#elevation-chart').collapse('show'); + this.shouldRestoreChart = undefined; } else { this._removeMarkedSegmentsOnMap(); this._resetDrag(); @@ -135,6 +180,11 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { layer.off('mousemove', this._mouseMoveHandlerBound); layer.off('mouseout', this._mouseoutHandlerBound); } + + if ($('#elevation-chart').hasClass('show')) { + this.shouldRestoreChart = true; + } + $('#elevation-chart').collapse('hide'); } }, @@ -188,8 +238,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { previousGradient = currentGradient; }); - // TODO when elevation profile is open, the toggle button should be blue, not gray - return [ { type: 'FeatureCollection', From d83fddd4edcbf3abcfb68d822102549099fa0f43 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Wed, 11 Nov 2020 13:22:05 -0800 Subject: [PATCH 19/20] Set elevation on points without it (workaround for bug in Leaflet.Heightgraph) --- js/plugin/Heightgraph.js | 168 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 159 insertions(+), 9 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 81d0813..ab16122 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -60,6 +60,14 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // extra options shortcut: { toggle: 69 // char code for 'e' + }, + patches: { + // whether or not to compensate for the bug in Leaflet.Heightgraph + // which makes the chart rendering to break if the track contains points + // without an elevation/altitude coordinate; + // if set to true, the elevation of these points will be inferred + // from the adjacent points which have it + inferElevation: true } }, @@ -219,14 +227,10 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { segments.forEach(function(segment) { var currentGradient = self._calculateGradient(segment); - if (typeof currentGradient === 'undefined') { - // not enough points on the segment to calculate the gradient - currentFeature = self._buildFeature(segment, currentGradient); - features.push(currentFeature); - } else if (currentGradient == previousGradient) { + if (currentGradient == previousGradient) { // the gradient hasn't changed, we can append this segment to the last feature; - // since the segment contains, at index 0 the last point on the feature, - // add only points from index 1 onward + // since the segment contains, at index 0, + // the last point on the current feature, add only points from index 1 onward self._addPointsToFeature(currentFeature, segment.slice(1)); } else { // the gradient has changed; create a new feature @@ -378,15 +382,17 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { * Add the given array of LatLng points to the end of the provided feature. */ _addPointsToFeature: function(feature, latLngs) { - latLngs.forEach(function(point) { + var latLngsWithElevation = this._inferElevation(latLngs); + latLngsWithElevation.forEach(function(point) { var coordinate = [point.lng, point.lat, point.alt]; feature.geometry.coordinates.push(coordinate); }); }, _buildFeature: function(latLngs, gradient) { + var latLngsWithElevation = this._inferElevation(latLngs); var coordinates = []; - latLngs.forEach(function(latLng) { + latLngsWithElevation.forEach(function(latLng) { coordinates.push([latLng.lng, latLng.lat, latLng.alt]); }); @@ -402,6 +408,147 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { }; }, + /** + * If the global flag is true, return a new list of LatLng points + * which all have the elevation coordinate set, + * so that the overall gradient profile of the point list stays the same. + * If there are points without elevation at the start or end of the given list, + * the elevation coordinate is set so that the gradient profile to the closest point + * with elevation is flat (i.e. the gradient is 0). + * If the global flag is false, the given list is returned. + */ + _inferElevation: function(latLngs) { + if (this.options.patches.inferElevation != true || latLngs.length === 0) { + return latLngs; + } + + var result = latLngs.slice(); + + this._inferElevationStart(result); + this._inferElevationEnd(result); + + // find points without elevation and set it + var previousIndex = -1; // keep track of the index of the previous point with elevation + for (var i = 0; i < result.length; i++) { + if (typeof result[i].alt !== 'undefined') { + previousIndex = i; + continue; + } + + // we've just found the first point in a potential series + + // if there is no previous point with elevation (which is unexpected), + // we're in a pickle, hence skip the current point + if (previousIndex == -1) { + continue; + } + + // look up the next point with ele + var nextIndex = i + 1; + for (; nextIndex < result.length; nextIndex++) { + if (typeof result[nextIndex].alt !== 'undefined') { + break; + } + } + // if we got to the end of list and haven't found a point with elevation + // (which is unexpected), we're in a pickle, hence skip the current point + if (nextIndex == result.length) { + continue; + } + + // fix the elevation on all points in the current series + this._inferElevationSublist(result, previousIndex + 1, nextIndex - 1); + + // finally, since we've fixed the current series, skip to the next point + i = nextIndex - 1; + } + + return result; + }, + + /* + * Check for points without elevation at the start of the list; + * if such points are found, set their elevation to be the same + * as the elevation on the first point with such coordinate. + * If no points in the list have an elevation coordinate, set it to 0 on all. + * Note that the given list of LatLng points is mutated. + */ + _inferElevationStart: function(latLngs) { + // if the list is empty, or the first point has elevation, there's nothing to do + if (latLngs.length == 0 || typeof latLngs[0].alt !== 'undefined') { + return; + } + + var index = 0; // the index of the first point with elevation + var alt = 0; // the elevation of the first point with such coordinate + for (; index < latLngs.length; index++) { + if (typeof latLngs[index].alt !== 'undefined') { + alt = latLngs[index].alt; + break; + } + } + // set the elevation on all points without it at the start of the list + for (var i = 0; i < index; i++) { + var point = latLngs[i]; + latLngs[i] = L.latLng(point.lat, point.lng, alt); + } + }, + + /* + * Check for points without elevation at the end of the list; + * if such points are found, set their elevation to be the same + * as the elevation on the last point with such coordinate. + * If no points in the list have an elevation coordinate, set it to 0 on all. + * Note that the given list of LatLng points is mutated. + */ + _inferElevationEnd: function(latLngs) { + // if the list is empty, or the last point has elevation, there's nothing to do + if (latLngs.length == 0 || typeof latLngs[latLngs.length - 1].alt !== 'undefined') { + return; + } + + var index = latLngs.length - 1; // the index of the last point with elevation + var alt = 0; // the elevation of the last point with such coordinate + for (; index >= 0; index--) { + if (typeof latLngs[index].alt !== 'undefined') { + alt = latLngs[index].alt; + break; + } + } + // set the elevation on all points without it at the end of the list + for (var i = index + 1; i < latLngs.length; i++) { + var point = latLngs[i]; + latLngs[i] = L.latLng(point.lat, point.lng, alt); + } + }, + + /** + * Given the list of LatLng points, + * and the series of points without elevation from startIndex to endIndex, + * set the elevation on these points so that the gradient from + * startIndex-1 to endIndex+1 is constant. + * startIndex must be preceeded by a point with elevation; + * endIndex must be followed by a point with elevation. + * Note that the given list of LatLng points is mutated. + */ + _inferElevationSublist: function(latLngs, startIndex, endIndex) { + var previousIndex = startIndex - 1; // index of the previous point with elevation before this series + var nextIndex = endIndex + 1; // index of the next point with elevation after this series + + // calculate the overall gradient for the current series + var distance = this._calculateDistance(latLngs.slice(previousIndex, nextIndex + 1)); + var altitudeDelta = latLngs[nextIndex].alt - latLngs[previousIndex].alt; + var gradient = distance == 0 ? 0 : (altitudeDelta * 100) / distance; + + // now fix the elevation on each point in the series, one by one + for (var i = startIndex; i <= endIndex; i++) { + var dist = latLngs[i].distanceTo(latLngs[i - 1]); + var alt = (gradient * dist) / 100 + latLngs[i - 1].alt; + var point = latLngs[i]; + latLngs[i] = L.latLng(point.lat, point.lng, Number(alt.toFixed(1))); + } + }, + /** * Map a gradient percentage to one of the levels defined * in options.mappings.gradient. @@ -429,6 +576,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { return 4; } else if (gradientPercentage >= 16) { return 5; + } else { + console.log('Unknown gradientPercentage: ', gradientPercentage, '; cannot map'); + return 0; } } }); From 63d080d14b01263c133d9932ad09a0e21d4b8085 Mon Sep 17 00:00:00 2001 From: alexcojocaru Date: Sun, 20 Dec 2020 20:51:58 -0800 Subject: [PATCH 20/20] Move the GeoJSON building functionality into separate module, and reference it --- js/plugin/Heightgraph.js | 398 +-------------------------------------- package.json | 5 +- yarn.lock | 15 +- 3 files changed, 18 insertions(+), 400 deletions(-) diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index ab16122..aea4348 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -60,14 +60,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // extra options shortcut: { toggle: 69 // char code for 'e' - }, - patches: { - // whether or not to compensate for the bug in Leaflet.Heightgraph - // which makes the chart rendering to break if the track contains points - // without an elevation/altitude coordinate; - // if set to true, the elevation of these points will be inferred - // from the adjacent points which have it - inferElevation: true } }, @@ -88,7 +80,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { // move elevation graph outside of the map setParent(this.getContainer(), document.getElementById('elevation-chart')); - // bind the the mouse move and mouse out handlers, I'll reuse them later on + // bind the mouse move and mouse out handlers, I'll reuse them later on this._mouseMoveHandlerBound = this.mapMousemoveHandler.bind(this); this._mouseoutHandlerBound = this._mouseoutHandler.bind(this); @@ -165,7 +157,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { } if (track && track.getLatLngs().length > 0) { - var geojsonFeatures = this._buildGeojsonFeatures(track.getLatLngs()); + var geojsonFeatures = geoDataExchange.buildGeojsonFeatures(track.getLatLngs()); this.addData(geojsonFeatures); // re-add handlers @@ -194,392 +186,6 @@ BR.Heightgraph = function(map, layersControl, routing, pois) { } $('#elevation-chart').collapse('hide'); } - }, - - /** - * @param {LatLng[]} latLngs an array of LatLng objects, guaranteed to be not empty - */ - _buildGeojsonFeatures: function(latLngs) { - var self = this; - - // since the altitude coordinate on points is not very reliable, let's normalize it - // by taking into account only the altitude on points at a given min distance - - // the minimum distance (in meters) between points which we consider - // for the purpose of calculating altitudes and gradients; - // consider 200 segments on the route, at least 200m long each - // for short routes, make sure we still have enough of a distance to normalize over; - // for long routes, we can afford to normalized over a longer distance, - // hence increasing the accuracy - var totalDistance = self._calculateDistance(latLngs); - var bufferMinDistance = Math.max(totalDistance / 200, 200); - - var segments = self._partitionByMinDistance(latLngs, bufferMinDistance); - - var features = []; - - // this is going to be initialized in the first loop, no need to initialize now - var currentFeature; - - // undefined is fine, as it will be different to the current gradient in the first loop - var previousGradient; - - segments.forEach(function(segment) { - var currentGradient = self._calculateGradient(segment); - - if (currentGradient == previousGradient) { - // the gradient hasn't changed, we can append this segment to the last feature; - // since the segment contains, at index 0, - // the last point on the current feature, add only points from index 1 onward - self._addPointsToFeature(currentFeature, segment.slice(1)); - } else { - // the gradient has changed; create a new feature - currentFeature = self._buildFeature(segment, currentGradient); - features.push(currentFeature); - } - - // reset to prepare for the next iteration - previousGradient = currentGradient; - }); - - return [ - { - type: 'FeatureCollection', - features: features, - properties: { - Creator: 'OpenRouteService.org', - records: features.length, - summary: 'gradient' - } - } - ]; - }, - - /** - * Given the list of latLng points, partition them into segments - * at least _minDistance__ meters long, - * where the first and last points always have a valid altitude. - * NOTE: Given that some of the given points might not have a valid altitude, - * the first point(s) in the first buffer, as well as the last point(s) - * in the last buffer, might not have a valid altitude. - */ - _partitionByMinDistance: function(latLngs, minDistance) { - var segments = []; - - // temporary buffer where we add points - // until the distance between them is at least minDistance - var buffer = []; - - // push all points up to (and including) the first one with a valid altitude - var index = 0; - for (; index < latLngs.length; index++) { - var latLng = latLngs[index]; - buffer.push(latLng); - if (typeof latLng.alt !== 'undefined') { - break; - } - } - - // since the segments are used for gradient calculation (hence alt is needed), - // consider 0 length so far, - // for all points so far, except for the last one, don't have an altitude - var bufferDistance = 0; - - // since index was already used, start at the next one - for (index = index + 1; index < latLngs.length; index++) { - var latLng = latLngs[index]; - buffer.push(latLng); // the buffer contains at least 2 points by now - bufferDistance = - bufferDistance + - // never negative - buffer[buffer.length - 1].distanceTo(buffer[buffer.length - 2]); - - // if we reached the tipping point, add the buffer to segments, then flush it; - // if this point doesn't have a valid alt, continue to the next one - if (bufferDistance >= minDistance && typeof latLng.alt !== 'undefined') { - segments.push(buffer); - // re-init the buffer with the last point from the previous buffer - buffer = [buffer[buffer.length - 1]]; - bufferDistance = 0; - } - } - - // if the buffer is not empty, add all points from it (except for the first one) - // to the last segment - if (segments.length === 0) { - segments.push(buffer); - } else if (buffer.length > 0) { - var lastSegment = segments[segments.length - 1]; - for (var i = 1; i < buffer.length; i++) { - lastSegment.push(buffer[i]); - } - } - - return segments; - }, - - /** - * Calculate the distance between all LatLng points in the given array. - */ - _calculateDistance: function(latLngs) { - var distance = 0; - for (var i = 1; i < latLngs.length; i++) { - distance += latLngs[i].distanceTo(latLngs[i - 1]); // never negative - } - return distance; - }, - - /** - * Calculate the gradient between the first and last point in the LatLng array, - * and map it to a gradient level. - * If less than 2 points have an altitude coordinate, the 0 gradient is returned. - */ - _calculateGradient: function(latLngs) { - if (latLngs.length < 2) { - return this._mapGradient(0); - } - - // find the index of the first point with a valid altitude - var firstIndex = -1; - for (var i = 0; i < latLngs.length; i++) { - if (typeof latLngs[i].alt !== 'undefined') { - firstIndex = i; - break; - } - } - // if no point with a valid altitude was found, there's not much to do here - if (firstIndex == -1) { - return this._mapGradient(0); - } - - // find the index of the last point with a valid altitude - var lastIndex = -1; - for (var i = latLngs.length - 1; i > firstIndex; i--) { - if (typeof latLngs[i].alt !== 'undefined') { - lastIndex = i; - break; - } - } - // if no point with a valid altitude was found between firstIndex and end of array, - // there's not much else to do - if (lastIndex == -1) { - return this._mapGradient(0); - } - - var altDelta = latLngs[lastIndex].alt - latLngs[firstIndex].alt; - - // calculate the distance only from firstIndex to lastIndex; - // points before or after don't have a valid altitude, - // hence they are not included in the gradient calculation - var distance = this._calculateDistance(latLngs.slice(firstIndex, lastIndex + 1)); - - var currentGradientPercentage = distance == 0 ? 0 : (altDelta * 100) / distance; - var currentGradient = this._mapGradient(currentGradientPercentage); - return currentGradient; - }, - - /** - * Add the given array of LatLng points to the end of the provided feature. - */ - _addPointsToFeature: function(feature, latLngs) { - var latLngsWithElevation = this._inferElevation(latLngs); - latLngsWithElevation.forEach(function(point) { - var coordinate = [point.lng, point.lat, point.alt]; - feature.geometry.coordinates.push(coordinate); - }); - }, - - _buildFeature: function(latLngs, gradient) { - var latLngsWithElevation = this._inferElevation(latLngs); - var coordinates = []; - latLngsWithElevation.forEach(function(latLng) { - coordinates.push([latLng.lng, latLng.lat, latLng.alt]); - }); - - return { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: coordinates - }, - properties: { - attributeType: gradient - } - }; - }, - - /** - * If the global flag is true, return a new list of LatLng points - * which all have the elevation coordinate set, - * so that the overall gradient profile of the point list stays the same. - * If there are points without elevation at the start or end of the given list, - * the elevation coordinate is set so that the gradient profile to the closest point - * with elevation is flat (i.e. the gradient is 0). - * If the global flag is false, the given list is returned. - */ - _inferElevation: function(latLngs) { - if (this.options.patches.inferElevation != true || latLngs.length === 0) { - return latLngs; - } - - var result = latLngs.slice(); - - this._inferElevationStart(result); - this._inferElevationEnd(result); - - // find points without elevation and set it - var previousIndex = -1; // keep track of the index of the previous point with elevation - for (var i = 0; i < result.length; i++) { - if (typeof result[i].alt !== 'undefined') { - previousIndex = i; - continue; - } - - // we've just found the first point in a potential series - - // if there is no previous point with elevation (which is unexpected), - // we're in a pickle, hence skip the current point - if (previousIndex == -1) { - continue; - } - - // look up the next point with ele - var nextIndex = i + 1; - for (; nextIndex < result.length; nextIndex++) { - if (typeof result[nextIndex].alt !== 'undefined') { - break; - } - } - // if we got to the end of list and haven't found a point with elevation - // (which is unexpected), we're in a pickle, hence skip the current point - if (nextIndex == result.length) { - continue; - } - - // fix the elevation on all points in the current series - this._inferElevationSublist(result, previousIndex + 1, nextIndex - 1); - - // finally, since we've fixed the current series, skip to the next point - i = nextIndex - 1; - } - - return result; - }, - - /* - * Check for points without elevation at the start of the list; - * if such points are found, set their elevation to be the same - * as the elevation on the first point with such coordinate. - * If no points in the list have an elevation coordinate, set it to 0 on all. - * Note that the given list of LatLng points is mutated. - */ - _inferElevationStart: function(latLngs) { - // if the list is empty, or the first point has elevation, there's nothing to do - if (latLngs.length == 0 || typeof latLngs[0].alt !== 'undefined') { - return; - } - - var index = 0; // the index of the first point with elevation - var alt = 0; // the elevation of the first point with such coordinate - for (; index < latLngs.length; index++) { - if (typeof latLngs[index].alt !== 'undefined') { - alt = latLngs[index].alt; - break; - } - } - // set the elevation on all points without it at the start of the list - for (var i = 0; i < index; i++) { - var point = latLngs[i]; - latLngs[i] = L.latLng(point.lat, point.lng, alt); - } - }, - - /* - * Check for points without elevation at the end of the list; - * if such points are found, set their elevation to be the same - * as the elevation on the last point with such coordinate. - * If no points in the list have an elevation coordinate, set it to 0 on all. - * Note that the given list of LatLng points is mutated. - */ - _inferElevationEnd: function(latLngs) { - // if the list is empty, or the last point has elevation, there's nothing to do - if (latLngs.length == 0 || typeof latLngs[latLngs.length - 1].alt !== 'undefined') { - return; - } - - var index = latLngs.length - 1; // the index of the last point with elevation - var alt = 0; // the elevation of the last point with such coordinate - for (; index >= 0; index--) { - if (typeof latLngs[index].alt !== 'undefined') { - alt = latLngs[index].alt; - break; - } - } - // set the elevation on all points without it at the end of the list - for (var i = index + 1; i < latLngs.length; i++) { - var point = latLngs[i]; - latLngs[i] = L.latLng(point.lat, point.lng, alt); - } - }, - - /** - * Given the list of LatLng points, - * and the series of points without elevation from startIndex to endIndex, - * set the elevation on these points so that the gradient from - * startIndex-1 to endIndex+1 is constant. - * startIndex must be preceeded by a point with elevation; - * endIndex must be followed by a point with elevation. - * Note that the given list of LatLng points is mutated. - */ - _inferElevationSublist: function(latLngs, startIndex, endIndex) { - var previousIndex = startIndex - 1; // index of the previous point with elevation before this series - var nextIndex = endIndex + 1; // index of the next point with elevation after this series - - // calculate the overall gradient for the current series - var distance = this._calculateDistance(latLngs.slice(previousIndex, nextIndex + 1)); - var altitudeDelta = latLngs[nextIndex].alt - latLngs[previousIndex].alt; - var gradient = distance == 0 ? 0 : (altitudeDelta * 100) / distance; - - // now fix the elevation on each point in the series, one by one - for (var i = startIndex; i <= endIndex; i++) { - var dist = latLngs[i].distanceTo(latLngs[i - 1]); - var alt = (gradient * dist) / 100 + latLngs[i - 1].alt; - var point = latLngs[i]; - latLngs[i] = L.latLng(point.lat, point.lng, Number(alt.toFixed(1))); - } - }, - - /** - * Map a gradient percentage to one of the levels defined - * in options.mappings.gradient. - */ - _mapGradient: function(gradientPercentage) { - if (gradientPercentage <= -16) { - return -5; - } else if (gradientPercentage > -16 && gradientPercentage <= -10) { - return -4; - } else if (gradientPercentage > -10 && gradientPercentage <= -7) { - return -3; - } else if (gradientPercentage > -7 && gradientPercentage <= -4) { - return -2; - } else if (gradientPercentage > -4 && gradientPercentage <= -1) { - return -1; - } else if (gradientPercentage > -1 && gradientPercentage < 1) { - return 0; - } else if (gradientPercentage >= 1 && gradientPercentage < 4) { - return 1; - } else if (gradientPercentage >= 4 && gradientPercentage < 7) { - return 2; - } else if (gradientPercentage >= 7 && gradientPercentage < 10) { - return 3; - } else if (gradientPercentage >= 10 && gradientPercentage < 16) { - return 4; - } else if (gradientPercentage >= 16) { - return 5; - } else { - console.log('Unknown gradientPercentage: ', gradientPercentage, '; cannot map'); - return 0; - } } }); diff --git a/package.json b/package.json index 0fcdac6..7bad1d8 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "codemirror": "^5.35.0", "datatables": "~1.10.18", "font-awesome": "^4.7.0", + "geo-data-exchange": "alexcojocaru/geo-data-exchange#v1.1.0", "i18next": "^15.0.4", "i18next-browser-languagedetector": "^3.0.1", "i18next-xhr-backend": "^2.0.1", @@ -109,8 +110,8 @@ "merge-stream": "^2.0.0", "node-fetch": "^2.6.1", "npmfiles": "^0.1.1", - "patch-package": "^6.2.2", - "postinstall-postinstall": "^2.1.0", + "patch-package": "~6.2.2", + "postinstall-postinstall": "~2.1.0", "prettier": "^1.17.1", "pretty-quick": "^1.10.0" }, diff --git a/yarn.lock b/yarn.lock index 56b74d3..5d4183b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3579,6 +3579,12 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +geo-data-exchange@alexcojocaru/geo-data-exchange#v1.1.0: + version "1.1.0" + resolved "https://codeload.github.com/alexcojocaru/geo-data-exchange/tar.gz/502d3c0180462be022dab7d470ac737f972c312b" + dependencies: + leaflet "^1.5.0" + geojson-equality@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/geojson-equality/-/geojson-equality-0.1.6.tgz#a171374ef043e5d4797995840bae4648e0752d72" @@ -5009,6 +5015,11 @@ leaflet@^1.0.1, leaflet@^1.3.4: resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.4.0.tgz#d5f56eeb2aa32787c24011e8be4c77e362ae171b" integrity sha512-x9j9tGY1+PDLN9pcWTx9/y6C5nezoTMB8BLK5jTakx+H7bPlnbCHfi9Hjg+Qt36sgDz/cb9lrSpNQXmk45Tvhw== +leaflet@^1.5.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19" + integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== + leaflet@^1.6.0, leaflet@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308" @@ -5976,7 +5987,7 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -patch-package@^6.2.2: +patch-package@~6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.2.2.tgz#71d170d650c65c26556f0d0fbbb48d92b6cc5f39" integrity sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg== @@ -6217,7 +6228,7 @@ postcss@^6.0.0, postcss@^6.0.23: source-map "^0.6.1" supports-color "^5.4.0" -postinstall-postinstall@^2.1.0: +postinstall-postinstall@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==