Merge branch 'master' into feature/distance-marker
This commit is contained in:
commit
a038f0448d
8 changed files with 274 additions and 1 deletions
|
|
@ -188,3 +188,5 @@ Copyright (c) 2018 Norbert Renner and [contributors](https://github.com/nrenner/
|
||||||
Copyright (c) 2013, Makina Corpus; [BSD 3-Clause License](https://github.com/makinacorpus/Leaflet.GeometryUtil/blob/master/LICENSE)
|
Copyright (c) 2013, Makina Corpus; [BSD 3-Clause License](https://github.com/makinacorpus/Leaflet.GeometryUtil/blob/master/LICENSE)
|
||||||
- [leaflet-distance-markers](https://github.com/adoroszlai/leaflet-distance-markers)
|
- [leaflet-distance-markers](https://github.com/adoroszlai/leaflet-distance-markers)
|
||||||
Copyright (c) 2014- Doroszlai Attila, 2016- Phil Whitehurst; [MIT License](https://github.com/adoroszlai/leaflet-distance-markers/blob/master/LICENSE.md)
|
Copyright (c) 2014- Doroszlai Attila, 2016- Phil Whitehurst; [MIT License](https://github.com/adoroszlai/leaflet-distance-markers/blob/master/LICENSE.md)
|
||||||
|
- [Leaflet.hotline](https://github.com/iosphere/Leaflet.hotline)
|
||||||
|
Copyright (c) 2015, iosphere GmbH, Jonas Coch; [Leaflet.hotline](https://github.com/iosphere/Leaflet.hotline/blob/master/LICENSE)
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,8 @@
|
||||||
requestUpdate: requestUpdate
|
requestUpdate: requestUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
|
routingPathQuality = new BR.RoutingPathQuality(map, layersControl);
|
||||||
|
|
||||||
routing = new BR.Routing({
|
routing = new BR.Routing({
|
||||||
routing: {
|
routing: {
|
||||||
router: L.bind(router.getRouteSegment, router)
|
router: L.bind(router.getRouteSegment, router)
|
||||||
|
|
@ -233,6 +235,7 @@
|
||||||
segmentsLayer = routing._segments;
|
segmentsLayer = routing._segments;
|
||||||
|
|
||||||
elevation.update(track, segmentsLayer);
|
elevation.update(track, segmentsLayer);
|
||||||
|
routingPathQuality.update(track, segmentsLayer);
|
||||||
if (BR.conf.transit) {
|
if (BR.conf.transit) {
|
||||||
itinerary.update(track, segments);
|
itinerary.update(track, segments);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -244,6 +247,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
routing.addTo(map);
|
routing.addTo(map);
|
||||||
|
|
||||||
elevation.addBelow(map);
|
elevation.addBelow(map);
|
||||||
|
|
||||||
sidebar = BR.sidebar({
|
sidebar = BR.sidebar({
|
||||||
|
|
@ -273,6 +277,8 @@
|
||||||
|
|
||||||
BR.tracksLoader(map, layersControl, routing);
|
BR.tracksLoader(map, layersControl, routing);
|
||||||
|
|
||||||
|
routingPathQuality.addTo(map);
|
||||||
|
|
||||||
map.addControl(
|
map.addControl(
|
||||||
new BR.OpacitySliderControl({
|
new BR.OpacitySliderControl({
|
||||||
id: 'route',
|
id: 'route',
|
||||||
|
|
|
||||||
202
js/plugin/RoutingPathQuality.js
Normal file
202
js/plugin/RoutingPathQuality.js
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
BR.RoutingPathQuality = L.Control.extend({
|
||||||
|
initialize: function(map, layersControl, options) {
|
||||||
|
L.setOptions(this, options);
|
||||||
|
|
||||||
|
// hotline uses canvas and cannot be moved in front of the svg, so we create another pane
|
||||||
|
map.createPane('routingQualityPane');
|
||||||
|
map.getPane('routingQualityPane').style.zIndex = 450;
|
||||||
|
map.getPane('routingQualityPane').style.pointerEvents = 'none';
|
||||||
|
var renderer = new L.Hotline.Renderer({ pane: 'routingQualityPane' });
|
||||||
|
|
||||||
|
this._routingSegments = L.featureGroup();
|
||||||
|
layersControl.addOverlay(this._routingSegments, i18next.t('map.layer.route-quality'));
|
||||||
|
|
||||||
|
this.providers = {
|
||||||
|
incline: {
|
||||||
|
title: i18next.t('map.route-quality-incline'),
|
||||||
|
icon: 'fa-line-chart',
|
||||||
|
provider: new HotLineQualityProvider({
|
||||||
|
hotlineOptions: {
|
||||||
|
min: -15,
|
||||||
|
max: 15,
|
||||||
|
palette: {
|
||||||
|
0.0: '#ff0000',
|
||||||
|
0.5: '#00ff00',
|
||||||
|
1.0: '#ff0000'
|
||||||
|
},
|
||||||
|
renderer: renderer
|
||||||
|
},
|
||||||
|
valueFunction: function(latLng, prevLatLng) {
|
||||||
|
const deltaAltitude = latLng.alt - prevLatLng.alt, // in m
|
||||||
|
distance = prevLatLng.distanceTo(latLng); // in m
|
||||||
|
if (distance === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (Math.atan(deltaAltitude / distance) * 180) / Math.PI;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
altitude: {
|
||||||
|
title: i18next.t('map.route-quality-altitude'),
|
||||||
|
icon: 'fa-area-chart',
|
||||||
|
provider: new HotLineQualityProvider({
|
||||||
|
hotlineOptions: {
|
||||||
|
renderer: renderer
|
||||||
|
},
|
||||||
|
valueFunction: function(latLng) {
|
||||||
|
return latLng.alt;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cost: {
|
||||||
|
title: i18next.t('map.route-quality-cost'),
|
||||||
|
icon: 'fa-usd',
|
||||||
|
provider: new HotLineQualityProvider({
|
||||||
|
hotlineOptions: {
|
||||||
|
renderer: renderer
|
||||||
|
},
|
||||||
|
valueFunction: function(latLng) {
|
||||||
|
const feature = latLng.feature;
|
||||||
|
return (
|
||||||
|
feature.cost.perKm +
|
||||||
|
feature.cost.elev +
|
||||||
|
feature.cost.turn +
|
||||||
|
feature.cost.node +
|
||||||
|
feature.cost.initial
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.selectedProvider = this.options.initialProvider || 'incline';
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function(map) {
|
||||||
|
var self = this;
|
||||||
|
this._map = map;
|
||||||
|
this._routingSegments.addTo(map);
|
||||||
|
|
||||||
|
var states = [],
|
||||||
|
i,
|
||||||
|
keys = Object.keys(this.providers),
|
||||||
|
l = keys.length;
|
||||||
|
|
||||||
|
for (i = 0; i < l; ++i) {
|
||||||
|
const provider = this.providers[keys[i]];
|
||||||
|
const nextState = keys[(i + 1) % l];
|
||||||
|
states.push({
|
||||||
|
stateName: keys[i],
|
||||||
|
icon: provider.icon,
|
||||||
|
title: provider.title,
|
||||||
|
onClick: function(btn) {
|
||||||
|
btn.state(nextState);
|
||||||
|
self.setProvider(nextState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.routingPathButton = new L.easyButton({
|
||||||
|
states: states
|
||||||
|
}).addTo(map);
|
||||||
|
return new L.DomUtil.create('div');
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(track, layer) {
|
||||||
|
var segments = [];
|
||||||
|
layer.eachLayer(function(layer) {
|
||||||
|
segments.push(layer);
|
||||||
|
});
|
||||||
|
this.segments = segments;
|
||||||
|
this._update(this.segments);
|
||||||
|
},
|
||||||
|
|
||||||
|
setProvider: function(provider) {
|
||||||
|
this.selectedProvider = provider;
|
||||||
|
this._update(this.segments);
|
||||||
|
this._routingSegments.addTo(this._map);
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function(segments) {
|
||||||
|
this._routingSegments.clearLayers();
|
||||||
|
const layers = this.providers[this.selectedProvider].provider.computeLayers(segments);
|
||||||
|
if (layers) {
|
||||||
|
for (var i = 0; i < layers.length; i++) {
|
||||||
|
this._routingSegments.addLayer(layers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var HotLineQualityProvider = L.Class.extend({
|
||||||
|
initialize: function(options) {
|
||||||
|
this.hotlineOptions = options.hotlineOptions;
|
||||||
|
this.valueFunction = options.valueFunction;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeLayers: function(segments) {
|
||||||
|
var layers = [];
|
||||||
|
if (segments) {
|
||||||
|
var segmentLatLngs = [];
|
||||||
|
for (var i = 0; segments && i < segments.length; i++) {
|
||||||
|
const segment = segments[i];
|
||||||
|
segmentLatLngs.push(this._computeLatLngVals(segment));
|
||||||
|
}
|
||||||
|
const flatLines = segmentLatLngs.flat();
|
||||||
|
|
||||||
|
if (flatLines.length > 0) {
|
||||||
|
const hotlineOptions = Object.assign(new Object(), this.hotlineOptions);
|
||||||
|
if (!hotlineOptions.min && !hotlineOptions.max) {
|
||||||
|
const minMax = this._calcMinMaxValues(flatLines);
|
||||||
|
hotlineOptions.min = minMax.min;
|
||||||
|
hotlineOptions.max = minMax.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < segmentLatLngs.length; i++) {
|
||||||
|
const line = segmentLatLngs[i];
|
||||||
|
const hotline = L.hotline(line, hotlineOptions);
|
||||||
|
layers.push(hotline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return layers;
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeLatLngVals: function(segment) {
|
||||||
|
var latLngVals = [],
|
||||||
|
segmentLatLngs = segment.getLatLngs(),
|
||||||
|
segmentLength = segmentLatLngs.length;
|
||||||
|
|
||||||
|
for (var i = 0; i < segmentLength; i++) {
|
||||||
|
const val = this.valueFunction.call(
|
||||||
|
this,
|
||||||
|
segmentLatLngs[i],
|
||||||
|
segmentLatLngs[Math.max(i - 1, 0)],
|
||||||
|
i,
|
||||||
|
segmentLatLngs
|
||||||
|
);
|
||||||
|
latLngVals.push(this._convertToArray(segmentLatLngs[i], val));
|
||||||
|
}
|
||||||
|
return latLngVals;
|
||||||
|
},
|
||||||
|
|
||||||
|
_convertToArray: function(latLng, val) {
|
||||||
|
return [latLng.lat, latLng.lng, val];
|
||||||
|
},
|
||||||
|
|
||||||
|
_calcMinMaxValues: function(lines) {
|
||||||
|
var min = lines[0][2],
|
||||||
|
max = min;
|
||||||
|
for (var i = 1; lines && i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
max = Math.max(max, line[2]);
|
||||||
|
min = Math.min(min, line[2]);
|
||||||
|
}
|
||||||
|
if (min === max) {
|
||||||
|
max = min + 1;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
min: min,
|
||||||
|
max: max
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -154,7 +154,7 @@ L.BRouter = L.Class.extend({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
geojson = JSON.parse(xhr.responseText);
|
geojson = JSON.parse(xhr.responseText);
|
||||||
layer = L.geoJSON(geojson).getLayers()[0];
|
layer = this._assignFeatures(L.geoJSON(geojson).getLayers()[0]);
|
||||||
|
|
||||||
return cb(null, layer);
|
return cb(null, layer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -190,6 +190,55 @@ L.BRouter = L.Class.extend({
|
||||||
xhr.send(profileText);
|
xhr.send(profileText);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_assignFeatures: function(segment) {
|
||||||
|
if (segment.feature.properties.messages) {
|
||||||
|
const featureMessages = segment.feature.properties.messages,
|
||||||
|
segmentLatLngs = segment.getLatLngs(),
|
||||||
|
segmentLength = segmentLatLngs.length;
|
||||||
|
var featureSegmentIndex = 0;
|
||||||
|
|
||||||
|
for (var mi = 1; mi < featureMessages.length; mi++) {
|
||||||
|
const featureLatLng = this._getFeatureLatLng(featureMessages[mi]);
|
||||||
|
|
||||||
|
for (var fi = featureSegmentIndex; fi < segmentLength; fi++) {
|
||||||
|
const segmentLatLng = segmentLatLngs[fi],
|
||||||
|
featureMessage = featureMessages[mi];
|
||||||
|
|
||||||
|
segmentLatLng.feature = this._getFeature(featureMessage);
|
||||||
|
segmentLatLng.message = featureMessage;
|
||||||
|
|
||||||
|
if (featureLatLng.equals(segmentLatLngs[fi])) {
|
||||||
|
featureSegmentIndex = fi + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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])
|
||||||
|
},
|
||||||
|
wayTags: featureMessage[9],
|
||||||
|
nodeTags: featureMessage[10]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getFeatureLatLng: function(message) {
|
||||||
|
var lon = message[0] / 1000000,
|
||||||
|
lat = message[1] / 1000000;
|
||||||
|
|
||||||
|
return L.latLng(lat, lon);
|
||||||
|
},
|
||||||
|
|
||||||
_handleProfileResponse: function(xhr, cb) {
|
_handleProfileResponse: function(xhr, cb) {
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@
|
||||||
"osm": "OpenStreetMap",
|
"osm": "OpenStreetMap",
|
||||||
"osmde": "OpenStreetMap.de",
|
"osmde": "OpenStreetMap.de",
|
||||||
"outdoors": "Outdoor (Thunderforest)",
|
"outdoors": "Outdoor (Thunderforest)",
|
||||||
|
"route-quality": "Routenqualitätscodierung",
|
||||||
"stamen-terrain": "Terrain (Stamen)",
|
"stamen-terrain": "Terrain (Stamen)",
|
||||||
"strava-segments": "Strava Segmente",
|
"strava-segments": "Strava Segmente",
|
||||||
"topo": "OpenTopoMap"
|
"topo": "OpenTopoMap"
|
||||||
|
|
@ -113,6 +114,9 @@
|
||||||
"opacity-slider": "Transparenz von Route und Markern anpassen",
|
"opacity-slider": "Transparenz von Route und Markern anpassen",
|
||||||
"privacy": "Datenschutz",
|
"privacy": "Datenschutz",
|
||||||
"reverse-route": "Route umkehren",
|
"reverse-route": "Route umkehren",
|
||||||
|
"route-quality-altitude": "Höhencodierung",
|
||||||
|
"route-quality-cost": "Kostencodierung",
|
||||||
|
"route-quality-incline": "Steigungscodierung",
|
||||||
"strava-biking": "Zeige Strava Radfahrsegmente",
|
"strava-biking": "Zeige Strava Radfahrsegmente",
|
||||||
"strava-running": "Zeige Strava Läufersegmente",
|
"strava-running": "Zeige Strava Läufersegmente",
|
||||||
"zoomInTitle": "Hineinzoomen",
|
"zoomInTitle": "Hineinzoomen",
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@
|
||||||
"osm": "OpenStreetMap",
|
"osm": "OpenStreetMap",
|
||||||
"osmde": "OpenStreetMap.de",
|
"osmde": "OpenStreetMap.de",
|
||||||
"outdoors": "Outdoors (Thunderforest)",
|
"outdoors": "Outdoors (Thunderforest)",
|
||||||
|
"route-quality": "Route quality coding",
|
||||||
"stamen-terrain": "Terrain (Stamen)",
|
"stamen-terrain": "Terrain (Stamen)",
|
||||||
"strava-segments": "Strava segments",
|
"strava-segments": "Strava segments",
|
||||||
"topo": "OpenTopoMap"
|
"topo": "OpenTopoMap"
|
||||||
|
|
@ -113,6 +114,9 @@
|
||||||
"opacity-slider": "Set transparency of route track and markers",
|
"opacity-slider": "Set transparency of route track and markers",
|
||||||
"privacy": "Privacy",
|
"privacy": "Privacy",
|
||||||
"reverse-route": "Reverse route",
|
"reverse-route": "Reverse route",
|
||||||
|
"route-quality-altitude": "Altitude coding",
|
||||||
|
"route-quality-cost": "Cost coding",
|
||||||
|
"route-quality-incline": "Incline coding",
|
||||||
"strava-biking": "Show Strava biking segments",
|
"strava-biking": "Show Strava biking segments",
|
||||||
"strava-running": "Show Strava running segments",
|
"strava-running": "Show Strava running segments",
|
||||||
"zoomInTitle": "Zoom in",
|
"zoomInTitle": "Zoom in",
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
"leaflet-easybutton": "*",
|
"leaflet-easybutton": "*",
|
||||||
"leaflet-editable": "^1.1.0",
|
"leaflet-editable": "^1.1.0",
|
||||||
"leaflet-elevation": "nrenner/Leaflet.Elevation#dev",
|
"leaflet-elevation": "nrenner/Leaflet.Elevation#dev",
|
||||||
|
"leaflet-hotline": "^0.4.0",
|
||||||
"leaflet-filelayer": "^1.2.0",
|
"leaflet-filelayer": "^1.2.0",
|
||||||
"leaflet-geometryutil": "^0.9.1",
|
"leaflet-geometryutil": "^0.9.1",
|
||||||
"leaflet-plugins": "~3.0.0",
|
"leaflet-plugins": "~3.0.0",
|
||||||
|
|
|
||||||
|
|
@ -4460,6 +4460,11 @@ leaflet-geometryutil@^0.9.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
leaflet ">=0.7.0"
|
leaflet ">=0.7.0"
|
||||||
|
|
||||||
|
leaflet-hotline@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/leaflet-hotline/-/leaflet-hotline-0.4.0.tgz#e01069836a9d2e2c78b1fa1db2013bd03c8ff8d9"
|
||||||
|
integrity sha1-4BBpg2qdLix4sfodsgE70DyP+Nk=
|
||||||
|
|
||||||
leaflet-plugins@~3.0.0:
|
leaflet-plugins@~3.0.0:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/leaflet-plugins/-/leaflet-plugins-3.0.3.tgz#7c727ac79a37636b245dd1adc64e10c61b425864"
|
resolved "https://registry.yarnpkg.com/leaflet-plugins/-/leaflet-plugins-3.0.3.tgz#7c727ac79a37636b245dd1adc64e10c61b425864"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue