Add markers POI layer

This commit is contained in:
Gautier Pelloux-Prayer 2019-08-06 14:27:29 +02:00
parent 998dfaee91
commit fa5af58372
9 changed files with 247 additions and 20 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ nbproject/
/dist /dist
brouter-web.*.zip brouter-web.*.zip
yarn-error.log yarn-error.log
package-lock.json

View file

@ -167,6 +167,9 @@ input#trackname:focus:invalid {
.routing-draw-enabled { .routing-draw-enabled {
cursor: crosshair; cursor: crosshair;
} }
.pois-draw-enabled {
cursor: cell;
}
#map { #map {
/* center error message horizontally */ /* center error message horizontally */

View file

@ -1,8 +1,9 @@
BR.Export = L.Class.extend({ BR.Export = L.Class.extend({
latLngs: [], latLngs: [],
initialize: function(router) { initialize: function(router, pois) {
this.router = router; this.router = router;
this.pois = pois;
this.exportButton = $('#exportButton'); this.exportButton = $('#exportButton');
var trackname = (this.trackname = document.getElementById('trackname')); var trackname = (this.trackname = document.getElementById('trackname'));
this.tracknameAllowedChars = BR.conf.tracknameAllowedChars; this.tracknameAllowedChars = BR.conf.tracknameAllowedChars;
@ -38,7 +39,7 @@ BR.Export = L.Class.extend({
var name = encodeURIComponent(exportForm['trackname'].value); var name = encodeURIComponent(exportForm['trackname'].value);
var includeWaypoints = exportForm['include-waypoints'].checked; var includeWaypoints = exportForm['include-waypoints'].checked;
var uri = this.router.getUrl(this.latLngs, format, name, includeWaypoints); var uri = this.router.getUrl(this.latLngs, this.pois.getMarkers(), format, name, includeWaypoints);
var evt = document.createEvent('MouseEvents'); var evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);

View file

@ -32,7 +32,7 @@
sidebar, sidebar,
drawButton, drawButton,
deleteRouteButton, deleteRouteButton,
drawToolbar, pois,
urlHash, urlHash,
saveWarningShown = false; saveWarningShown = false;
@ -93,7 +93,7 @@
function() { function() {
bootbox.prompt({ bootbox.prompt({
size: 'small', size: 'small',
title: i18next.t('map.delete-route-nogos'), title: i18next.t('map.clear-route'),
inputType: 'checkbox', inputType: 'checkbox',
inputOptions: [ inputOptions: [
{ {
@ -103,6 +103,10 @@
{ {
text: i18next.t('map.delete-nogo-areas'), text: i18next.t('map.delete-nogo-areas'),
value: 'nogo' value: 'nogo'
},
{
text: i18next.t('map.delete-pois'),
value: 'pois'
} }
], ],
value: ['route'], value: ['route'],
@ -114,13 +118,16 @@
if (result.indexOf('nogo') !== -1) { if (result.indexOf('nogo') !== -1) {
nogos.clear(); nogos.clear();
} }
if (result.indexOf('pois') !== -1) {
pois.clear();
}
onUpdate(); onUpdate();
urlHash.onMapMove(); urlHash.onMapMove();
} }
} }
}); });
}, },
i18next.t('map.delete-route-nogos') i18next.t('map.clear-route')
); );
function updateRoute(evt) { function updateRoute(evt) {
@ -160,7 +167,6 @@
} else { } else {
stats = new BR.TrackStats(); stats = new BR.TrackStats();
} }
exportRoute = new BR.Export(router);
elevation = new BR.Elevation(); elevation = new BR.Elevation();
profile = new BR.Profile(); profile = new BR.Profile();
@ -205,6 +211,12 @@
styles: BR.conf.routingStyles styles: BR.conf.routingStyles
}); });
pois = new BR.PoiMarkers({
routing: routing
});
exportRoute = new BR.Export(router, pois);
routing.on('routing:routeWaypointEnd routing:setWaypointsEnd', function(evt) { routing.on('routing:routeWaypointEnd routing:setWaypointsEnd', function(evt) {
search.clear(); search.clear();
onUpdate(evt && evt.err); onUpdate(evt && evt.err);
@ -245,6 +257,8 @@
routing.addTo(map); routing.addTo(map);
elevation.addBelow(map); elevation.addBelow(map);
pois.addTo(map);
sidebar = BR.sidebar({ sidebar = BR.sidebar({
defaultTabId: BR.conf.transit ? 'tab_itinerary' : 'tab_profile', defaultTabId: BR.conf.transit ? 'tab_itinerary' : 'tab_profile',
listeningTabs: { listeningTabs: {
@ -257,13 +271,7 @@
} }
nogos.addTo(map); nogos.addTo(map);
L.easyBar([ L.easyBar([drawButton, reverseRouteButton, nogos.getButton(), deletePointButton, deleteRouteButton]).addTo(map);
drawButton,
reverseRouteButton,
nogos.getButton(),
deletePointButton,
deleteRouteButton
]).addTo(map);
nogos.preventRoutePointOnCreate(routing); nogos.preventRoutePointOnCreate(routing);
if (BR.keys.strava) { if (BR.keys.strava) {
@ -304,6 +312,7 @@
return p; return p;
}; };
if (url == null) return; if (url == null) return;
var opts = router.parseUrlParams(url2params(url)); var opts = router.parseUrlParams(url2params(url));
router.setOptions(opts); router.setOptions(opts);
routingOptions.setOptions(opts); routingOptions.setOptions(opts);
@ -315,6 +324,10 @@
routing.clear(); routing.clear();
routing.setWaypoints(opts.lonlats); routing.setWaypoints(opts.lonlats);
} }
if (opts.pois) {
pois.setMarkers(opts.pois);
}
}; };
var onInvalidHashChangeCb = function(params) { var onInvalidHashChangeCb = function(params) {
@ -327,9 +340,13 @@
// do not initialize immediately // do not initialize immediately
urlHash = new L.Hash(null, null); urlHash = new L.Hash(null, null);
// this callback is used to append anything in URL after L.Hash wrote #map=zoom/lat/lng/layer
urlHash.additionalCb = function() { urlHash.additionalCb = function() {
var url = router.getUrl(routing.getWaypoints(), null).substr('brouter?'.length + 1); var url = router.getUrl(routing.getWaypoints(), pois.getMarkers(), null).substr('brouter?'.length + 1);
// by default brouter use | as separator. To make URL more human-readable, we remplace them with ; for users
url = url.replace(/\|/g, ';'); url = url.replace(/\|/g, ';');
return url.length > 0 ? '&' + url : null; return url.length > 0 ? '&' + url : null;
}; };
urlHash.onHashChangeCb = onHashChangeCb; urlHash.onHashChangeCb = onHashChangeCb;
@ -346,6 +363,7 @@
routingOptions.on('update', urlHash.onMapMove, urlHash); routingOptions.on('update', urlHash.onMapMove, urlHash);
nogos.on('update', urlHash.onMapMove, urlHash); nogos.on('update', urlHash.onMapMove, urlHash);
pois.on('update', urlHash.onMapMove, urlHash);
// waypoint add, move, delete (but last) // waypoint add, move, delete (but last)
routing.on('routing:routeWaypointEnd', urlHash.onMapMove, urlHash); routing.on('routing:routeWaypointEnd', urlHash.onMapMove, urlHash);
// delete last waypoint // delete last waypoint
@ -394,6 +412,8 @@
}); });
} }
L.AwesomeMarkers.Icon.prototype.options.prefix = 'fa';
i18next i18next
.use(window.i18nextXHRBackend) .use(window.i18nextXHRBackend)
.use(window.i18nextBrowserLanguageDetector) .use(window.i18nextBrowserLanguageDetector)

147
js/plugin/POIMarkers.js Normal file
View file

@ -0,0 +1,147 @@
BR.PoiMarkers = L.Control.extend({
markersLayer: null,
options: {
routing: null,
shortcut: {
draw: {
enable: 80, // char code for 'p'
disable: 27 // char code for 'ESC'
}
}
},
onAdd: function(map) {
var self = this;
this.map = map;
this.markersLayer = L.layerGroup([]).addTo(map);
this.drawButton = L.easyButton({
states: [
{
stateName: 'activate-poi',
icon: 'fa-hand-o-right',
onClick: function() {
self.draw(true);
},
title: i18next.t('map.draw-poi-start')
},
{
stateName: 'deactivate-poi',
icon: 'fa-hand-o-right active',
onClick: function() {
self.draw(false);
},
title: i18next.t('map.draw-poi-stop')
}
]
}).addTo(map);
map.on('routing:draw-start', function() {
self.draw(false);
});
var container = new L.DomUtil.create('div');
// keys not working when map container does not have focus, use document instead
L.DomEvent.removeListener(container, 'keyup', this._keyupListener);
L.DomEvent.addListener(document, 'keyup', this._keyupListener, this);
return container;
},
draw: function(enable) {
this.drawButton.state(enable ? 'deactivate-poi' : 'activate-poi');
if (enable) {
this.options.routing.draw(false);
this.map.on('click', this.onMapClick, this);
L.DomUtil.addClass(this.map.getContainer(), 'pois-draw-enabled');
} else {
this.map.off('click', this.onMapClick, this);
L.DomUtil.removeClass(this.map.getContainer(), 'pois-draw-enabled');
}
},
_keyupListener: function(e) {
// Suppress shortcut handling when a text input field is focussed
if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') {
return;
}
if (e.keyCode === this.options.shortcut.draw.disable) {
this.draw(false);
} else if (e.keyCode === this.options.shortcut.draw.enable) {
this.draw(true);
}
},
onMapClick: function(e) {
var self = this;
bootbox.prompt({
title: i18next.t('map.enter-poi-name'),
callback: function(result) {
if (result !== null) {
self.addMarker(e.latlng, result);
}
}
});
},
addMarker: function(latlng, name) {
// this method must only be used to sanitize for textContent.
// do NOT use it to sanitize any attribute,
// see https://web.archive.org/web/20121208091505/http://benv.ca/2012/10/4/you-are-probably-misusing-DOM-text-methods/
var sanitizeHTMLContent = function(str) {
var temp = document.createElement('div');
temp.textContent = str;
return temp.innerHTML;
};
var icon = L.AwesomeMarkers.icon({
icon: 'star',
markerColor: 'cadetblue'
});
var content = sanitizeHTMLContent(name) + '<br>';
content += "<button id='remove-poi-marker' class='btn btn-secondary'><i class='fa fa-trash'></i></button>";
var self = this;
var marker = L.marker(latlng, { icon: icon, draggable: true, name: name })
.bindPopup(content)
.on('dragend', function() {
self.fire('update');
})
.on('popupopen', function() {
$('#remove-poi-marker').on('click', function(e) {
self.markersLayer.removeLayer(marker);
e.preventDefault();
self.fire('update');
});
})
.addTo(this.markersLayer);
},
clear: function() {
this.markersLayer.clearLayers();
},
setMarkers: function(latLngNames) {
this.clear();
if (!latLngNames) return;
for (var i = 0; i < latLngNames.length; i++) {
var r = latLngNames[i];
this.addMarker(r.latlng, r.name);
}
},
getMarkers: function() {
return this.markersLayer.getLayers().map(function(it) {
return {
latlng: it._latlng,
name: it.options.name
};
});
}
});
BR.PoiMarkers.include(L.Evented.prototype);

View file

@ -38,7 +38,7 @@ L.BRouter = L.Class.extend({
L.setOptions(this, options); L.setOptions(this, options);
}, },
getUrlParams: function(latLngs, format) { getUrlParams: function(latLngs, pois, format) {
params = {}; params = {};
if (this._getLonLatsString(latLngs) != null) params.lonlats = this._getLonLatsString(latLngs); if (this._getLonLatsString(latLngs) != null) params.lonlats = this._getLonLatsString(latLngs);
@ -53,6 +53,8 @@ L.BRouter = L.Class.extend({
if (this.options.profile != null) params.profile = this.options.profile; if (this.options.profile != null) params.profile = this.options.profile;
if (pois && this._getLonLatsNameString(pois) != null) params.pois = this._getLonLatsNameString(pois);
params.alternativeidx = this.options.alternative; params.alternativeidx = this.options.alternative;
if (format != null) { if (format != null) {
@ -91,15 +93,18 @@ L.BRouter = L.Class.extend({
if (params.profile) { if (params.profile) {
opts.profile = params.profile; opts.profile = params.profile;
} }
if (params.pois) {
opts.pois = this._parseLonLatNames(params.pois);
}
return opts; return opts;
}, },
getUrl: function(latLngs, format, trackname, exportWaypoints) { getUrl: function(latLngs, pois, format, trackname, exportWaypoints) {
var urlParams = this.getUrlParams(latLngs, format); var urlParams = this.getUrlParams(latLngs, pois, format);
var args = []; var args = [];
if (urlParams.lonlats != null && urlParams.lonlats.length > 0) if (urlParams.lonlats != null && urlParams.lonlats.length > 0)
args.push(L.Util.template('lonlats={lonlats}', urlParams)); args.push(L.Util.template('lonlats={lonlats}', urlParams));
if (urlParams.pois != null && urlParams.pois.length > 0) args.push(L.Util.template('pois={pois}', urlParams));
if (urlParams.nogos != null) args.push(L.Util.template('nogos={nogos}', urlParams)); if (urlParams.nogos != null) args.push(L.Util.template('nogos={nogos}', urlParams));
if (urlParams.polylines != null) args.push(L.Util.template('polylines={polylines}', urlParams)); if (urlParams.polylines != null) args.push(L.Util.template('polylines={polylines}', urlParams));
if (urlParams.polygons != null) args.push(L.Util.template('polygons={polygons}', urlParams)); if (urlParams.polygons != null) args.push(L.Util.template('polygons={polygons}', urlParams));
@ -120,7 +125,7 @@ L.BRouter = L.Class.extend({
}, },
getRoute: function(latLngs, cb) { getRoute: function(latLngs, cb) {
var url = this.getUrl(latLngs, 'geojson'), var url = this.getUrl(latLngs, null, 'geojson'),
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
if (!url) { if (!url) {
@ -231,6 +236,39 @@ L.BRouter = L.Class.extend({
return lonlats; return lonlats;
}, },
_getLonLatsNameString: function(latLngNames) {
var s = '';
for (var i = 0; i < latLngNames.length; i++) {
s += this._formatLatLng(latLngNames[i].latlng);
s += L.BRouter.NUMBER_SEPARATOR;
s += encodeURIComponent(latLngNames[i].name);
if (i < latLngNames.length - 1) {
s += L.BRouter.GROUP_SEPARATOR;
}
}
return s;
},
_parseLonLatNames: function(s) {
var groups,
part,
lonlatnames = [];
if (!s) {
return lonlatnames;
}
groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) {
// lng,lat,name
part = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
lonlatnames.push({ latlng: L.latLng(part[1], part[0]), name: decodeURIComponent(part[2]) });
}
return lonlatnames;
},
_getNogosString: function(nogos) { _getNogosString: function(nogos) {
var s = ''; var s = '';
for (var i = 0, circle; i < nogos.length; i++) { for (var i = 0, circle; i < nogos.length; i++) {

View file

@ -76,14 +76,18 @@
"map": { "map": {
"attribution-osm-long": "OpenStreetMap contributors", "attribution-osm-long": "OpenStreetMap contributors",
"attribution-osm-short": "OpenStreetMap", "attribution-osm-short": "OpenStreetMap",
"clear-route": "Clear route data",
"copyright": "Copyright", "copyright": "Copyright",
"cycling": "Cycling", "cycling": "Cycling",
"delete-last-point": "Delete last point", "delete-last-point": "Delete last point",
"delete-nogo-areas": "Delete all no-go areas", "delete-nogo-areas": "Delete all no-go areas",
"delete-pois": "Delete all points of interest",
"delete-route": "Delete route", "delete-route": "Delete route",
"delete-route-nogos": "Delete route and nogos", "draw-poi-start": "Draw points of interest (P key)",
"draw-poi-stop": "Stop drawing points of interest (ESC key)",
"draw-route-start": "Draw route (D key)", "draw-route-start": "Draw route (D key)",
"draw-route-stop": "Stop drawing route (ESC key)", "draw-route-stop": "Stop drawing route (ESC key)",
"enter-poi-name": "Enter Point of Interest name",
"hikebike-hillshading": "Hillshading", "hikebike-hillshading": "Hillshading",
"hiking": "Hiking", "hiking": "Hiking",
"layer": { "layer": {

View file

@ -52,6 +52,7 @@
"leaflet-routing": "nrenner/leaflet-routing#dev", "leaflet-routing": "nrenner/leaflet-routing#dev",
"leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev", "leaflet-sidebar-v2": "nrenner/leaflet-sidebar-v2#dev",
"leaflet-triangle-marker": "^1.0.1", "leaflet-triangle-marker": "^1.0.1",
"leaflet.awesome-markers": "^2.0.5",
"leaflet.locatecontrol": "^0.60.0", "leaflet.locatecontrol": "^0.60.0",
"leaflet.snogylop": "^0.4.0", "leaflet.snogylop": "^0.4.0",
"leaflet.stravasegments": "2.3.2", "leaflet.stravasegments": "2.3.2",
@ -175,6 +176,13 @@
"leaflet.stravasegments": { "leaflet.stravasegments": {
"main": "dist/index.js" "main": "dist/index.js"
}, },
"leaflet.awesome-markers": {
"main": [
"dist/leaflet.awesome-markers.js",
"dist/leaflet.awesome-markers.css",
"dist/images/*.png"
]
},
"font-awesome": { "font-awesome": {
"main": [ "main": [
"css/font-awesome.css", "css/font-awesome.css",

View file

@ -4453,6 +4453,11 @@ leaflet-triangle-marker@^1.0.1:
resolved "https://registry.yarnpkg.com/leaflet-triangle-marker/-/leaflet-triangle-marker-1.0.1.tgz#0775ee4903c6c0b71b20023dfb295dfc026bc23d" resolved "https://registry.yarnpkg.com/leaflet-triangle-marker/-/leaflet-triangle-marker-1.0.1.tgz#0775ee4903c6c0b71b20023dfb295dfc026bc23d"
integrity sha512-nK2Wtp5tUPwg9STrE78oKLQJvcDZTMU5i+4la1zhHZKZcjoTl9oVVd0f6keMx+wN70IiHsoDkHCFzIiVcCs9eQ== integrity sha512-nK2Wtp5tUPwg9STrE78oKLQJvcDZTMU5i+4la1zhHZKZcjoTl9oVVd0f6keMx+wN70IiHsoDkHCFzIiVcCs9eQ==
leaflet.awesome-markers@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/leaflet.awesome-markers/-/leaflet.awesome-markers-2.0.5.tgz#b7b0210d87e2566359bf478c1ab3ab07c7246f30"
integrity sha512-Ne/xDjkGyaujwNVVkv2tyXQUV0ZW7gZ0Mo0FuQY4jp2qWrvXi0hwDBvmZyF/8YOvybyMabTMM/mFWCTd1jZIQA==
leaflet.locatecontrol@^0.60.0: leaflet.locatecontrol@^0.60.0:
version "0.60.0" version "0.60.0"
resolved "https://registry.yarnpkg.com/leaflet.locatecontrol/-/leaflet.locatecontrol-0.60.0.tgz#fc7be657ca9b7e8b8ba7263e52b0bb902b7cd965" resolved "https://registry.yarnpkg.com/leaflet.locatecontrol/-/leaflet.locatecontrol-0.60.0.tgz#fc7be657ca9b7e8b8ba7263e52b0bb902b7cd965"