Merge pull request #621 from tbsmark86/road-quality
Road surface as quality color + highlight matching row when hovering route on map
This commit is contained in:
commit
b3e788eec4
7 changed files with 242 additions and 9 deletions
|
|
@ -567,6 +567,7 @@ table.dataTable thead .sorting {
|
|||
|
||||
table.track-analysis-table tbody tr:hover,
|
||||
table.dataTable.hover tbody tr:hover,
|
||||
table.dataTable.hover tbody tr.hoverRoute,
|
||||
table.dataTable.display tbody tr:hover {
|
||||
background-color: rgba(255, 255, 0, 0.3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ BR.TrackMessages = L.Class.extend({
|
|||
*/
|
||||
trackPolyline: null,
|
||||
|
||||
segments: null,
|
||||
|
||||
initialize: function (map, options) {
|
||||
L.setOptions(this, options);
|
||||
this._map = map;
|
||||
|
|
@ -46,9 +48,12 @@ BR.TrackMessages = L.Class.extend({
|
|||
|
||||
var syncButton = document.getElementById('data-sync-map');
|
||||
L.DomEvent.on(syncButton, 'click', this._toggleSyncMap, this);
|
||||
|
||||
this._mapMouseMoveHandlerBound = this.mapMouseMoveHandler.bind(this);
|
||||
this._mapMouseOutHandlerBound = this.mapMouseOutHandler.bind(this);
|
||||
},
|
||||
|
||||
update: function (polyline, segments) {
|
||||
update: function (polyline, segments, layer) {
|
||||
var i,
|
||||
messages,
|
||||
columns,
|
||||
|
|
@ -56,11 +61,13 @@ BR.TrackMessages = L.Class.extend({
|
|||
data = [];
|
||||
|
||||
if (!this.active) {
|
||||
this.listenMapEvents(layer, false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.trackPolyline = polyline;
|
||||
this.trackEdges = new BR.TrackEdges(segments);
|
||||
this.segments = segments;
|
||||
|
||||
for (i = 0; segments && i < segments.length; i++) {
|
||||
messages = segments[i].feature.properties.messages;
|
||||
|
|
@ -72,6 +79,7 @@ BR.TrackMessages = L.Class.extend({
|
|||
this._destroyTable();
|
||||
|
||||
if (data.length === 0) {
|
||||
this.listenMapEvents(layer, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +104,20 @@ BR.TrackMessages = L.Class.extend({
|
|||
|
||||
$('#datatable tbody tr').hover(L.bind(this._handleHover, this), L.bind(this._handleHoverOut, this));
|
||||
$('#datatable tbody').on('click', 'tr', L.bind(this._toggleSelected, this));
|
||||
|
||||
this.listenMapEvents(layer, true);
|
||||
},
|
||||
|
||||
listenMapEvents: function (layer, on) {
|
||||
if (layer) {
|
||||
if (on) {
|
||||
layer.on('mousemove', this._mapMouseMoveHandlerBound);
|
||||
layer.on('mouseout', this._mapMouseOutHandlerBound);
|
||||
} else {
|
||||
layer.off('mousemove', this._mapMouseMoveHandlerBound);
|
||||
layer.off('mouseout', this._mapMouseOutHandlerBound);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
|
|
@ -209,4 +231,58 @@ BR.TrackMessages = L.Class.extend({
|
|||
button.classList.toggle('active');
|
||||
this.options.syncMap = !this.options.syncMap;
|
||||
},
|
||||
|
||||
mapMouseMoveHandler: function (evt) {
|
||||
// initialize the vars for the closest item calculation
|
||||
let closestPointIdx = null;
|
||||
// large enough to be trumped by any point on the chart
|
||||
let closestDistance = 2 * Math.pow(100, 2);
|
||||
// consider a good enough match if the given point (lat and lng) is within
|
||||
// 1.1 meters of a point on the chart (there are 111,111 meters in a degree)
|
||||
const exactMatchRounding = 1.1 / 111111;
|
||||
|
||||
const point = turf.point([evt.latlng.lng, evt.latlng.lat]);
|
||||
let idxOffset = 0;
|
||||
outer: for (let segment of this.segments) {
|
||||
const bestPointForSegement = turf.nearestPointOnLine(segment.feature, point);
|
||||
if (bestPointForSegement.properties.dist < closestDistance) {
|
||||
closestPointIdx = idxOffset + bestPointForSegement.properties.index;
|
||||
closestDistance = bestPointForSegement.properties.dist;
|
||||
}
|
||||
idxOffset += segment.feature.geometry.coordinates.length;
|
||||
}
|
||||
|
||||
if (closestPointIdx !== null) {
|
||||
// Now map point to next data row
|
||||
let rowIdx = -1;
|
||||
for (let i = 0; i < this.trackEdges.edges.length; i++) {
|
||||
if (closestPointIdx < this.trackEdges.edges[i]) {
|
||||
rowIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowIdx != -1) {
|
||||
// highlight found row
|
||||
const rowObj = this._table.row(rowIdx);
|
||||
if (rowObj && rowObj != this._mapHoveredRow) {
|
||||
if (this._mapHoveredRow) {
|
||||
this._mapHoveredRow.classList.remove('hoverRoute');
|
||||
}
|
||||
this._mapHoveredRow = rowObj.node();
|
||||
this._mapHoveredRow.classList.add('hoverRoute');
|
||||
this._mapHoveredRow.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this._mapHoveredRow) {
|
||||
this._mapHoveredRow.classList.remove('hoverRoute');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mapMouseOutHandler: function () {
|
||||
if (this._mapHoveredRow) {
|
||||
this._mapHoveredRow.classList.remove('hoverRoute');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
pois,
|
||||
circlego,
|
||||
urlHash;
|
||||
|
||||
// By default bootstrap-select use glyphicons
|
||||
$('.selectpicker').selectpicker({
|
||||
iconBase: 'fa',
|
||||
|
|
@ -223,9 +222,10 @@
|
|||
|
||||
function requestUpdate(updatable) {
|
||||
var track = routing.toPolyline(),
|
||||
segments = routing.getSegments();
|
||||
segments = routing.getSegments(),
|
||||
segmentsLayer = routing._segments;
|
||||
|
||||
updatable.update(track, segments);
|
||||
updatable.update(track, segments, segmentsLayer);
|
||||
}
|
||||
|
||||
routingOptions = new BR.RoutingOptions();
|
||||
|
|
@ -342,7 +342,7 @@
|
|||
} else {
|
||||
stats.update(track, segments);
|
||||
}
|
||||
trackMessages.update(track, segments);
|
||||
trackMessages.update(track, segments, segmentsLayer);
|
||||
trackAnalysis.update(track, segments);
|
||||
|
||||
exportRoute.update(latLngs, segments);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,162 @@ BR.RoutingPathQuality = L.Control.extend({
|
|||
},
|
||||
}),
|
||||
},
|
||||
surface: {
|
||||
title: i18next.t('map.route-quality-surface'),
|
||||
icon: 'fa-road',
|
||||
provider: new HotLineQualityProvider({
|
||||
hotlineOptions: {
|
||||
renderer: renderer,
|
||||
palette: {
|
||||
// normal range
|
||||
0.0: 'red',
|
||||
0.45: 'yellow',
|
||||
0.9: 'green',
|
||||
// special value for unknown
|
||||
1.0: '#888888',
|
||||
},
|
||||
// note: without this the lib will get min/max from the actual
|
||||
// values rendering the special values moot
|
||||
min: 0,
|
||||
max: 1,
|
||||
discreteStrokes: true,
|
||||
},
|
||||
valueFunction: (function () {
|
||||
let cache = [];
|
||||
return function (latLng) {
|
||||
var feature = latLng.feature;
|
||||
if (!feature.wayTags) {
|
||||
return 1.0;
|
||||
} else if (cache[feature.wayTags]) {
|
||||
return cache[feature.wayTags];
|
||||
}
|
||||
let data = new URLSearchParams(feature.wayTags.replace(/\s+/g, '&')); // eslint-disable-line compat/compat
|
||||
let surface = null;
|
||||
switch (data.get('surface')) {
|
||||
case 'paved':
|
||||
case 'chipseal':
|
||||
surface = 0.8;
|
||||
break;
|
||||
case 'asphalt':
|
||||
case 'concrete':
|
||||
surface = 1;
|
||||
break;
|
||||
case 'concrete:lanes':
|
||||
case 'concrete:plates':
|
||||
surface = 0.6;
|
||||
case 'sett':
|
||||
case 'gravel':
|
||||
case 'pebblestone':
|
||||
case 'unpaved':
|
||||
surface = 0.5;
|
||||
break;
|
||||
case 'paving_stones':
|
||||
case 'compacted':
|
||||
case 'fine_gravel':
|
||||
surface = 0.7;
|
||||
break;
|
||||
case 'cobblestone':
|
||||
case 'dirt':
|
||||
case 'grass':
|
||||
surface = 0.2;
|
||||
break;
|
||||
case 'unhewn_cobblestone':
|
||||
surface = 0.01;
|
||||
break;
|
||||
case 'ground':
|
||||
case 'earth':
|
||||
surface = 0.3;
|
||||
break;
|
||||
case 'mud':
|
||||
case 'sand':
|
||||
surface = 0.01;
|
||||
break;
|
||||
case null:
|
||||
break;
|
||||
/*default:
|
||||
console.warn('unhandled surface type', data.get('surface'));
|
||||
break;*/
|
||||
}
|
||||
|
||||
// modifier tracktype; also sometimes only tracktype is available
|
||||
if (data.get('highway') === 'track') {
|
||||
switch (data.get('tracktype') || 'unknown') {
|
||||
case 'grade1':
|
||||
if (surface === null) {
|
||||
surface = 0.9;
|
||||
} /* else {
|
||||
don't change
|
||||
} */
|
||||
break;
|
||||
case 'grade2':
|
||||
if (surface === null) {
|
||||
surface = 0.7;
|
||||
} else {
|
||||
surface *= 0.9;
|
||||
}
|
||||
break;
|
||||
case 'grade3':
|
||||
if (surface === null) {
|
||||
surface = 0.4;
|
||||
} else {
|
||||
surface *= 0.8;
|
||||
}
|
||||
break;
|
||||
case 'grade4':
|
||||
if (surface === null) {
|
||||
surface = 0.1;
|
||||
} else {
|
||||
surface *= 0.6;
|
||||
}
|
||||
break;
|
||||
case 'grade5':
|
||||
if (surface === null) {
|
||||
surface = 0.01;
|
||||
} else {
|
||||
surface *= 0.4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (surface !== null) {
|
||||
// modifier for surface quality
|
||||
switch (data.get('smoothness')) {
|
||||
case 'excellent':
|
||||
surface = Math.min(surface * 1.1, 1.0);
|
||||
break;
|
||||
case 'good':
|
||||
surface = Math.min(surface * 1.05, 1.0);
|
||||
break;
|
||||
case 'intermediate':
|
||||
surface *= 0.9;
|
||||
break;
|
||||
case 'bad':
|
||||
surface *= 0.7;
|
||||
break;
|
||||
case 'very_bad':
|
||||
surface *= 0.5;
|
||||
break;
|
||||
case 'horrible':
|
||||
surface *= 0.4;
|
||||
break;
|
||||
case 'very_horrible':
|
||||
surface *= 0.2;
|
||||
break;
|
||||
case 'impassable':
|
||||
surface *= 0.01;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// limit normal values 0-0.9 so 1.0 can be unknown
|
||||
const final = surface === null ? 1.0 : surface * 0.9;
|
||||
cache[feature.wayTags] = final;
|
||||
return final;
|
||||
};
|
||||
})(),
|
||||
}),
|
||||
},
|
||||
cost: {
|
||||
title: i18next.t('map.route-quality-shortcut', { action: '$t(map.route-quality-cost)', key: 'C' }),
|
||||
icon: 'fa-usd',
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@
|
|||
"route-quality-altitude": "Altitude coding",
|
||||
"route-quality-cost": "Cost coding",
|
||||
"route-quality-incline": "Incline coding",
|
||||
"route-quality-surface": "Road surface/quality",
|
||||
"route-quality-shortcut": "{{action}} ({{key}} key to toggle)",
|
||||
"route-tooltip-segment": "Drag to create a new waypoint. Click to toggle straight line.",
|
||||
"route-tooltip-waypoint": "Waypoint. Drag to move; Click to remove.",
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
"leaflet-editable": "1.2.0",
|
||||
"leaflet-filelayer": "1.2.0",
|
||||
"leaflet-geometryutil": "0.10.1",
|
||||
"leaflet-hotline": "0.4.0",
|
||||
"leaflet-hotline": "tbsmark86/Leaflet.hotline#25b2457",
|
||||
"leaflet-osm-notes": "osmlab/leaflet-osm-notes#af2aa811",
|
||||
"leaflet-plugins": "3.4.0",
|
||||
"leaflet-providers": "1.13.0",
|
||||
|
|
|
|||
|
|
@ -8515,10 +8515,9 @@ leaflet-geometryutil@0.10.1:
|
|||
dependencies:
|
||||
leaflet "^1.6.0"
|
||||
|
||||
leaflet-hotline@0.4.0:
|
||||
leaflet-hotline@tbsmark86/Leaflet.hotline#25b2457:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/leaflet-hotline/-/leaflet-hotline-0.4.0.tgz#e01069836a9d2e2c78b1fa1db2013bd03c8ff8d9"
|
||||
integrity sha512-+In6c8WxMsRKMmwQ1m2GmbNxbXvA3WsrOilJGK7l4Sj+mUDh1gdyGMYCIoRBtUeX7lMvBc4KKeEVAlwQERKpxg==
|
||||
resolved "https://codeload.github.com/tbsmark86/Leaflet.hotline/tar.gz/25b24572b99ac66203d857e0fb27f430e2f68448"
|
||||
|
||||
leaflet-osm-notes@osmlab/leaflet-osm-notes#af2aa811:
|
||||
version "0.0.1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue