From acf9c5988856d98c299380a692ea87b2720ba304 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 16 Apr 2021 19:07:52 +0200 Subject: [PATCH 01/47] Initial take on supporting straight line segments --- js/index.js | 2 +- js/plugin/Routing.js | 6 ++++-- package.json | 2 +- yarn.lock | 6 +++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/js/index.js b/js/index.js index 8ef2fd3..be76e59 100644 --- a/js/index.js +++ b/js/index.js @@ -267,7 +267,7 @@ exportRoute = new BR.Export(router, pois, profile); - routing.on('routing:routeWaypointEnd routing:setWaypointsEnd', function (evt) { + routing.on('routing:routeWaypointEnd routing:setWaypointsEnd routing:rerouteSegmentEnd', function (evt) { search.clear(); onUpdate(evt && evt.err); }); diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 5f80e85..115fab2 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -185,7 +185,9 @@ BR.Routing = L.Routing.extend({ }, _addSegmentCasing: function (e) { - var casing = L.polyline(e.layer.getLatLngs(), this.options.styles.trackCasing); + // extend layer style to inherit beeline dashArray + const casingStyle = Object.assign({}, e.layer.options, this.options.styles.trackCasing); + const casing = L.polyline(e.layer.getLatLngs(), casingStyle); this._segmentsCasing.addLayer(casing); e.layer._casing = casing; this._segments.bringToFront(); @@ -308,7 +310,7 @@ BR.Routing = L.Routing.extend({ this._eachSegment(function (m1, m2, line) { // omit if null (still calculating) or error // NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line - if (line && line.feature) { + if (line && (line.feature || m1._routing.beeline)) { latLngs = latLngs.concat(line.getLatLngs()); } }); diff --git a/package.json b/package.json index d36326e..717edb0 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "leaflet-hotline": "^0.4.0", "leaflet-plugins": "~3.0.0", "leaflet-providers": "^1.10.2", - "leaflet-routing": "nrenner/leaflet-routing#e94e153", + "leaflet-routing": "nrenner/leaflet-routing#9fa0cd2", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", "leaflet.heightgraph": "nrenner/Leaflet.Heightgraph#0757b2a", diff --git a/yarn.lock b/yarn.lock index 8ec6618..55d8bfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7806,9 +7806,9 @@ leaflet-providers@^1.10.2: resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.10.2.tgz#763c8e6655f26caf1afe3a1ef4add6c3e32de663" integrity sha512-1l867LObxwuFBeyPeBewip8PAXKOnvEoujq4/9y2TKTiZNHH76ksBD6dfktGjgUrOF+IdjsGHkpASPE+v2DQLw== -leaflet-routing@nrenner/leaflet-routing#e94e153: - version "0.1.3" - resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/e94e153b7574510313cb0bfefcd8776edebf627e" +leaflet-routing@nrenner/leaflet-routing#9fa0cd2: + version "0.1.4-alpha" + resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/9fa0cd22984a206518c6e20b7a53255531763e88" leaflet-sidebar-v2@nrenner/leaflet-sidebar-v2#dev: version "3.0.2" From b4368c1f51bd6337fd9c333329863ecca180c988 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 29 Apr 2021 20:41:38 +0200 Subject: [PATCH 02/47] Toggle beeline mode and style beeline Restore removed key listeners of super class to avoid duplicate calls (36d8a20) --- config.template.js | 12 ++++++++++++ js/plugin/Routing.js | 20 ++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/config.template.js b/config.template.js index 09a97e6..28ea8d1 100644 --- a/config.template.js +++ b/config.template.js @@ -100,6 +100,18 @@ nodata: { color: 'darkred', }, + beeline: { + weight: 5, + dashArray: [1, 10], + color: 'magenta', + opacity: BR.conf.defaultOpacity, + }, + beelineTrailer: { + weight: 5, + dashArray: [1, 10], + opacity: 0.6, + color: 'magenta', + }, }; BR.conf.markerColors = { diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 115fab2..9e56d47 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -28,6 +28,11 @@ BR.Routing = L.Routing.extend({ draw: { enable: 68, // char code for 'd' disable: 27, // char code for 'ESC' + beelineMode: 66, // char code for 'b' + // char code for 'Shift', same key as `beelineModifierName` + beelineModifier: 16, + // modifier key to draw straight line on click [shiftKey|null] (others don't work everywhere) + beelineModifierName: 'shiftKey', }, reverse: 82, // char code for 'r' deleteLastPoint: 90, // char code for 'z' @@ -175,6 +180,11 @@ BR.Routing = L.Routing.extend({ this._draw ); + // remove listeners registered in super.onAdd, keys not working when map container lost focus + // (by navbar/sidebar interaction), use document instead + L.DomEvent.removeListener(this._container, 'keydown', this._keydownListener, this); + L.DomEvent.removeListener(this._container, 'keyup', this._keyupListener, this); + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); @@ -376,14 +386,16 @@ BR.Routing = L.Routing.extend({ this.reverse(); } else if (e.keyCode === this.options.shortcut.deleteLastPoint) { this.deleteLastPoint(); + } else if (e.keyCode === this.options.shortcut.draw.beelineMode) { + this.toggleBeelineDrawing(); + } else if (e.keyCode === this.options.shortcut.draw.beelineModifier) { + this._draw._setTrailerStyle(true); } }, _keyupListener: function (e) { - // Prevent Leaflet from triggering drawing a second time on keyup, - // since this is already done in _keydownListener - if (e.keyCode === this.options.shortcut.draw.enable) { - return; + if (e.keyCode === this.options.shortcut.draw.beelineModifier) { + this._draw._setTrailerStyle(false); } }, From 6ea972bcf8723952e73b09f962cc42ae895cdc82 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 30 Apr 2021 18:30:01 +0200 Subject: [PATCH 03/47] Avoid accidental zooms while drawing beeline --- js/plugin/Routing.js | 10 ++++++ js/util/ClickTolerantBoxZoom.js | 55 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 js/util/ClickTolerantBoxZoom.js diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 9e56d47..0bcb459 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -180,6 +180,16 @@ BR.Routing = L.Routing.extend({ this._draw ); + // avoid accidental shift-drag zooms while drawing beeline with shift-click + this._map.boxZoom.disable(); + this._map.addHandler('boxZoom', BR.ClickTolerantBoxZoom); + this._draw.on('enabled', function () { + this._map.boxZoom.tolerant = true; + }); + this._draw.on('disabled', function () { + this._map.boxZoom.tolerant = false; + }); + // remove listeners registered in super.onAdd, keys not working when map container lost focus // (by navbar/sidebar interaction), use document instead L.DomEvent.removeListener(this._container, 'keydown', this._keydownListener, this); diff --git a/js/util/ClickTolerantBoxZoom.js b/js/util/ClickTolerantBoxZoom.js new file mode 100644 index 0000000..dc5ee17 --- /dev/null +++ b/js/util/ClickTolerantBoxZoom.js @@ -0,0 +1,55 @@ +/** + * Avoids conflict between shift-click and shift-drag. + * Extends BoxZoom to support a small click tolerance like in Draggable and + * a larger drag tolerance as a "neutral zone" before starting with box zoom dragging, + * to avoid accidental zooms. + */ +BR.ClickTolerantBoxZoom = L.Map.BoxZoom.extend({ + clickTolerance: L.Draggable.prototype.options.clickTolerance, + // use more than clickTolerance before starting box zoom to surely avoid accidental zooms + dragTolerance: 15, + // flag to enable or disable click/drag tolerance, classic BoxZoom behaviour when false + tolerant: true, + + // "neutral zone", state between clickTolerance and dragTolerance, + // already signals dragging to map and thus prevents click + _preMoved: false, + + moved: function () { + return this._preMoved || this._moved; + }, + + _resetState: function () { + L.Map.BoxZoom.prototype._resetState.call(this); + this._preMoved = false; + }, + + _onMouseMove: function (e) { + if (!this._moved) { + const point = this._map.mouseEventToContainerPoint(e); + + // derived from L.Draggable._onMove + var offsetPoint = point.clone()._subtract(this._startPoint); + var offset = Math.abs(offsetPoint.x || 0) + Math.abs(offsetPoint.y || 0); + + if (this.tolerant && offset < this.dragTolerance) { + if (!this._preMoved && offset >= this.clickTolerance) { + this._preMoved = true; + } + + return; + } + } + + L.Map.BoxZoom.prototype._onMouseMove.call(this, e); + }, + + _onMouseUp: function (e) { + L.Map.BoxZoom.prototype._onMouseUp.call(this, e); + + if (!this._moved && this._preMoved) { + this._clearDeferredResetState(); + this._resetStateTimeout = setTimeout(L.Util.bind(this._resetState, this), 0); + } + }, +}); From b4fbae093f04dd4f91e11bd6f19f1f8ebb97cf5f Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 5 May 2021 22:25:10 +0200 Subject: [PATCH 04/47] Add beeline button --- css/style.css | 16 ++++++++++++++++ js/index.js | 38 +++++++++++++++++++++++++++++++++++++- locales/en.json | 1 + locales/keys.js | 1 + 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/css/style.css b/css/style.css index de47a03..d13c689 100644 --- a/css/style.css +++ b/css/style.css @@ -443,6 +443,22 @@ button.btn { line-height: 30px; } +button.activate-beeline-active, +button.deactivate-beeline-active { + transition-duration: 0.3s; +} +button.activate-beeline-active.disabled, +button.deactivate-beeline-active.disabled { + height: 0; + border-bottom-width: 0px; +} +.mdi.active { + fill: #2074b6; +} +.mdi { + width: 18px; +} + /* smaller tab height */ .nav > li > a { padding: 2px 15px; diff --git a/js/index.js b/js/index.js index be76e59..f19f122 100644 --- a/js/index.js +++ b/js/index.js @@ -83,6 +83,40 @@ ], }); + // https://github.com/Templarian/MaterialDesign/blob/d0b28330af6648ca4c50c14d55043d71f813b3ae/svg/vector-line.svg + // Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0), https://github.com/Templarian/MaterialDesign/blob/master/LICENSE + const svg = ` + + + `; + const beelineClickHandler = function (control) { + const enabled = routing.toggleBeelineDrawing(); + control.state(enabled ? 'deactivate-beeline' : 'activate-beeline'); + }; + const title = i18next.t('keyboard.generic-shortcut', { + action: '$t(map.toggle-beeline)', + key: 'B', + }); + const beelineButton = L.easyButton({ + states: [ + { + stateName: 'activate-beeline', + icon: svg.replace(' active', ''), + onClick: beelineClickHandler, + title: title, + }, + { + stateName: 'deactivate-beeline', + icon: svg, + onClick: beelineClickHandler, + title: title, + }, + ], + }); + map.on('routing:beeline-start', () => beelineButton.state('deactivate-beeline')); + map.on('routing:beeline-end', () => beelineButton.state('activate-beeline')); + var reverseRouteButton = L.easyButton( 'fa-random', function () { @@ -273,9 +307,11 @@ }); map.on('routing:draw-start', function () { drawButton.state('deactivate-draw'); + beelineButton.enable(); }); map.on('routing:draw-end', function () { drawButton.state('activate-draw'); + beelineButton.disable(); }); function onUpdate(err) { @@ -330,7 +366,7 @@ circlego.addTo(map); } - var buttons = [drawButton, reverseRouteButton, nogos.getButton()]; + var buttons = [drawButton, beelineButton, reverseRouteButton, nogos.getButton()]; if (circlego) buttons.push(circlego.getButton()); buttons.push(deletePointButton, deleteRouteButton); diff --git a/locales/en.json b/locales/en.json index cecdb83..f7b92dd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -154,6 +154,7 @@ "strava-biking": "Show Strava biking segments", "strava-running": "Show Strava running segments", "strava-shortcut": "{{action}}\n({{key}} key to toggle layer, click to reload for current area)", + "toggle-beeline": "Toggle straight line", "zoomInTitle": "Zoom in", "zoomOutTitle": "Zoom out" }, diff --git a/locales/keys.js b/locales/keys.js index ceba32f..8d1f07b 100644 --- a/locales/keys.js +++ b/locales/keys.js @@ -13,6 +13,7 @@ i18next.t('map.draw-poi-start'); i18next.t('map.draw-poi-stop'); i18next.t('map.draw-route-start'); i18next.t('map.draw-route-stop'); +i18next.t('map.toggle-beeline'); i18next.t('map.geocoder'); i18next.t('map.locate-me'); i18next.t('map.nogo.cancel'); From 2cd233f70d0605dcb4832095e1d0b2be41f7053f Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 19 May 2021 21:09:29 +0200 Subject: [PATCH 05/47] Update Leaflet to 1.7.1 --- js/Map.js | 2 -- package.json | 2 +- yarn.lock | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/js/Map.js b/js/Map.js index 2995a36..08c4f97 100644 --- a/js/Map.js +++ b/js/Map.js @@ -17,8 +17,6 @@ BR.Map = { worldCopyJump: true, minZoom: 0, maxZoom: maxZoom, - // fix for route drag on mobile (#285), until next Leaflet version released (> 1.6.0) - tap: false, }); if (BR.Util.getResponsiveBreakpoint() >= '3md') { diff --git a/package.json b/package.json index 717edb0..fd22ed3 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "jquery": "3.5.1", "jquery-i18next": "^1.2.1", "jstree": "^3.3.8", - "leaflet": "~1.6.0", + "leaflet": "~1.7.1", "leaflet-control-geocoder": "^2.2.0", "leaflet-easybutton": "*", "leaflet-editable": "^1.1.0", diff --git a/yarn.lock b/yarn.lock index 55d8bfd..ae2d527 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7861,12 +7861,12 @@ 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: +leaflet@^1.5.0, leaflet@~1.7.1: 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: +leaflet@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308" integrity sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ== From 47f3a06be6127e2bdf16e753105b4905cb5efd96 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 20 May 2021 13:11:18 +0200 Subject: [PATCH 06/47] Improve route interactivity by using canvas --- js/Map.js | 5 +++++ js/plugin/Heightgraph.js | 10 ++++++++++ js/plugin/Routing.js | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/js/Map.js b/js/Map.js index 08c4f97..dbcb69c 100644 --- a/js/Map.js +++ b/js/Map.js @@ -12,7 +12,12 @@ BR.Map = { var maxZoom = 19; + if (BR.Browser.touch) { + L.Draggable.prototype.options.clickTolerance = 10; + } + map = new L.Map('map', { + renderer: L.canvas({ tolerance: BR.Browser.touch ? 10 : 5 }), zoomControl: false, // add it manually so that we can translate it worldCopyJump: true, minZoom: 0, diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 715ee3f..8cc1660 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -63,6 +63,16 @@ BR.Heightgraph = function (map, layersControl, routing, pois) { }, }, + onAdd(map) { + // As we're using canvas, initialize an `svg` root that is needed for map marker, + // see `Heightgraph._showMapMarker` + if (!document.querySelector('.leaflet-overlay-pane svg')) { + L.svg().addTo(map); + } + + return L.Control.Heightgraph.prototype.onAdd.call(this, map); + }, + addBelow: function (map) { // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 // this.width($('#map').outerWidth()); diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 0bcb459..63665b4 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -207,7 +207,7 @@ BR.Routing = L.Routing.extend({ _addSegmentCasing: function (e) { // extend layer style to inherit beeline dashArray const casingStyle = Object.assign({}, e.layer.options, this.options.styles.trackCasing); - const casing = L.polyline(e.layer.getLatLngs(), casingStyle); + const casing = L.polyline(e.layer.getLatLngs(), Object.assign({}, casingStyle, { interactive: false })); this._segmentsCasing.addLayer(casing); e.layer._casing = casing; this._segments.bringToFront(); From 91108bc334165ec4d9582fb029f0bf2af28c2841 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 20 May 2021 21:02:31 +0200 Subject: [PATCH 07/47] Fix hidden routing marker still being interactive --- js/plugin/Routing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 63665b4..78a5577 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -70,7 +70,7 @@ BR.Routing = L.Routing.extend({ return; } - this._mouseMarker.setOpacity(0.0); + this._hideMouseMarker(); this._map.off('mousemove', this._segmentOnMousemove, this); this._suspended = true; }, From 84a69e0af1c6b1e36d4c1858470707c67e6547f0 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 21 May 2021 09:17:33 +0200 Subject: [PATCH 08/47] Patch Leaflet drag offset bug --- js/util/LeafletPatches.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 js/util/LeafletPatches.js diff --git a/js/util/LeafletPatches.js b/js/util/LeafletPatches.js new file mode 100644 index 0000000..bfdae9c --- /dev/null +++ b/js/util/LeafletPatches.js @@ -0,0 +1,16 @@ +// Fixes wrong added offset when dragging, which can leave mouse off the marker +// after dragging and cause a map click +// see https://github.com/Leaflet/Leaflet/pull/7446 +// see https://github.com/Leaflet/Leaflet/issues/4457 +L.Draggable.prototype._onMoveOrig = L.Draggable.prototype._onMove; +L.Draggable.prototype._onMove = function (e) { + var start = !this._moved; + + this._onMoveOrig.call(this, e); + + if (start && this._moved) { + var offset = this._newPos.subtract(this._startPos); + this._startPos = this._startPos.add(offset); + this._newPos = this._newPos.add(offset); + } +}; From 7c1347668da909e1a23337cd4ab645af92c92564 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 21 May 2021 17:43:52 +0200 Subject: [PATCH 09/47] Prevent trailer showing on beeline button clicks --- js/plugin/Routing.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 78a5577..8ddb57c 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -146,22 +146,27 @@ BR.Routing = L.Routing.extend({ this._show(); } } - function hide() { + var hide = function () { if (!this._hidden && this._parent._waypoints._first) { this._hide(); } + }.bind(this._draw); + function hideOverControl(e) { + hide(); + // prevent showing trailer when clicking state buttons (causes event that propagates to map) + L.DomEvent.stopPropagation(e); } this._draw.on('enabled', function () { this._map.on('mouseout', hide, this); this._map.on('mouseover', show, this); L.DomEvent.on(this._map._controlContainer, 'mouseout', show, this); - L.DomEvent.on(this._map._controlContainer, 'mouseover', hide, this); + L.DomEvent.on(this._map._controlContainer, 'mouseover', hideOverControl, this); }); this._draw.on('disabled', function () { this._map.off('mouseout', hide, this); this._map.off('mouseover', show, this); L.DomEvent.off(this._map._controlContainer, 'mouseout', show, this); - L.DomEvent.off(this._map._controlContainer, 'mouseover', hide, this); + L.DomEvent.off(this._map._controlContainer, 'mouseover', hideOverControl, this); }); // Call show after deleting last waypoint, but hide trailer. From 9901acdf6aad8b164c6ee1ac9e8a7e927393f280 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 27 May 2021 19:46:02 +0200 Subject: [PATCH 10/47] Update leaflet-routing to alpha.2 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fd22ed3..73a85e4 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "leaflet-hotline": "^0.4.0", "leaflet-plugins": "~3.0.0", "leaflet-providers": "^1.10.2", - "leaflet-routing": "nrenner/leaflet-routing#9fa0cd2", + "leaflet-routing": "nrenner/leaflet-routing#a8213a4", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", "leaflet.heightgraph": "nrenner/Leaflet.Heightgraph#0757b2a", diff --git a/yarn.lock b/yarn.lock index ae2d527..60187c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7806,9 +7806,9 @@ leaflet-providers@^1.10.2: resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.10.2.tgz#763c8e6655f26caf1afe3a1ef4add6c3e32de663" integrity sha512-1l867LObxwuFBeyPeBewip8PAXKOnvEoujq4/9y2TKTiZNHH76ksBD6dfktGjgUrOF+IdjsGHkpASPE+v2DQLw== -leaflet-routing@nrenner/leaflet-routing#9fa0cd2: - version "0.1.4-alpha" - resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/9fa0cd22984a206518c6e20b7a53255531763e88" +leaflet-routing@nrenner/leaflet-routing#a8213a4: + version "0.1.4-alpha.2" + resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/a8213a4089c8be24a725a8f575880b0c20f64e54" leaflet-sidebar-v2@nrenner/leaflet-sidebar-v2#dev: version "3.0.2" From eb8492e30d7ef5fb0115ad3b0f419a1e4e5b3e45 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 3 Jun 2021 20:30:08 +0200 Subject: [PATCH 11/47] Fix loading trailer animation --- js/plugin/Routing.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 8ddb57c..8a4aee1 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -45,6 +45,7 @@ BR.Routing = L.Routing.extend({ this._segmentsCasing = new L.FeatureGroup().addTo(map); this._loadingTrailerGroup = new L.FeatureGroup().addTo(map); + this._loadingTrailerRenderer = L.svg(); // CSS animation based on SVG path element var container = L.Routing.prototype.onAdd.call(this, map); @@ -358,6 +359,7 @@ BR.Routing = L.Routing.extend({ opacity: this.options.styles.trailer.opacity, dashArray: [10, 10], className: 'loading-trailer', + renderer: this._loadingTrailerRenderer, }); this._loadingTrailerGroup.addLayer(loadingTrailer); } From cafb87cc6eba6181f74aef57895b0c374b7e03db Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 9 Jun 2021 17:04:11 +0200 Subject: [PATCH 12/47] Add basic beeline support by dummy feature --- js/plugin/Routing.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 8a4aee1..78782f5 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -448,4 +448,25 @@ BR.Routing = L.Routing.extend({ this._map.addLayer(this._distanceMarkers); } }, + + createBeeline: function (latLng1, latLng2) { + const layer = L.Routing.prototype.createBeeline.call(this, latLng1, latLng2); + const props = { + cost: 0, + 'filtered ascend': 0, + 'plain-ascend': 0, + 'total-energy': 0, + 'total-time': 0, + 'track-length': 0, + messages: [], + }; + layer.feature = turf.lineString( + [ + [latLng1.lng, latLng1.lat], + [latLng2.lng, latLng2.lat], + ], + props + ); + return layer; + }, }); From c3db03d1e35ba327544524b93d1ed145e6cfffb3 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 11 Jun 2021 12:01:10 +0200 Subject: [PATCH 13/47] Keep ele when removing duplicates on concat --- js/control/Export.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/js/control/Export.js b/js/control/Export.js index 5ec4a5e..54904b4 100644 --- a/js/control/Export.js +++ b/js/control/Export.js @@ -284,8 +284,15 @@ BR.Export._concatTotalTrack = function (segments) { let featureCoordinates = feature.geometry.coordinates; if (segmentIndex > 0) { - // remove first segment coordinate, same as previous last - featureCoordinates = featureCoordinates.slice(1); + // remove duplicate coordinate: first segment coordinate same as previous last, + // remove the one without ele value (e.g. beeline) + const prevLast = coordinates[coordinates.length - 1]; + const first = featureCoordinates[0]; + if (prevLast.length < first.length) { + coordinates.pop(); + } else { + featureCoordinates = featureCoordinates.slice(1); + } } coordinates = coordinates.concat(featureCoordinates); } From da2043fb474f00c90110f3527bde30b707bc930d Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Sat, 12 Jun 2021 11:11:08 +0200 Subject: [PATCH 14/47] Break after self-closing trkpt without ele (beeline) --- js/format/Xml.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/format/Xml.js b/js/format/Xml.js index 93efdcd..d52a147 100644 --- a/js/format/Xml.js +++ b/js/format/Xml.js @@ -25,7 +25,11 @@ BR.Xml = { } } else { if (singleLineTagList.includes(tag)) { - singleLineTag = tag; + const closeIndex = xml.indexOf('>', match.index + 1); + const selfClosing = xml.charAt(closeIndex - 1) === '/'; + if (!selfClosing) { + singleLineTag = tag; + } } let endIndex = match.index + 1; lines.push(xml.substring(startIndex, endIndex)); From f3d48dc63e5d6d4bc4e881302b9f1fb09bc5493a Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Sat, 12 Jun 2021 11:19:57 +0200 Subject: [PATCH 15/47] Add message headings to fix error in data tab --- js/plugin/Routing.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 78782f5..9a0810b 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -458,7 +458,21 @@ BR.Routing = L.Routing.extend({ 'total-energy': 0, 'total-time': 0, 'track-length': 0, - messages: [], + messages: [ + [ + 'Longitude', + 'Latitude', + 'Elevation', + 'Distance', + 'CostPerKm', + 'ElevCost', + 'TurnCost', + 'NodeCost', + 'InitialCost', + 'WayTags', + 'NodeTags', + ], + ], }; layer.feature = turf.lineString( [ From 3c8be960856f1e8525fbbaed9baba772eba57a69 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Sat, 19 Jun 2021 16:23:01 +0200 Subject: [PATCH 16/47] Support beelines in hash url (first stab) --- js/control/Export.js | 10 +++++++++- js/index.js | 10 ++++++++-- js/plugin/RouteLoaderConverter.js | 2 +- js/plugin/Routing.js | 5 +++-- js/router/BRouter.js | 33 +++++++++++++++++++++++++++---- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/js/control/Export.js b/js/control/Export.js index 54904b4..05a694d 100644 --- a/js/control/Export.js +++ b/js/control/Export.js @@ -74,7 +74,15 @@ BR.Export = L.Class.extend({ link.download = (name || 'brouter') + '.' + format; link.click(); } else { - var uri = this.router.getUrl(this.latLngs, this.pois.getMarkers(), null, format, nameUri, includeWaypoints); + var uri = this.router.getUrl( + this.latLngs, + null, + this.pois.getMarkers(), + null, + format, + nameUri, + includeWaypoints + ); var evt = document.createEvent('MouseEvents'); evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); var link = document.createElement('a'); diff --git a/js/index.js b/js/index.js index f19f122..5299db6 100644 --- a/js/index.js +++ b/js/index.js @@ -432,7 +432,7 @@ if (opts.lonlats) { routing.draw(false); routing.clear(); - routing.setWaypoints(opts.lonlats); + routing.setWaypoints(opts.lonlats, opts.beelineFlags); } if (opts.pois) { pois.setMarkers(opts.pois); @@ -455,7 +455,13 @@ // this callback is used to append anything in URL after L.Hash wrote #map=zoom/lat/lng/layer urlHash.additionalCb = function () { var url = router - .getUrl(routing.getWaypoints(), pois.getMarkers(), circlego ? circlego.getCircle() : null, null) + .getUrl( + routing.getWaypoints(), + routing.getBeelineFlags(), + pois.getMarkers(), + circlego ? circlego.getCircle() : null, + null + ) .substr('brouter?'.length + 1); // by default brouter use | as separator. To make URL more human-readable, we remplace them with ; for users diff --git a/js/plugin/RouteLoaderConverter.js b/js/plugin/RouteLoaderConverter.js index 53967c7..1be7cad 100644 --- a/js/plugin/RouteLoaderConverter.js +++ b/js/plugin/RouteLoaderConverter.js @@ -341,7 +341,7 @@ BR.routeLoader = function (map, layersControl, routing, pois) { } if (routingPoints.length > 0) { - routing.setWaypoints(routingPoints, function (event) { + routing.setWaypoints(routingPoints, null, function (event) { if (!event) return; var err = event.error; BR.message.showError( diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 9a0810b..7bdeda6 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -290,7 +290,7 @@ BR.Routing = L.Routing.extend({ } }, - setWaypoints: function (latLngs, cb) { + setWaypoints: function (latLngs, beelineFlags, cb) { var i; var callbackCount = 0; var firstErr; @@ -319,7 +319,8 @@ BR.Routing = L.Routing.extend({ this._loadingTrailerGroup._map = null; for (i = 0; latLngs && i < latLngs.length; i++) { - this.addWaypoint(latLngs[i], this._waypoints._last, null, callback); + const beeline = beelineFlags && i < beelineFlags.length ? beelineFlags[i] : null; + this.addWaypoint(latLngs[i], beeline, this._waypoints._last, null, callback); } this._loadingTrailerGroup._map = this._map; diff --git a/js/router/BRouter.js b/js/router/BRouter.js index 728f716..1bcf5d6 100644 --- a/js/router/BRouter.js +++ b/js/router/BRouter.js @@ -42,10 +42,15 @@ L.BRouter = L.Class.extend({ L.setOptions(this, options); }, - getUrlParams: function (latLngs, pois, circlego, format) { + getUrlParams: function (latLngs, beelineFlags, pois, circlego, format) { params = {}; if (this._getLonLatsString(latLngs) != null) params.lonlats = this._getLonLatsString(latLngs); + if (beelineFlags && beelineFlags.length > 0) { + const beelineString = this._getBeelineString(beelineFlags); + if (beelineString.length > 0) params.straight = beelineString; + } + if (this.options.nogos && this._getNogosString(this.options.nogos).length > 0) params.nogos = this._getNogosString(this.options.nogos); @@ -84,6 +89,9 @@ L.BRouter = L.Class.extend({ if (params.lonlats) { opts.lonlats = this._parseLonLats(params.lonlats); } + if (params.straight) { + opts.beelineFlags = this._parseBeelines(params.straight); + } if (params.nogos) { opts.nogos = this._parseNogos(params.nogos); } @@ -117,11 +125,12 @@ L.BRouter = L.Class.extend({ return opts; }, - getUrl: function (latLngs, pois, circlego, format, trackname, exportWaypoints) { - var urlParams = this.getUrlParams(latLngs, pois, circlego, format); + getUrl: function (latLngs, beelineFlags, pois, circlego, format, trackname, exportWaypoints) { + var urlParams = this.getUrlParams(latLngs, beelineFlags, pois, circlego, format); var args = []; if (urlParams.lonlats != null && urlParams.lonlats.length > 0) args.push(L.Util.template('lonlats={lonlats}', urlParams)); + if (urlParams.straight != null) args.push(L.Util.template('straight={straight}', urlParams)); if (urlParams.pois != null && urlParams.pois.length > 0) args.push(L.Util.template('pois={pois}', urlParams)); if (urlParams.circlego != null) args.push(L.Util.template('ringgo={circlego}', urlParams)); if (urlParams.nogos != null) args.push(L.Util.template('nogos={nogos}', urlParams)); @@ -144,7 +153,7 @@ L.BRouter = L.Class.extend({ }, getRoute: function (latLngs, cb) { - var url = this.getUrl(latLngs, null, null, 'geojson'), + var url = this.getUrl(latLngs, null, null, null, 'geojson'), xhr = new XMLHttpRequest(); if (!url) { @@ -305,6 +314,22 @@ L.BRouter = L.Class.extend({ return lonlats; }, + _getBeelineString: function (beelineFlags) { + var s = ''; + for (var i = 0; i < beelineFlags.length; i++) { + s += beelineFlags[i] ? '1' : '0'; + } + return s; + }, + + _parseBeelines: function (s) { + const beelineFlags = []; + for (const c of s) { + beelineFlags.push(!!+c); + } + return beelineFlags; + }, + _getLonLatsNameString: function (latLngNames) { var s = ''; for (var i = 0; i < latLngNames.length; i++) { From 044dab0f80ea262c0508d60049b6712fd568f3e0 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 23 Jun 2021 23:23:36 +0200 Subject: [PATCH 17/47] Encode beeline hash as indexes --- js/router/BRouter.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/js/router/BRouter.js b/js/router/BRouter.js index 1bcf5d6..5438744 100644 --- a/js/router/BRouter.js +++ b/js/router/BRouter.js @@ -90,7 +90,7 @@ L.BRouter = L.Class.extend({ opts.lonlats = this._parseLonLats(params.lonlats); } if (params.straight) { - opts.beelineFlags = this._parseBeelines(params.straight); + opts.beelineFlags = this._parseBeelines(params.straight, opts.lonlats); } if (params.nogos) { opts.nogos = this._parseNogos(params.nogos); @@ -315,17 +315,22 @@ L.BRouter = L.Class.extend({ }, _getBeelineString: function (beelineFlags) { - var s = ''; + var indexes = []; for (var i = 0; i < beelineFlags.length; i++) { - s += beelineFlags[i] ? '1' : '0'; + if (beelineFlags[i]) { + indexes.push(i); + } } - return s; + return indexes.join(','); }, - _parseBeelines: function (s) { - const beelineFlags = []; - for (const c of s) { - beelineFlags.push(!!+c); + _parseBeelines: function (s, lonlats) { + if (!lonlats || lonlats.length < 2) return []; + + const beelineFlags = new Array(lonlats.length - 1); + beelineFlags.fill(false); + for (const i of s.split(',')) { + beelineFlags[i] = true; } return beelineFlags; }, From 39f3a10965239b95429d57a935b9df7d64ebef98 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 25 Jun 2021 22:33:11 +0200 Subject: [PATCH 18/47] Support beelines in data and analysis tabs (WIP) --- js/plugin/Routing.js | 10 ++++++++++ js/router/BRouter.js | 18 +----------------- js/util/TrackEdges.js | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 7bdeda6..19907ac 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -452,6 +452,7 @@ BR.Routing = L.Routing.extend({ createBeeline: function (latLng1, latLng2) { const layer = L.Routing.prototype.createBeeline.call(this, latLng1, latLng2); + const distance = Math.round(latLng1.distanceTo(latLng2)); const props = { cost: 0, 'filtered ascend': 0, @@ -473,6 +474,7 @@ BR.Routing = L.Routing.extend({ 'WayTags', 'NodeTags', ], + [latLng2.lng * 1000000, latLng2.lat * 1000000, 0, distance, 0, 0, 0, 0, 0, '', ''], ], }; layer.feature = turf.lineString( @@ -482,6 +484,14 @@ BR.Routing = L.Routing.extend({ ], props ); + + // corresponding to BRouter._assignFeatures + for (const latLng of layer.getLatLngs()) { + const featureMessage = props.messages[1]; + latLng.feature = BR.TrackEdges.getFeature(featureMessage); + latLng.message = featureMessage; + } + return layer; }, }); diff --git a/js/router/BRouter.js b/js/router/BRouter.js index 5438744..6b272cd 100644 --- a/js/router/BRouter.js +++ b/js/router/BRouter.js @@ -237,7 +237,7 @@ L.BRouter = L.Class.extend({ var segmentLatLng = segmentLatLngs[fi], featureMessage = featureMessages[mi]; - segmentLatLng.feature = this._getFeature(featureMessage); + segmentLatLng.feature = BR.TrackEdges.getFeature(featureMessage); segmentLatLng.message = featureMessage; if (featureLatLng.equals(segmentLatLngs[fi])) { @@ -250,22 +250,6 @@ L.BRouter = L.Class.extend({ return segment; }, - _getFeature: function (featureMessage) { - //["Longitude", "Latitude", "Elevation", "Distance", "CostPerKm", "ElevCost", "TurnCost", "NodeCost", "InitialCost", "WayTags", "NodeTags"] - return { - cost: { - perKm: parseInt(featureMessage[4]), - elev: parseInt(featureMessage[5]), - turn: parseInt(featureMessage[6]), - node: parseInt(featureMessage[7]), - initial: parseInt(featureMessage[8]), - }, - distance: parseInt(featureMessage[3]), - wayTags: featureMessage[9], - nodeTags: featureMessage[10], - }; - }, - _getFeatureLatLng: function (message) { var lon = message[0] / 1000000, lat = message[1] / 1000000; diff --git a/js/util/TrackEdges.js b/js/util/TrackEdges.js index 6602545..9102ec8 100644 --- a/js/util/TrackEdges.js +++ b/js/util/TrackEdges.js @@ -5,6 +5,24 @@ * @type {L.Class} */ BR.TrackEdges = L.Class.extend({ + statics: { + getFeature: function (featureMessage) { + //["Longitude", "Latitude", "Elevation", "Distance", "CostPerKm", "ElevCost", "TurnCost", "NodeCost", "InitialCost", "WayTags", "NodeTags"] + return { + cost: { + perKm: parseInt(featureMessage[4]), + elev: parseInt(featureMessage[5]), + turn: parseInt(featureMessage[6]), + node: parseInt(featureMessage[7]), + initial: parseInt(featureMessage[8]), + }, + distance: parseInt(featureMessage[3]), + wayTags: featureMessage[9], + nodeTags: featureMessage[10], + }; + }, + }, + /** * List of indexes for the track array where * a segment with different features ends From f185d78dca78ab245d1c3ab330fc727aa4619b72 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Tue, 29 Jun 2021 21:51:28 +0200 Subject: [PATCH 19/47] Support undefined highway tag for beelines --- js/control/TrackAnalysis.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/control/TrackAnalysis.js b/js/control/TrackAnalysis.js index 2f230c9..3a54f89 100644 --- a/js/control/TrackAnalysis.js +++ b/js/control/TrackAnalysis.js @@ -470,9 +470,11 @@ BR.TrackAnalysis = L.Class.extend({ } return typeof parsed.tracktype === 'string' && parsed.tracktype === trackType; + } else if (dataName === 'internal-unknown' && typeof parsed.highway !== 'string') { + return true; } - return parsed.highway === dataName; + return typeof parsed.highway === 'string' && parsed.highway === dataName; case 'surface': if (dataName === 'internal-unknown' && typeof parsed.surface !== 'string') { return true; From 9f4a4983c5303a99e2aa06b3803580d84bd49e51 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 1 Jul 2021 12:02:49 +0200 Subject: [PATCH 20/47] Support beelines on reverse --- js/plugin/Routing.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 19907ac..7481fe4 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -422,10 +422,12 @@ BR.Routing = L.Routing.extend({ }, reverse: function () { - var waypoints = this.getWaypoints(); + const waypoints = this.getWaypoints(); + const beelineFlags = this.getBeelineFlags(); waypoints.reverse(); + beelineFlags.reverse(); this.clear(); - this.setWaypoints(waypoints); + this.setWaypoints(waypoints, beelineFlags); }, deleteLastPoint: function () { From 7118cc70d7f5c4860eeed4b14d03b9e7a8d2460a Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 1 Jul 2021 12:05:16 +0200 Subject: [PATCH 21/47] Show empty cells in data tab instead of 0 which makes it more clear that there is no data --- js/plugin/Routing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 7481fe4..627ed41 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -476,7 +476,7 @@ BR.Routing = L.Routing.extend({ 'WayTags', 'NodeTags', ], - [latLng2.lng * 1000000, latLng2.lat * 1000000, 0, distance, 0, 0, 0, 0, 0, '', ''], + [latLng2.lng * 1000000, latLng2.lat * 1000000, null, distance, null, null, null, null, null, '', ''], ], }; layer.feature = turf.lineString( From fe050816785e274b02e9299c26bcea42cd54e611 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 2 Jul 2021 19:44:43 +0200 Subject: [PATCH 22/47] Add beeline support to distance markers - update on beeline toggle - avoid distance markers wandering accross the map while routing many waypoints, because beelines are added immediately, by turning off updates in-between --- js/plugin/Routing.js | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 627ed41..7d048a6 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -54,13 +54,36 @@ BR.Routing = L.Routing.extend({ this._waypoints.on('layeradd', this._setMarkerOpacity, this); - this.on('routing:routeWaypointStart routing:rerouteAllSegmentsStart', function (evt) { - this._removeDistanceMarkers(); + // flag if (re-)routing of all segments is ongoing + this._routingAll = false; + this.on('routing:rerouteAllSegmentsStart routing:setWaypointsStart', function (evt) { + this._routingAll = true; + }); + this.on('routing:rerouteAllSegmentsEnd routing:setWaypointsEnd', function (evt) { + this._routingAll = false; }); - this.on('routing:routeWaypointEnd routing:setWaypointsEnd routing:rerouteAllSegmentsEnd', function (evt) { - this._updateDistanceMarkers(evt); - }); + this.on( + 'routing:routeWaypointStart routing:rerouteAllSegmentsStart routing:rerouteSegmentStart', + function (evt) { + if (!this._routingAll || evt.type === 'routing:rerouteAllSegmentsStart') { + this._removeDistanceMarkers(); + } + } + ); + + this.on( + 'routing:routeWaypointEnd routing:setWaypointsEnd routing:rerouteAllSegmentsEnd routing:rerouteSegmentEnd', + function (evt) { + if ( + !this._routingAll || + evt.type === 'routing:rerouteAllSegmentsEnd' || + evt.type === 'routing:setWaypointsEnd' + ) { + this._updateDistanceMarkers(evt); + } + } + ); // turn line mouse marker off while over waypoint marker this.on( From af1a11872adaa0410695f0c1082e71108b76b1c4 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 4 Oct 2021 10:26:54 +0200 Subject: [PATCH 23/47] Initial client-side stats from BRouter StdPath.java - `computeKinematic` and dependencies extracted from: https://github.com/abrensch/brouter/blob/57da34d205d26f22b31f667facc99ab7507d468c/brouter-core/src/main/java/btools/router/StdPath.java - transpiled with JSweet 3.0.0 - http://www.jsweet.org/jsweet-live-sandbox/ - manual cleanup --- js/util/StdPath.js | 146 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 js/util/StdPath.js diff --git a/js/util/StdPath.js b/js/util/StdPath.js new file mode 100644 index 0000000..14e45ba --- /dev/null +++ b/js/util/StdPath.js @@ -0,0 +1,146 @@ +(function () { + // Calculates time and energy stats + // + // from BRouter btools.router.StdPath + + class BExpressionContextWay { + getMaxspeed() { + return 45.0; + } + getCostfactor() { + return 1.0; + } + } + + class BExpressionContext { + getVariableValue(name, defaultValue) { + return defaultValue; + } + } + + class RoutingContext { + constructor() { + this.expctxGlobal = new BExpressionContext(); + this.expctxWay = new BExpressionContextWay(); + this.bikeMode = true; + this.footMode = false; + this.totalMass = this.expctxGlobal.getVariableValue('totalMass', 90.0); + this.maxSpeed = this.expctxGlobal.getVariableValue('maxSpeed', this.footMode ? 6.0 : 45.0) / 3.6; + this.S_C_x = this.expctxGlobal.getVariableValue('S_C_x', 0.5 * 0.45); + this.defaultC_r = this.expctxGlobal.getVariableValue('C_r', 0.01); + this.bikerPower = this.expctxGlobal.getVariableValue('bikerPower', 100.0); + } + } + + class StdPath { + constructor() { + this.totalTime = 0; + this.totalEnergy = 0; + this.elevation_buffer = 0; + } + + /** + * Approximation to Math.exp for small negative arguments + * @param {number} e + * @return {number} + */ + static exp(e) { + var x = e; + var f = 1.0; + while (e < -1.0) { + { + e += 1.0; + f *= 0.367879; + } + } + return f * (1.0 + x * (1.0 + x * (0.5 + x * (0.166667 + 0.0416667 * x)))); + } + + static solveCubic(a, c, d) { + var v = 8.0; + var findingStartvalue = true; + for (var i = 0; i < 10; i++) { + { + var y = (a * v * v + c) * v - d; + if (y < 0.1) { + if (findingStartvalue) { + v *= 2.0; + continue; + } + break; + } + findingStartvalue = false; + var y_prime = 3 * a * v * v + c; + v -= y / y_prime; + } + } + return v; + } + + resetState() { + this.totalTime = 0.0; + this.totalEnergy = 0.0; + this.elevation_buffer = 0.0; + } + + calcIncline(dist) { + var min_delta = 3.0; + var shift; + if (this.elevation_buffer > min_delta) { + shift = -min_delta; + } else if (this.elevation_buffer < min_delta) { + shift = -min_delta; + } else { + return 0.0; + } + var decayFactor = StdPath.exp(-dist / 100.0); + var new_elevation_buffer = (this.elevation_buffer + shift) * decayFactor - shift; + var incline = (this.elevation_buffer - new_elevation_buffer) / dist; + this.elevation_buffer = new_elevation_buffer; + return incline; + } + + computeKinematic(rc, dist, delta_h, detailMode) { + if (!detailMode) { + return; + } + this.elevation_buffer += delta_h; + var incline = this.calcIncline(dist); + var wayMaxspeed; + wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6; + if (wayMaxspeed === 0) { + wayMaxspeed = rc.maxSpeed; + } + wayMaxspeed = Math.min(wayMaxspeed, rc.maxSpeed); + var speed; + var f_roll = rc.totalMass * StdPath.GRAVITY * (rc.defaultC_r + incline); + if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9) { + speed = rc.maxSpeed * 3.6; + speed = (speed * StdPath.exp(-3.5 * Math.abs(incline + 0.05))) / 3.6; + } else if (rc.bikeMode) { + speed = StdPath.solveCubic(rc.S_C_x, f_roll, rc.bikerPower); + speed = Math.min(speed, wayMaxspeed); + } else { + speed = wayMaxspeed; + } + var dt = dist / speed; + this.totalTime += dt; + var energy = dist * (rc.S_C_x * speed * speed + f_roll); + if (energy > 0.0) { + this.totalEnergy += energy; + } + } + + getTotalTime() { + return this.totalTime; + } + + getTotalEnergy() { + return this.totalEnergy; + } + } + + StdPath.GRAVITY = 9.81; + + BR.StdPath = StdPath; +})(); From bd2e89ef06fdf49d779ab09c01bae059b533065d Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 8 Oct 2021 19:40:41 +0200 Subject: [PATCH 24/47] Temporarily add transpiled BRouter CheapRuler for better comparison during development --- js/util/CheapRuler.js | 109 ++++++++++++++++++++++++++++++++++ tests/util/CheapRuler.test.js | 18 ++++++ 2 files changed, 127 insertions(+) create mode 100644 js/util/CheapRuler.js create mode 100644 tests/util/CheapRuler.test.js diff --git a/js/util/CheapRuler.js b/js/util/CheapRuler.js new file mode 100644 index 0000000..199097f --- /dev/null +++ b/js/util/CheapRuler.js @@ -0,0 +1,109 @@ +/* Generated from Java with JSweet 3.1.0 - http://www.jsweet.org */ +//var btools; +btools = {}; +(function (btools) { + var util; + (function (util) { + class CheapRuler { + static __static_initialize() { + if (!CheapRuler.__static_initialized) { + CheapRuler.__static_initialized = true; + CheapRuler.__static_initializer_0(); + } + } + static DEG_TO_RAD_$LI$() { + CheapRuler.__static_initialize(); + if (CheapRuler.DEG_TO_RAD == null) { + CheapRuler.DEG_TO_RAD = Math.PI / 180.0; + } + return CheapRuler.DEG_TO_RAD; + } + static SCALE_CACHE_$LI$() { + CheapRuler.__static_initialize(); + if (CheapRuler.SCALE_CACHE == null) { + CheapRuler.SCALE_CACHE = ((s) => { + let a = []; + while (s-- > 0) a.push(null); + return a; + })(CheapRuler.SCALE_CACHE_LENGTH); + } + return CheapRuler.SCALE_CACHE; + } + static __static_initializer_0() { + for (let i = 0; i < CheapRuler.SCALE_CACHE_LENGTH; i++) { + { + CheapRuler.SCALE_CACHE_$LI$()[i] = CheapRuler.calcKxKyFromILat( + i * CheapRuler.SCALE_CACHE_INCREMENT + ((CheapRuler.SCALE_CACHE_INCREMENT / 2) | 0) + ); + } + } + } + /*private*/ static calcKxKyFromILat(ilat) { + const lat = CheapRuler.DEG_TO_RAD_$LI$() * (ilat * CheapRuler.ILATLNG_TO_LATLNG - 90); + const cos = Math.cos(lat); + const cos2 = 2 * cos * cos - 1; + const cos3 = 2 * cos * cos2 - cos; + const cos4 = 2 * cos * cos3 - cos2; + const cos5 = 2 * cos * cos4 - cos3; + const kxky = [0, 0]; + kxky[0] = + (111.41513 * cos - 0.09455 * cos3 + 1.2e-4 * cos5) * + CheapRuler.ILATLNG_TO_LATLNG * + CheapRuler.KILOMETERS_TO_METERS; + kxky[1] = + (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * + CheapRuler.ILATLNG_TO_LATLNG * + CheapRuler.KILOMETERS_TO_METERS; + return kxky; + } + /** + * Calculate the degree->meter scale for given latitude + * + * @return {double[]} [lon->meter,lat->meter] + * @param {number} ilat + */ + static getLonLatToMeterScales(ilat) { + return CheapRuler.SCALE_CACHE_$LI$()[(ilat / CheapRuler.SCALE_CACHE_INCREMENT) | 0]; + } + /** + * Compute the distance (in meters) between two points represented by their + * (integer) latitude and longitude. + * + * @param {number} ilon1 Integer longitude for the start point. this is (longitude in degrees + 180) * 1e6. + * @param {number} ilat1 Integer latitude for the start point, this is (latitude + 90) * 1e6. + * @param {number} ilon2 Integer longitude for the end point, this is (longitude + 180) * 1e6. + * @param {number} ilat2 Integer latitude for the end point, this is (latitude + 90) * 1e6. + * @return {number} The distance between the two points, in meters. + * + * Note: + * Integer longitude is ((longitude in degrees) + 180) * 1e6. + * Integer latitude is ((latitude in degrees) + 90) * 1e6. + */ + static distance(ilon1, ilat1, ilon2, ilat2) { + const kxky = CheapRuler.getLonLatToMeterScales((ilat1 + ilat2) >> 1); + const dlon = (ilon1 - ilon2) * kxky[0]; + const dlat = (ilat1 - ilat2) * kxky[1]; + return Math.sqrt(dlat * dlat + dlon * dlon); + } + } + CheapRuler.__static_initialized = false; + /** + * Cheap-Ruler Java implementation + * See + * https://blog.mapbox.com/fast-geodesic-approximations-with-cheap-ruler-106f229ad016 + * for more details. + * + * Original code is at https://github.com/mapbox/cheap-ruler under ISC license. + * + * This is implemented as a Singleton to have a unique cache for the cosine + * values across all the code. + */ + CheapRuler.ILATLNG_TO_LATLNG = 1.0e-6; + CheapRuler.KILOMETERS_TO_METERS = 1000; + CheapRuler.SCALE_CACHE_LENGTH = 1800; + CheapRuler.SCALE_CACHE_INCREMENT = 100000; + util.CheapRuler = CheapRuler; + CheapRuler['__class'] = 'btools.util.CheapRuler'; + })((util = btools.util || (btools.util = {}))); +})(btools || (btools = {})); +btools.util.CheapRuler.__static_initialize(); diff --git a/tests/util/CheapRuler.test.js b/tests/util/CheapRuler.test.js new file mode 100644 index 0000000..3634de2 --- /dev/null +++ b/tests/util/CheapRuler.test.js @@ -0,0 +1,18 @@ +require('../../js/util/CheapRuler.js'); + +test('distance', () => { + // https://github.com/abrensch/brouter/issues/3#issuecomment-440375918 + const latlng1 = [48.8124, 2.3158]; + const latlng2 = [48.8204, 2.321]; + + const ilon1 = (latlng1[1] + 180) * 1e6; + const ilat1 = (latlng1[0] + 90) * 1e6; + const ilon2 = (latlng2[1] + 180) * 1e6; + const ilat2 = (latlng2[0] + 90) * 1e6; + + const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + + // 968.1670119067338 - issue #3 (App.java) + // 968.0593622374572 - CheapRuler.java + expect(distance).toBeCloseTo(968.0593622374572); +}); From 7c8c71a3de14192f4e3aa980afd7398cbd8b7c7a Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 14 Oct 2021 17:53:21 +0200 Subject: [PATCH 25/47] Add stats track tests (failing) --- js/util/CheapRuler.js | 7 +++++++ js/util/StdPath.js | 5 +++-- tests/util/CheapRuler.test.js | 32 ++++++++++++++++++++++++++------ tests/util/StdPath.test.js | 30 ++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 tests/util/StdPath.test.js diff --git a/js/util/CheapRuler.js b/js/util/CheapRuler.js index 199097f..3ac144d 100644 --- a/js/util/CheapRuler.js +++ b/js/util/CheapRuler.js @@ -107,3 +107,10 @@ btools = {}; })((util = btools.util || (btools.util = {}))); })(btools || (btools = {})); btools.util.CheapRuler.__static_initialize(); + +btools.util.CheapRuler.toIntegerLngLat = (coordinate) => { + const ilon = (coordinate[0] + 180) * 1e6; + const ilat = (coordinate[1] + 90) * 1e6; + + return [ilon, ilat]; +}; diff --git a/js/util/StdPath.js b/js/util/StdPath.js index 14e45ba..d24e487 100644 --- a/js/util/StdPath.js +++ b/js/util/StdPath.js @@ -1,7 +1,5 @@ (function () { // Calculates time and energy stats - // - // from BRouter btools.router.StdPath class BExpressionContextWay { getMaxspeed() { @@ -18,6 +16,7 @@ } } + // from BRouter btools.router.RoutingContext class RoutingContext { constructor() { this.expctxGlobal = new BExpressionContext(); @@ -32,6 +31,7 @@ } } + // from BRouter btools.router.StdPath class StdPath { constructor() { this.totalTime = 0; @@ -143,4 +143,5 @@ StdPath.GRAVITY = 9.81; BR.StdPath = StdPath; + BR.RoutingContext = RoutingContext; })(); diff --git a/tests/util/CheapRuler.test.js b/tests/util/CheapRuler.test.js index 3634de2..3da5fbb 100644 --- a/tests/util/CheapRuler.test.js +++ b/tests/util/CheapRuler.test.js @@ -1,14 +1,13 @@ require('../../js/util/CheapRuler.js'); +const geoJson = require('../format/data/track.json'); test('distance', () => { // https://github.com/abrensch/brouter/issues/3#issuecomment-440375918 - const latlng1 = [48.8124, 2.3158]; - const latlng2 = [48.8204, 2.321]; + const lngLat1 = [2.3158, 48.8124]; + const lngLat2 = [2.321, 48.8204]; - const ilon1 = (latlng1[1] + 180) * 1e6; - const ilat1 = (latlng1[0] + 90) * 1e6; - const ilon2 = (latlng2[1] + 180) * 1e6; - const ilat2 = (latlng2[0] + 90) * 1e6; + const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat(lngLat1); + const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat(lngLat2); const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); @@ -16,3 +15,24 @@ test('distance', () => { // 968.0593622374572 - CheapRuler.java expect(distance).toBeCloseTo(968.0593622374572); }); + +test('total distance', () => { + const coordinates = geoJson.features[0].geometry.coordinates; + const properties = geoJson.features[0].properties; + let totalDistance = 0; + + for (let i = 0; i < coordinates.length; i++) { + if (i === 0) continue; + + const coord1 = coordinates[i - 1]; + const coord2 = coordinates[i]; + + const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat(coord1); + const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat(coord2); + + const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + totalDistance += distance; + } + + expect(Math.round(totalDistance)).toEqual(+properties['track-length']); +}); diff --git a/tests/util/StdPath.test.js b/tests/util/StdPath.test.js new file mode 100644 index 0000000..a0a4be1 --- /dev/null +++ b/tests/util/StdPath.test.js @@ -0,0 +1,30 @@ +BR = {}; +require('../../js/util/CheapRuler.js'); +require('../../js/util/StdPath.js'); + +const geoJson = require('../format/data/track.json'); + +test('simple track', () => { + const coordinates = geoJson.features[0].geometry.coordinates; + const properties = geoJson.features[0].properties; + const rc = new BR.RoutingContext(); + const stdPath = new BR.StdPath(); + + for (let i = 0; i < coordinates.length; i++) { + if (i === 0) continue; + + const coord1 = coordinates[i - 1]; + const coord2 = coordinates[i]; + + const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat(coord1); + const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat(coord2); + + const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + const deltaHeight = coord2[2] - coord1[2]; + + stdPath.computeKinematic(rc, distance, deltaHeight, true); + } + + expect(Math.round(stdPath.getTotalEnergy())).toEqual(+properties['total-energy']); + expect(Math.round(stdPath.getTotalTime())).toEqual(+properties['total-time']); +}); From 025eb02a6a0f60bcd826235b09ffca722bc23bd6 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 20 Jan 2022 18:38:51 +0100 Subject: [PATCH 26/47] Fix stats tests by summarizing integer distances --- js/util/CheapRuler.js | 13 +++++++++++-- tests/format/Gpx.test.js | 1 + tests/util/CheapRuler.test.js | 2 +- tests/util/StdPath.test.js | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/js/util/CheapRuler.js b/js/util/CheapRuler.js index 3ac144d..ad12fd1 100644 --- a/js/util/CheapRuler.js +++ b/js/util/CheapRuler.js @@ -109,8 +109,17 @@ btools = {}; btools.util.CheapRuler.__static_initialize(); btools.util.CheapRuler.toIntegerLngLat = (coordinate) => { - const ilon = (coordinate[0] + 180) * 1e6; - const ilat = (coordinate[1] + 90) * 1e6; + const ilon = Math.round((coordinate[0] + 180) * 1e6); + const ilat = Math.round((coordinate[1] + 90) * 1e6); return [ilon, ilat]; }; + +btools.util.CheapRuler.calcDistance = (ilon1, ilat1, ilon2, ilat2) => { + const distanceFloat = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + + // Convert to integer (no decimals) values to match BRouter OsmPathElement.calcDistance: + // `(int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );` + // https://github.com/abrensch/brouter/blob/1640bafa800f8bab7aebde797edc99fdbeea3b07/brouter-core/src/main/java/btools/router/OsmPathElement.java#L81 + return Math.trunc(distanceFloat + 1.0); +}; diff --git a/tests/format/Gpx.test.js b/tests/format/Gpx.test.js index df9d972..9235bf5 100644 --- a/tests/format/Gpx.test.js +++ b/tests/format/Gpx.test.js @@ -10,6 +10,7 @@ require('../../js/format/Gpx.js'); const fs = require('fs'); +// lonlats=8.467712,49.488117;8.470598,49.488849 + turnInstructionMode = 5 (gpsies-style) const geoJson = require('./data/track.json'); // lonlats=8.467712,49.488117;8.469354,49.488394;8.470556,49.488946;8.469982,49.489176 + turnInstructionMode = 5 // console log in Export._formatTrack diff --git a/tests/util/CheapRuler.test.js b/tests/util/CheapRuler.test.js index 3da5fbb..28b9498 100644 --- a/tests/util/CheapRuler.test.js +++ b/tests/util/CheapRuler.test.js @@ -30,7 +30,7 @@ test('total distance', () => { const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat(coord1); const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat(coord2); - const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + const distance = btools.util.CheapRuler.calcDistance(ilon1, ilat1, ilon2, ilat2); totalDistance += distance; } diff --git a/tests/util/StdPath.test.js b/tests/util/StdPath.test.js index a0a4be1..9673a5c 100644 --- a/tests/util/StdPath.test.js +++ b/tests/util/StdPath.test.js @@ -19,7 +19,7 @@ test('simple track', () => { const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat(coord1); const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat(coord2); - const distance = btools.util.CheapRuler.distance(ilon1, ilat1, ilon2, ilat2); + const distance = btools.util.CheapRuler.calcDistance(ilon1, ilat1, ilon2, ilat2); const deltaHeight = coord2[2] - coord1[2]; stdPath.computeKinematic(rc, distance, deltaHeight, true); From 6ee2b457160bb2cf185196bcaab84c8aa0dc5945 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 21 Jan 2022 18:23:13 +0100 Subject: [PATCH 27/47] Update track.json --- tests/format/Gpx.test.js | 2 ++ tests/format/data/track.json | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/format/Gpx.test.js b/tests/format/Gpx.test.js index 9235bf5..4f1c6ac 100644 --- a/tests/format/Gpx.test.js +++ b/tests/format/Gpx.test.js @@ -65,6 +65,8 @@ describe('voice hints', () => { /:(rteTime|rteSpeed)>([\d.]*)<\//g, (match, p1, p2) => `:${p1}>${(+p2).toFixed(3)}9.361<', 'rteSpeed>9.360<'); const gpx = BR.Gpx.format(geoJson, 2); expect(gpx).toEqual(brouterGpx); diff --git a/tests/format/data/track.json b/tests/format/data/track.json index b2c989c..0054266 100644 --- a/tests/format/data/track.json +++ b/tests/format/data/track.json @@ -4,7 +4,7 @@ { "type": "Feature", "properties": { - "creator": "BRouter-1.1", + "creator": "BRouter-1.6.3", "name": "Track", "track-length": "319", "filtered ascend": "2", @@ -17,11 +17,11 @@ [5,2,0,90.0,-90," 6(-89)6 (0)6 (89)6"] ], "messages": [ - ["Longitude", "Latitude", "Elevation", "Distance", "CostPerKm", "ElevCost", "TurnCost", "NodeCost", "InitialCost", "WayTags", "NodeTags"], - ["8468340", "49488794", "101", "89", "1000", "0", "0", "0", "0", "highway=residential surface=asphalt cycleway=lane oneway=yes lcn=yes smoothness=good route_bicycle_icn=yes route_bicycle_ncn=yes route_bicycle_rcn=yes", ""], - ["8470671", "49488909", "99", "230", "1150", "0", "180", "0", "0", "highway=residential surface=asphalt oneway=yes smoothness=good", ""] + ["Longitude", "Latitude", "Elevation", "Distance", "CostPerKm", "ElevCost", "TurnCost", "NodeCost", "InitialCost", "WayTags", "NodeTags", "Time", "Energy"], + ["8468340", "49488794", "101", "89", "1000", "0", "0", "0", "0", "highway=residential surface=asphalt cycleway=lane oneway=yes lcn=yes smoothness=good route_bicycle_icn=yes route_bicycle_ncn=yes route_bicycle_rcn=yes", "", "9", "959"], + ["8470671", "49488909", "99", "230", "1150", "0", "180", "0", "0", "highway=residential surface=asphalt oneway=yes smoothness=good", "", "44", "4412"] ], - "times": [0.0,9.592433,12.270765,14.129882,19.406338,34.50238,44.117233] + "times": [0,9.592,12.271,14.13,19.406,34.502,44.117] }, "geometry": { "type": "LineString", From 6db0ce816166fbd1dcb9a974438e139239f1f7fb Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 28 Jan 2022 11:14:49 +0100 Subject: [PATCH 28/47] Fix track.json source comment --- tests/format/Gpx.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/format/Gpx.test.js b/tests/format/Gpx.test.js index 4f1c6ac..5888aa8 100644 --- a/tests/format/Gpx.test.js +++ b/tests/format/Gpx.test.js @@ -10,7 +10,7 @@ require('../../js/format/Gpx.js'); const fs = require('fs'); -// lonlats=8.467712,49.488117;8.470598,49.488849 + turnInstructionMode = 5 (gpsies-style) +// lonlats=8.467712,49.488117;8.470598,49.488849 + turnInstructionMode = 4 (comment-style) const geoJson = require('./data/track.json'); // lonlats=8.467712,49.488117;8.469354,49.488394;8.470556,49.488946;8.469982,49.489176 + turnInstructionMode = 5 // console log in Export._formatTrack From 9abf4b94c4197f5ddd4106930381f6be61f78e8f Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 7 Feb 2022 17:52:16 +0100 Subject: [PATCH 29/47] Calc stats for straight line --- js/plugin/Routing.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 7d048a6..0acc4eb 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -475,16 +475,34 @@ BR.Routing = L.Routing.extend({ } }, + _distance: function (latLng1, latLng2) { + //return Math.round(latLng1.distanceTo(latLng2)); + const [ilon1, ilat1] = btools.util.CheapRuler.toIntegerLngLat([latLng1.lng, latLng1.lat]); + const [ilon2, ilat2] = btools.util.CheapRuler.toIntegerLngLat([latLng2.lng, latLng2.lat]); + + return btools.util.CheapRuler.calcDistance(ilon1, ilat1, ilon2, ilat2); + }, + + _computeKinematic: function (distance, deltaHeight) { + const rc = new BR.RoutingContext(); + const stdPath = new BR.StdPath(); + + stdPath.computeKinematic(rc, distance, deltaHeight, true); + return stdPath; + }, + createBeeline: function (latLng1, latLng2) { const layer = L.Routing.prototype.createBeeline.call(this, latLng1, latLng2); - const distance = Math.round(latLng1.distanceTo(latLng2)); + const distance = this._distance(latLng1, latLng2); + const deltaHeight = (latLng2.alt ?? 0) - (latLng1.alt ?? 0); + const stdPath = this._computeKinematic(distance, deltaHeight); const props = { cost: 0, 'filtered ascend': 0, 'plain-ascend': 0, - 'total-energy': 0, - 'total-time': 0, - 'track-length': 0, + 'total-energy': stdPath.getTotalEnergy(), + 'total-time': stdPath.getTotalTime(), + 'track-length': distance, messages: [ [ 'Longitude', From 4d441533163791fd93ee760f50aa3e9628d876d3 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Tue, 15 Feb 2022 19:30:49 +0100 Subject: [PATCH 30/47] Read time/energy calc variables from profile and ensure profile text is loaded before updating route and straight line stats --- js/control/Profile.js | 10 ++++++++-- js/index.js | 29 ++++++++++++++++++----------- js/plugin/Routing.js | 8 +++++++- js/util/StdPath.js | 22 +++++++++++++++++----- tests/util/StdPath.test.js | 8 +++++++- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/js/control/Profile.js b/js/control/Profile.js index 8de56bc..4238577 100644 --- a/js/control/Profile.js +++ b/js/control/Profile.js @@ -38,11 +38,12 @@ BR.Profile = L.Evented.extend({ button.blur(); }, - update: function (options) { + update: function (options, cb) { var profileName = options.profile, profileUrl, empty = !this.editor.getValue(), - clean = this.editor.isClean(); + clean = this.editor.isClean(), + loading = false; if (profileName && BR.conf.profilesUrl) { // only synchronize profile editor/parameters with selection if no manual changes in full editor, @@ -51,11 +52,13 @@ BR.Profile = L.Evented.extend({ this.profileName = profileName; if (!(profileName in this.cache)) { profileUrl = BR.conf.profilesUrl + profileName + '.brf'; + loading = true; BR.Util.get( profileUrl, L.bind(function (err, profileText) { if (err) { console.warn('Error getting profile from "' + profileUrl + '": ' + err); + if (cb) cb(); return; } @@ -65,6 +68,7 @@ BR.Profile = L.Evented.extend({ if (!this.profileName || this.profileName === profileName) { this._setValue(profileText); } + if (cb) cb(); }, this) ); } else { @@ -80,6 +84,8 @@ BR.Profile = L.Evented.extend({ } } } + + if (cb && !loading) cb(); }, show: function () { diff --git a/js/index.js b/js/index.js index 5299db6..ae235c9 100644 --- a/js/index.js +++ b/js/index.js @@ -226,9 +226,12 @@ } routingOptions = new BR.RoutingOptions(); - routingOptions.on('update', updateRoute); routingOptions.on('update', function (evt) { - profile.update(evt.options); + if (urlHash.movingMap) return; + + profile.update(evt.options, () => { + updateRoute(evt); + }); }); BR.NogoAreas.MSG_BUTTON = i18next.t('keyboard.generic-shortcut', { @@ -290,7 +293,7 @@ routingPathQuality = new BR.RoutingPathQuality(map, layersControl); - routing = new BR.Routing({ + routing = new BR.Routing(profile, { routing: { router: L.bind(router.getRouteSegment, router), }, @@ -399,11 +402,12 @@ // initial option settings (after controls are added and initialized with onAdd) router.setOptions(nogos.getOptions()); router.setOptions(routingOptions.getOptions()); - profile.update(routingOptions.getOptions()); - // restore active layers from local storage when called without hash // (check before hash plugin init) if (!location.hash) { + profile.update(routingOptions.getOptions()); + + // restore active layers from local storage when called without hash layersControl.loadActiveLayers(); } @@ -427,13 +431,16 @@ router.setOptions(opts); routingOptions.setOptions(opts); nogos.setOptions(opts); - profile.update(opts); - if (opts.lonlats) { - routing.draw(false); - routing.clear(); - routing.setWaypoints(opts.lonlats, opts.beelineFlags); - } + const optsOrDefault = Object.assign({}, routingOptions.getOptions(), opts); + profile.update(optsOrDefault, () => { + if (opts.lonlats) { + routing.draw(false); + routing.clear(); + routing.setWaypoints(opts.lonlats, opts.beelineFlags); + } + }); + if (opts.pois) { pois.setMarkers(opts.pois); } diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 0acc4eb..648ecf9 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -39,6 +39,12 @@ BR.Routing = L.Routing.extend({ }, }, + initialize: function (profile, options) { + L.Routing.prototype.initialize.call(this, options); + + this.profile = profile; + }, + onAdd: function (map) { this.options.tooltips.waypoint = i18next.t('map.route-tooltip-waypoint'); this.options.tooltips.segment = i18next.t('map.route-tooltip-segment'); @@ -484,7 +490,7 @@ BR.Routing = L.Routing.extend({ }, _computeKinematic: function (distance, deltaHeight) { - const rc = new BR.RoutingContext(); + const rc = new BR.RoutingContext(this.profile); const stdPath = new BR.StdPath(); stdPath.computeKinematic(rc, distance, deltaHeight, true); diff --git a/js/util/StdPath.js b/js/util/StdPath.js index d24e487..3e66269 100644 --- a/js/util/StdPath.js +++ b/js/util/StdPath.js @@ -11,18 +11,30 @@ } class BExpressionContext { + constructor(profile) { + this.profile = profile; + } + getVariableValue(name, defaultValue) { - return defaultValue; + let value = this.profile?.getProfileVar(name) ?? defaultValue; + if (value === 'true') { + value = 1; + } else if (value === 'false') { + value = 0; + } + return +value; } } // from BRouter btools.router.RoutingContext class RoutingContext { - constructor() { - this.expctxGlobal = new BExpressionContext(); + constructor(profile) { + this.expctxGlobal = new BExpressionContext(profile); this.expctxWay = new BExpressionContextWay(); - this.bikeMode = true; - this.footMode = false; + + this.bikeMode = 0 !== this.expctxGlobal.getVariableValue('validForBikes', 0); + this.footMode = 0 !== this.expctxGlobal.getVariableValue('validForFoot', 0); + this.totalMass = this.expctxGlobal.getVariableValue('totalMass', 90.0); this.maxSpeed = this.expctxGlobal.getVariableValue('maxSpeed', this.footMode ? 6.0 : 45.0) / 3.6; this.S_C_x = this.expctxGlobal.getVariableValue('S_C_x', 0.5 * 0.45); diff --git a/tests/util/StdPath.test.js b/tests/util/StdPath.test.js index 9673a5c..5561fe3 100644 --- a/tests/util/StdPath.test.js +++ b/tests/util/StdPath.test.js @@ -7,7 +7,13 @@ const geoJson = require('../format/data/track.json'); test('simple track', () => { const coordinates = geoJson.features[0].geometry.coordinates; const properties = geoJson.features[0].properties; - const rc = new BR.RoutingContext(); + const dummyProfileVars = { + getProfileVar(name) { + const vars = { validForBikes: 1 }; + return vars[name]; + }, + }; + const rc = new BR.RoutingContext(dummyProfileVars); const stdPath = new BR.StdPath(); for (let i = 0; i < coordinates.length; i++) { From 7bbbffbd3fe2816683a5945427817e9ef5af0608 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 16 Feb 2022 16:12:24 +0100 Subject: [PATCH 31/47] Get selected profile vars despite pinned custom --- js/control/Profile.js | 81 ++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/js/control/Profile.js b/js/control/Profile.js index 4238577..5663ef2 100644 --- a/js/control/Profile.js +++ b/js/control/Profile.js @@ -41,47 +41,34 @@ BR.Profile = L.Evented.extend({ update: function (options, cb) { var profileName = options.profile, profileUrl, - empty = !this.editor.getValue(), - clean = this.editor.isClean(), loading = false; if (profileName && BR.conf.profilesUrl) { - // only synchronize profile editor/parameters with selection if no manual changes in full editor, - // else keep custom profile pinned - to prevent changes in another profile overwriting previous ones - if (empty || clean) { - this.profileName = profileName; - if (!(profileName in this.cache)) { - profileUrl = BR.conf.profilesUrl + profileName + '.brf'; - loading = true; - BR.Util.get( - profileUrl, - L.bind(function (err, profileText) { - if (err) { - console.warn('Error getting profile from "' + profileUrl + '": ' + err); - if (cb) cb(); - return; - } + this.selectedProfileName = profileName; - this.cache[profileName] = profileText; - - // don't set when option has changed while loading - if (!this.profileName || this.profileName === profileName) { - this._setValue(profileText); - } + if (!(profileName in this.cache)) { + profileUrl = BR.conf.profilesUrl + profileName + '.brf'; + loading = true; + BR.Util.get( + profileUrl, + L.bind(function (err, profileText) { + if (err) { + console.warn('Error getting profile from "' + profileUrl + '": ' + err); if (cb) cb(); - }, this) - ); - } else { - this._setValue(this.cache[profileName]); - } + return; + } - if (!this.pinned.hidden) { - this.pinned.hidden = true; - } + this.cache[profileName] = profileText; + + // don't set when option has changed while loading + if (!this.profileName || this.selectedProfileName === profileName) { + this._updateProfile(profileName, profileText); + } + if (cb) cb(); + }, this) + ); } else { - if (this.pinned.hidden) { - this.pinned.hidden = false; - } + this._updateProfile(profileName, this.cache[profileName]); } } @@ -107,7 +94,7 @@ BR.Profile = L.Evented.extend({ } } - const profileText = this._getProfileText(); + const profileText = this._getSelectedProfileText(); if (!profileText) return value; const regex = new RegExp(`assign\\s*${name}\\s*=?\\s*([\\w\\.]*)`); @@ -194,6 +181,26 @@ BR.Profile = L.Evented.extend({ }); }, + _updateProfile: function (profileName, profileText) { + const empty = !this.editor.getValue(); + const clean = this.editor.isClean(); + + // only synchronize profile editor/parameters with selection if no manual changes in full editor, + // else keep custom profile pinned - to prevent changes in another profile overwriting previous ones + if (empty || clean) { + this.profileName = profileName; + this._setValue(profileText); + + if (!this.pinned.hidden) { + this.pinned.hidden = true; + } + } else { + if (this.pinned.hidden) { + this.pinned.hidden = false; + } + } + }, + _setValue: function (profileText) { profileText = profileText || ''; @@ -369,4 +376,8 @@ BR.Profile = L.Evented.extend({ _getProfileText: function () { return this.editor.getValue(); }, + + _getSelectedProfileText: function () { + return this.cache[this.selectedProfileName] ?? this.editor.getValue(); + }, }); From 41a3b8f0acc0812aea9c43b9ca40fd3f4ca14bab Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 17 Feb 2022 18:39:16 +0100 Subject: [PATCH 32/47] Update leaflet-routing --- package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 73a85e4..bece57a 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "leaflet-hotline": "^0.4.0", "leaflet-plugins": "~3.0.0", "leaflet-providers": "^1.10.2", - "leaflet-routing": "nrenner/leaflet-routing#a8213a4", + "leaflet-routing": "nrenner/leaflet-routing#e86220a", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", "leaflet.heightgraph": "nrenner/Leaflet.Heightgraph#0757b2a", diff --git a/yarn.lock b/yarn.lock index 60187c0..a0c2981 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7806,9 +7806,9 @@ leaflet-providers@^1.10.2: resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.10.2.tgz#763c8e6655f26caf1afe3a1ef4add6c3e32de663" integrity sha512-1l867LObxwuFBeyPeBewip8PAXKOnvEoujq4/9y2TKTiZNHH76ksBD6dfktGjgUrOF+IdjsGHkpASPE+v2DQLw== -leaflet-routing@nrenner/leaflet-routing#a8213a4: +leaflet-routing@nrenner/leaflet-routing#e86220a: version "0.1.4-alpha.2" - resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/a8213a4089c8be24a725a8f575880b0c20f64e54" + resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/e86220aecd9f57a826ff64b1fc4382ef7af6cb35" leaflet-sidebar-v2@nrenner/leaflet-sidebar-v2#dev: version "3.0.2" From 30f8a889579c4ff8c17e4863a0ce17b7d652471e Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 1 Apr 2022 14:30:08 +0200 Subject: [PATCH 33/47] Interpolate stats over consecutive beelines --- js/plugin/Routing.js | 53 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 648ecf9..c9318f9 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -497,17 +497,62 @@ BR.Routing = L.Routing.extend({ return stdPath; }, + _interpolateBeelines(serialBeelines) { + let altStart = serialBeelines[0].getLatLngs()[0].alt; + const altEnd = serialBeelines[serialBeelines.length - 1].getLatLngs()[1].alt ?? altStart; + altStart ??= altEnd; + + let serialDelta = 0; + if (altStart != null && altEnd != null) { + serialDelta = altEnd - altStart; + } + const serialDistance = serialBeelines.reduce( + (dist, line) => (dist += line.feature.properties['track-length']), + 0 + ); + + for (const beeline of serialBeelines) { + const props = beeline.feature.properties; + const distance = props['track-length']; + const deltaHeight = (serialDelta * distance) / serialDistance; + + const stdPath = this._computeKinematic(distance, deltaHeight); + props['total-energy'] = stdPath.getTotalEnergy(); + props['total-time'] = stdPath.getTotalTime(); + // do not set interpolated alt value, to explicitly show missing data, e.g. in height graph + } + }, + + _updateBeelines: function () { + L.Routing.prototype._updateBeelines.call(this); + + let serialBeelines = []; + + this._eachSegment(function (m1, m2, line) { + if (line?._routing?.beeline) { + serialBeelines.push(line); + } else { + if (serialBeelines.length > 0) { + this._interpolateBeelines(serialBeelines); + } + serialBeelines = []; + } + }); + + if (serialBeelines.length > 0) { + this._interpolateBeelines(serialBeelines); + } + }, + createBeeline: function (latLng1, latLng2) { const layer = L.Routing.prototype.createBeeline.call(this, latLng1, latLng2); const distance = this._distance(latLng1, latLng2); - const deltaHeight = (latLng2.alt ?? 0) - (latLng1.alt ?? 0); - const stdPath = this._computeKinematic(distance, deltaHeight); const props = { cost: 0, 'filtered ascend': 0, 'plain-ascend': 0, - 'total-energy': stdPath.getTotalEnergy(), - 'total-time': stdPath.getTotalTime(), + 'total-energy': 0, + 'total-time': 0, 'track-length': distance, messages: [ [ From 2f8ddfa9e7c209ad8213ab1d674fc9c42773b084 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 6 Apr 2022 11:51:20 +0200 Subject: [PATCH 34/47] Interpolate ascend stats --- js/plugin/Routing.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index c9318f9..e7272ef 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -519,6 +519,14 @@ BR.Routing = L.Routing.extend({ const stdPath = this._computeKinematic(distance, deltaHeight); props['total-energy'] = stdPath.getTotalEnergy(); props['total-time'] = stdPath.getTotalTime(); + + // match BRouter/Java rounding where `(int)` cast truncates decimals + // https://github.com/abrensch/brouter/blob/14d5a2c4e6b101a2eab711e70151142881df95c6/brouter-core/src/main/java/btools/router/RoutingEngine.java#L1216-L1217 + if (deltaHeight > 0) { + // no filtering for simplicity for now + props['filtered ascend'] = Math.trunc(deltaHeight); + } + props['plain-ascend'] = Math.trunc(deltaHeight + 0.5); // do not set interpolated alt value, to explicitly show missing data, e.g. in height graph } }, From 5dd801d7140d35e66b962c1ca54be5d03d429984 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Sat, 9 Apr 2022 11:55:56 +0200 Subject: [PATCH 35/47] Interpolate cost by using max of neighbours --- js/plugin/Routing.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index e7272ef..1499f8e 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -497,7 +497,20 @@ BR.Routing = L.Routing.extend({ return stdPath; }, - _interpolateBeelines(serialBeelines) { + _getCostFactor: function (line) { + let costFactor = null; + if (line) { + const props = line.feature.properties; + const length = props['track-length']; + const cost = props['cost']; + if (length) { + costFactor = cost / length; + } + } + return costFactor; + }, + + _interpolateBeelines: function (serialBeelines, before, after) { let altStart = serialBeelines[0].getLatLngs()[0].alt; const altEnd = serialBeelines[serialBeelines.length - 1].getLatLngs()[1].alt ?? altStart; altStart ??= altEnd; @@ -511,6 +524,15 @@ BR.Routing = L.Routing.extend({ 0 ); + let beforeCostFactor = this._getCostFactor(before); + let afterCostFactor = this._getCostFactor(after); + let costFactor; + if (beforeCostFactor != null && afterCostFactor != null) { + costFactor = Math.max(beforeCostFactor, afterCostFactor); + } else { + costFactor = beforeCostFactor ?? afterCostFactor ?? 0; + } + for (const beeline of serialBeelines) { const props = beeline.feature.properties; const distance = props['track-length']; @@ -528,6 +550,8 @@ BR.Routing = L.Routing.extend({ } props['plain-ascend'] = Math.trunc(deltaHeight + 0.5); // do not set interpolated alt value, to explicitly show missing data, e.g. in height graph + + props['cost'] = Math.round(distance * costFactor); } }, @@ -535,20 +559,22 @@ BR.Routing = L.Routing.extend({ L.Routing.prototype._updateBeelines.call(this); let serialBeelines = []; + let before = null; this._eachSegment(function (m1, m2, line) { if (line?._routing?.beeline) { serialBeelines.push(line); } else { if (serialBeelines.length > 0) { - this._interpolateBeelines(serialBeelines); + this._interpolateBeelines(serialBeelines, before, line); } + before = line; serialBeelines = []; } }); if (serialBeelines.length > 0) { - this._interpolateBeelines(serialBeelines); + this._interpolateBeelines(serialBeelines, before, null); } }, From 42d8b272665e18a9b8c2a55752084ad0f6ae1fd4 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 11 Apr 2022 18:37:40 +0200 Subject: [PATCH 36/47] Add time and energy to messages --- js/plugin/Routing.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 1499f8e..a19ce19 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -601,8 +601,24 @@ BR.Routing = L.Routing.extend({ 'InitialCost', 'WayTags', 'NodeTags', + 'Time', + 'Energy', + ], + [ + latLng2.lng * 1000000, + latLng2.lat * 1000000, + null, + distance, + null, + null, + null, + null, + null, + '', + '', + null, + null, ], - [latLng2.lng * 1000000, latLng2.lat * 1000000, null, distance, null, null, null, null, null, '', ''], ], }; layer.feature = turf.lineString( From c122b31e738c739985c5172edd5091508b9194ae Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 11 Apr 2022 18:59:28 +0200 Subject: [PATCH 37/47] Replace ??=, not supported by i18next-scanner --- js/plugin/Routing.js | 2 +- package.json | 2 +- yarn.lock | 15 ++++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index a19ce19..fabd871 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -513,7 +513,7 @@ BR.Routing = L.Routing.extend({ _interpolateBeelines: function (serialBeelines, before, after) { let altStart = serialBeelines[0].getLatLngs()[0].alt; const altEnd = serialBeelines[serialBeelines.length - 1].getLatLngs()[1].alt ?? altStart; - altStart ??= altEnd; + altStart ?? (altStart = altEnd); let serialDelta = 0; if (altStart != null && altEnd != null) { diff --git a/package.json b/package.json index bece57a..d84c20b 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "gulp-util": "^3.0.7", "gulp-zip": "^5.0.2", "husky": "^4.3.4", - "i18next-scanner": "^3.0.0", + "i18next-scanner": "^3.1.0", "jest": "^26.6.3", "marked": "^2.0.0", "merge-stream": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index a0c2981..2583708 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5138,6 +5138,11 @@ esprima-fb@3001.1.0-dev-harmony-fb: resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz#b77d37abcd38ea0b77426bb8bc2922ce6b426411" integrity sha1-t303q8046gt3Qmu4vCkizmtCZBE= +esprima-next@^5.7.0: + version "5.8.1" + resolved "https://registry.yarnpkg.com/esprima-next/-/esprima-next-5.8.1.tgz#e670c9e807dce91075160d7cd7735c4b74581338" + integrity sha512-jPuleZ9j065A9xGKreFh9YSgPlbL9/miG/l4KslkwEb7Ilwl5Ct7BmDkSTHA0rW0qnqLx+hsZWIB66s1XaMAyA== + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -6529,10 +6534,10 @@ i18next-browser-languagedetector@^6.0.1: dependencies: "@babel/runtime" "^7.5.5" -i18next-scanner@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/i18next-scanner/-/i18next-scanner-3.0.0.tgz#16024fa7f6dc5fd73d91545bd01566f86a76529a" - integrity sha512-cm4Ch3VqicGZS8y+4xSvXoOsnE/iWhHZi6AZEyAgLLm3EDZ/eY21gDbLfbnwKVY6wCghzAEO9LfRNlxwTo8KMQ== +i18next-scanner@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/i18next-scanner/-/i18next-scanner-3.1.0.tgz#35d00d945637c1a2b90124b0fd327040ac197598" + integrity sha512-dHLXUJIiF1CYJNslCkJFDYJySk5fg+dzdg9O73XXqHcdZwJ2947SWusqq8HdNFB7LpkBi8oTG6TWLZPmqbAh8Q== dependencies: acorn "^8.0.4" acorn-dynamic-import "^4.0.0" @@ -6545,7 +6550,7 @@ i18next-scanner@^3.0.0: deepmerge "^4.0.0" ensure-array "^1.0.0" eol "^0.9.1" - esprima "^4.0.0" + esprima-next "^5.7.0" gulp-sort "^2.0.0" i18next "*" lodash "^4.0.0" From 6a19b53dc0da20e3357a40bc9f149a96e4bcb008 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 11 Apr 2022 19:24:48 +0200 Subject: [PATCH 38/47] Add interpolated warning for stats with beeline --- index.html | 10 ++++++++++ js/control/TrackStats.js | 3 +++ locales/en.json | 1 + 3 files changed, 14 insertions(+) diff --git a/index.html b/index.html index 5185d5a..1c21d6f 100644 --- a/index.html +++ b/index.html @@ -1131,6 +1131,16 @@
    +
  • Distance

    diff --git a/js/control/TrackStats.js b/js/control/TrackStats.js index de6f63f..4040798 100644 --- a/js/control/TrackStats.js +++ b/js/control/TrackStats.js @@ -9,6 +9,9 @@ BR.TrackStats = L.Class.extend({ $('#stats-container').show(); $('#stats-info').hide(); + const hasBeeline = segments.filter((line) => line?._routing?.beeline).length > 0; + document.getElementById('beeline-warning').hidden = !hasBeeline; + var stats = this.calcStats(polyline, segments), length1 = L.Util.formatNum(stats.trackLength / 1000, 1).toLocaleString(), length3 = L.Util.formatNum(stats.trackLength / 1000, 3).toLocaleString(undefined, { diff --git a/locales/en.json b/locales/en.json index f7b92dd..bae2e86 100644 --- a/locales/en.json +++ b/locales/en.json @@ -43,6 +43,7 @@ }, "footer": { "ascend": "Ascend", + "beeline-warning": "Warning: no data for straight lines, values interpolated", "cost": "Cost", "distance": "Distance", "elevation-chart": "Toggle elevation chart", From 7f481b8db3fb72bb4f58407cf84552cf695165ca Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Tue, 26 Apr 2022 12:41:41 +0200 Subject: [PATCH 39/47] Pass estimated cost factor to kinematic calc (only to use different formula > 4.9) --- js/plugin/Routing.js | 11 ++++++----- js/util/StdPath.js | 9 +++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index fabd871..c0600fd 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -489,8 +489,9 @@ BR.Routing = L.Routing.extend({ return btools.util.CheapRuler.calcDistance(ilon1, ilat1, ilon2, ilat2); }, - _computeKinematic: function (distance, deltaHeight) { + _computeKinematic: function (distance, deltaHeight, costFactor) { const rc = new BR.RoutingContext(this.profile); + rc.expctxWay = new BR.BExpressionContextWay(undefined, costFactor); const stdPath = new BR.StdPath(); stdPath.computeKinematic(rc, distance, deltaHeight, true); @@ -498,7 +499,7 @@ BR.Routing = L.Routing.extend({ }, _getCostFactor: function (line) { - let costFactor = null; + let costFactor; if (line) { const props = line.feature.properties; const length = props['track-length']; @@ -530,7 +531,7 @@ BR.Routing = L.Routing.extend({ if (beforeCostFactor != null && afterCostFactor != null) { costFactor = Math.max(beforeCostFactor, afterCostFactor); } else { - costFactor = beforeCostFactor ?? afterCostFactor ?? 0; + costFactor = beforeCostFactor ?? afterCostFactor; } for (const beeline of serialBeelines) { @@ -538,7 +539,7 @@ BR.Routing = L.Routing.extend({ const distance = props['track-length']; const deltaHeight = (serialDelta * distance) / serialDistance; - const stdPath = this._computeKinematic(distance, deltaHeight); + const stdPath = this._computeKinematic(distance, deltaHeight, costFactor); props['total-energy'] = stdPath.getTotalEnergy(); props['total-time'] = stdPath.getTotalTime(); @@ -551,7 +552,7 @@ BR.Routing = L.Routing.extend({ props['plain-ascend'] = Math.trunc(deltaHeight + 0.5); // do not set interpolated alt value, to explicitly show missing data, e.g. in height graph - props['cost'] = Math.round(distance * costFactor); + props['cost'] = Math.round(distance * (costFactor ?? 0)); } }, diff --git a/js/util/StdPath.js b/js/util/StdPath.js index 3e66269..108c39a 100644 --- a/js/util/StdPath.js +++ b/js/util/StdPath.js @@ -2,11 +2,15 @@ // Calculates time and energy stats class BExpressionContextWay { + constructor(maxspeed = 45.0, costfactor = 1.0) { + this.maxspeed = maxspeed; + this.costfactor = costfactor; + } getMaxspeed() { - return 45.0; + return this.maxspeed; } getCostfactor() { - return 1.0; + return this.costfactor; } } @@ -156,4 +160,5 @@ BR.StdPath = StdPath; BR.RoutingContext = RoutingContext; + BR.BExpressionContextWay = BExpressionContextWay; })(); From 855dc81283a749a60ec2e60dbf821e84c407d485 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Mon, 2 May 2022 10:55:20 +0200 Subject: [PATCH 40/47] Show gap in elevation graph for single beeline --- js/plugin/Routing.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index c0600fd..58fbbd5 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -512,8 +512,8 @@ BR.Routing = L.Routing.extend({ }, _interpolateBeelines: function (serialBeelines, before, after) { - let altStart = serialBeelines[0].getLatLngs()[0].alt; - const altEnd = serialBeelines[serialBeelines.length - 1].getLatLngs()[1].alt ?? altStart; + let altStart = before?.getLatLngs()[before.getLatLngs().length - 1].alt; + const altEnd = after?.getLatLngs()[0].alt ?? altStart; altStart ?? (altStart = altEnd); let serialDelta = 0; @@ -581,6 +581,9 @@ BR.Routing = L.Routing.extend({ createBeeline: function (latLng1, latLng2) { const layer = L.Routing.prototype.createBeeline.call(this, latLng1, latLng2); + // remove alt from cloned LatLngs to show gap in elevation graph to indicate no data inbetween + delete layer.getLatLngs()[0].alt; + delete layer.getLatLngs()[1].alt; const distance = this._distance(latLng1, latLng2); const props = { cost: 0, From 72588f3fd968adbc2e2af5b202654301c1d06d3d Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Wed, 4 May 2022 22:21:51 +0200 Subject: [PATCH 41/47] Only use Canvas for routing segments Routing trailers are lagging behind and Canvas by default might have side effects (Plugins) --- js/Map.js | 1 - js/plugin/Heightgraph.js | 10 ---------- js/plugin/Routing.js | 2 -- js/plugin/TracksLoader.js | 5 +++++ js/util/Track.js | 1 + 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/js/Map.js b/js/Map.js index dbcb69c..3e1f1d9 100644 --- a/js/Map.js +++ b/js/Map.js @@ -17,7 +17,6 @@ BR.Map = { } map = new L.Map('map', { - renderer: L.canvas({ tolerance: BR.Browser.touch ? 10 : 5 }), zoomControl: false, // add it manually so that we can translate it worldCopyJump: true, minZoom: 0, diff --git a/js/plugin/Heightgraph.js b/js/plugin/Heightgraph.js index 8cc1660..715ee3f 100644 --- a/js/plugin/Heightgraph.js +++ b/js/plugin/Heightgraph.js @@ -63,16 +63,6 @@ BR.Heightgraph = function (map, layersControl, routing, pois) { }, }, - onAdd(map) { - // As we're using canvas, initialize an `svg` root that is needed for map marker, - // see `Heightgraph._showMapMarker` - if (!document.querySelector('.leaflet-overlay-pane svg')) { - L.svg().addTo(map); - } - - return L.Control.Heightgraph.prototype.onAdd.call(this, map); - }, - addBelow: function (map) { // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 // this.width($('#map').outerWidth()); diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 58fbbd5..cfc6c43 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -51,7 +51,6 @@ BR.Routing = L.Routing.extend({ this._segmentsCasing = new L.FeatureGroup().addTo(map); this._loadingTrailerGroup = new L.FeatureGroup().addTo(map); - this._loadingTrailerRenderer = L.svg(); // CSS animation based on SVG path element var container = L.Routing.prototype.onAdd.call(this, map); @@ -389,7 +388,6 @@ BR.Routing = L.Routing.extend({ opacity: this.options.styles.trailer.opacity, dashArray: [10, 10], className: 'loading-trailer', - renderer: this._loadingTrailerRenderer, }); this._loadingTrailerGroup.addLayer(loadingTrailer); } diff --git a/js/plugin/TracksLoader.js b/js/plugin/TracksLoader.js index a68990a..bef99e0 100644 --- a/js/plugin/TracksLoader.js +++ b/js/plugin/TracksLoader.js @@ -70,6 +70,11 @@ BR.tracksLoader = function (map, layersControl, routing, pois) { } }, }); + + // make sure tracks are always shown below route by adding a custom pane below `leaflet-overlay-pane` + map.createPane('tracks'); + map.getPane('tracks').style.zIndex = 350; + var tracksLoaderControl = new TracksLoader(); tracksLoaderControl.addTo(map); diff --git a/js/util/Track.js b/js/util/Track.js index 19734ca..27cb7fa 100644 --- a/js/util/Track.js +++ b/js/util/Track.js @@ -32,6 +32,7 @@ BR.Track = { zIndexOffset: -1000, }); }, + pane: 'tracks', }; }, From 2badd9f87887e229661b7f7e55885f40716fc3d7 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 5 May 2022 11:31:31 +0200 Subject: [PATCH 42/47] Update leaflet-routing --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d84c20b..b2b9a7a 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "leaflet-hotline": "^0.4.0", "leaflet-plugins": "~3.0.0", "leaflet-providers": "^1.10.2", - "leaflet-routing": "nrenner/leaflet-routing#e86220a", + "leaflet-routing": "nrenner/leaflet-routing#d10c9f5", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", "leaflet.heightgraph": "nrenner/Leaflet.Heightgraph#0757b2a", From 049ba848f3485d039c6ca5009fe19d91792f1ca7 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 5 May 2022 11:38:25 +0200 Subject: [PATCH 43/47] Update leaflet-routing (yarn.lock) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2583708..4efcfd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7811,9 +7811,9 @@ leaflet-providers@^1.10.2: resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.10.2.tgz#763c8e6655f26caf1afe3a1ef4add6c3e32de663" integrity sha512-1l867LObxwuFBeyPeBewip8PAXKOnvEoujq4/9y2TKTiZNHH76ksBD6dfktGjgUrOF+IdjsGHkpASPE+v2DQLw== -leaflet-routing@nrenner/leaflet-routing#e86220a: - version "0.1.4-alpha.2" - resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/e86220aecd9f57a826ff64b1fc4382ef7af6cb35" +leaflet-routing@nrenner/leaflet-routing#d10c9f5: + version "0.1.4-alpha.3" + resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/d10c9f5ca116300ddc337974e53b582e81eb3c31" leaflet-sidebar-v2@nrenner/leaflet-sidebar-v2#dev: version "3.0.2" From 31916c2cae68d92c385db84d973f5104e407cecb Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 6 May 2022 09:43:37 +0200 Subject: [PATCH 44/47] Omit beelines in quality coding --- js/plugin/RoutingPathQuality.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/plugin/RoutingPathQuality.js b/js/plugin/RoutingPathQuality.js index 2e0e074..0ff67e0 100644 --- a/js/plugin/RoutingPathQuality.js +++ b/js/plugin/RoutingPathQuality.js @@ -227,6 +227,7 @@ var HotLineQualityProvider = L.Class.extend({ var flatLines = []; for (var i = 0; segments && i < segments.length; i++) { var segment = segments[i]; + if (segment._routing?.beeline) continue; var vals = this._computeLatLngVals(segment); segmentLatLngs.push(vals); Array.prototype.push.apply(flatLines, vals); From d8e0e8ee097ce2f054423b06ac333c5c4fabbd4e Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Fri, 6 May 2022 15:27:46 +0200 Subject: [PATCH 45/47] Simplify response check, beeline has feature now --- js/plugin/Routing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index cfc6c43..ed6bbb8 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -365,7 +365,7 @@ BR.Routing = L.Routing.extend({ this._eachSegment(function (m1, m2, line) { // omit if null (still calculating) or error // NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line - if (line && (line.feature || m1._routing.beeline)) { + if (line && line.feature) { latLngs = latLngs.concat(line.getLatLngs()); } }); From 0087d31844d3430f3ca2cc7a82bbab884d7c7143 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Tue, 10 May 2022 11:43:07 +0200 Subject: [PATCH 46/47] Add beeline hint to segment tooltip --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index bae2e86..f6acb88 100644 --- a/locales/en.json +++ b/locales/en.json @@ -150,7 +150,7 @@ "route-quality-cost": "Cost coding", "route-quality-incline": "Incline coding", "route-quality-shortcut": "{{action}} ({{key}} key to toggle)", - "route-tooltip-segment": "Drag to create a new waypoint", + "route-tooltip-segment": "Drag to create a new waypoint. Click to toggle straight line.", "route-tooltip-waypoint": "Waypoint. Drag to move; Click to remove.", "strava-biking": "Show Strava biking segments", "strava-running": "Show Strava running segments", From 89dff44d5a98104d9590b0d63f487d3a470d86a6 Mon Sep 17 00:00:00 2001 From: Norbert Renner Date: Thu, 12 May 2022 11:28:04 +0200 Subject: [PATCH 47/47] Update leaflet-routing --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b2b9a7a..31bff64 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "leaflet-hotline": "^0.4.0", "leaflet-plugins": "~3.0.0", "leaflet-providers": "^1.10.2", - "leaflet-routing": "nrenner/leaflet-routing#d10c9f5", + "leaflet-routing": "nrenner/leaflet-routing#773314a", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-triangle-marker": "^1.0.2", "leaflet.heightgraph": "nrenner/Leaflet.Heightgraph#0757b2a", diff --git a/yarn.lock b/yarn.lock index 4efcfd2..7688dd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7811,9 +7811,9 @@ leaflet-providers@^1.10.2: resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.10.2.tgz#763c8e6655f26caf1afe3a1ef4add6c3e32de663" integrity sha512-1l867LObxwuFBeyPeBewip8PAXKOnvEoujq4/9y2TKTiZNHH76ksBD6dfktGjgUrOF+IdjsGHkpASPE+v2DQLw== -leaflet-routing@nrenner/leaflet-routing#d10c9f5: - version "0.1.4-alpha.3" - resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/d10c9f5ca116300ddc337974e53b582e81eb3c31" +leaflet-routing@nrenner/leaflet-routing#773314a: + version "0.1.4-beta" + resolved "https://codeload.github.com/nrenner/leaflet-routing/tar.gz/773314a37940b32b2fec84886611ecbd4d6f3df9" leaflet-sidebar-v2@nrenner/leaflet-sidebar-v2#dev: version "3.0.2"