Merge remote-tracking branch 'upstream/master' into heightgraph-transpiled

This commit is contained in:
alexcojocaru 2020-12-21 21:23:04 -08:00
commit 3c1f5d0d6d
62 changed files with 4486 additions and 3554 deletions

View file

@ -3,14 +3,14 @@ BR.BingLayer = L.BingLayer.extend({
maxZoom: 19,
attribution:
'<a target="_blank" href="https://www.bing.com/maps/">Bing Maps</a>' +
' (<a target="_blank" href="https://go.microsoft.com/?linkid=9710837">TOU</a>)'
' (<a target="_blank" href="https://go.microsoft.com/?linkid=9710837">TOU</a>)',
},
initialize: function(key, options) {
initialize: function (key, options) {
L.BingLayer.prototype.initialize.call(this, key, options);
this._logo = L.control({ position: 'bottomleft' });
this._logo.onAdd = function(map) {
this._logo.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'bing-logo');
this._div.innerHTML =
'<img src="https://www.microsoft.com/maps/images/branding/Bing%20logo%20white_50px-19px.png">';
@ -18,13 +18,13 @@ BR.BingLayer = L.BingLayer.extend({
};
},
onAdd: function(map) {
onAdd: function (map) {
L.BingLayer.prototype.onAdd.call(this, map);
map.addControl(this._logo);
},
onRemove: function(map) {
onRemove: function (map) {
L.BingLayer.prototype.onRemove.call(this, map);
map.removeControl(this._logo);
}
},
});

248
js/plugin/CircleGoArea.js Normal file
View file

@ -0,0 +1,248 @@
BR.CircleGoArea = L.Control.extend({
circleLayer: null,
outsideAreaRenderer: L.svg({ padding: 1 }),
options: {
radius: 1000, // in meters
shortcut: {
draw: {
enable: 73, // char code for 'i'
disable: 27, // char code for 'ESC'
},
},
},
initialize: function (routing, nogos, pois) {
this.routing = routing;
this.nogos = nogos;
this.pois = pois;
},
onAdd: function (map) {
var self = this;
this.map = map;
this.circleLayer = L.layerGroup([]).addTo(map);
var radiusKm = (this.options.radius / 1000).toFixed();
this.drawButton = L.easyButton({
states: [
{
stateName: 'activate-circlego',
icon: 'fa-circle-o',
onClick: function () {
self.draw(true);
},
title: i18next.t('keyboard.generic-shortcut', {
action: i18next.t('map.draw-circlego-start', { radius: radiusKm }),
key: 'I',
}),
},
{
stateName: 'deactivate-circlego',
icon: 'fa-circle-o active',
onClick: function () {
self.draw(false);
},
title: i18next.t('keyboard.generic-shortcut', {
action: i18next.t('map.draw-circlego-stop', { radius: radiusKm }),
key: '$t(keyboard.escape)',
}),
},
],
});
map.on('routing:draw-start', function () {
self.draw(false);
});
L.DomEvent.addListener(document, 'keydown', this._keydownListener, this);
var container = new L.DomUtil.create('div');
return container;
},
draw: function (enable) {
this.drawButton.state(enable ? 'deactivate-circlego' : 'activate-circlego');
if (enable) {
this.routing.draw(false);
this.pois.draw(false);
this.map.on('click', this.onMapClick, this);
L.DomUtil.addClass(this.map.getContainer(), 'circlego-draw-enabled');
} else {
this.map.off('click', this.onMapClick, this);
L.DomUtil.removeClass(this.map.getContainer(), 'circlego-draw-enabled');
}
},
_keydownListener: function (e) {
if (!BR.Util.keyboardShortcutsAllowed(e)) {
return;
}
if (e.keyCode === this.options.shortcut.draw.disable) {
this.draw(false);
} else if (e.keyCode === this.options.shortcut.draw.enable) {
this.draw(true);
}
},
setNogoCircle: function (center) {
if (center) {
var polygon = this.circleToPolygon(center, this.options.radius);
var geoJson = JSON.stringify(polygon);
$('#nogoJSON').val(geoJson);
$('#nogoBuffer').val(0);
this.nogos.uploadNogos();
var polygonClone = JSON.parse(geoJson);
this.setOutsideArea(polygonClone);
} else {
this.nogos.clear();
this.map.removeLayer(this.outsideArea);
}
},
setOutsideArea: function (polygon) {
var inner = polygon.features[0].geometry.coordinates.concat(polygon.features[1].geometry.coordinates);
var world = [
[180, 90],
[-180, 90],
[-180, -90],
[180, -90],
[180, 90],
];
polygon.features[0].geometry.coordinates = [world, inner];
polygon.features[0].geometry.type = 'Polygon';
polygon.features.pop();
if (this.outsideArea) {
this.map.removeLayer(this.outsideArea);
}
this.outsideArea = L.geoJson(polygon, {
renderer: this.outsideAreaRenderer,
style: function (feature) {
return {
weight: 2,
color: 'black',
opacity: 0.4,
fillColor: 'black',
fillOpacity: 0.4,
className: 'circlego-outside',
};
},
})
.on('click', L.DomEvent.stop)
.addTo(this.map);
},
onMapClick: function (e) {
this.setCircle([e.latlng.lng, e.latlng.lat], false);
},
setCircle: function (center, skipNogo) {
var self = this;
var icon = L.VectorMarkers.icon({
icon: 'home',
markerColor: BR.conf.markerColors.circlego,
});
var marker = L.marker([center[1], center[0]], { icon: icon, draggable: true, name: name })
.on('dragend', function (e) {
self.setNogoCircle([e.target.getLatLng().lng, e.target.getLatLng().lat]);
})
.on('click', function () {
var drawing = self.drawButton.state() == 'deactivate-circlego';
if (drawing) {
self.circleLayer.removeLayer(marker);
self.setNogoCircle(undefined);
}
});
this.clear();
marker.addTo(this.circleLayer);
if (!skipNogo) {
this.setNogoCircle(center);
} else {
var polygon = this.circleToPolygon(center, this.options.radius);
this.setOutsideArea(polygon);
}
this.draw(false);
},
clear: function () {
this.circleLayer.clearLayers();
},
getButton: function () {
return this.drawButton;
},
getCircle: function () {
var circle = this.circleLayer.getLayers().map(function (it) {
return it.getLatLng();
});
if (circle && circle.length) {
return [circle[0].lng.toFixed(6), circle[0].lat.toFixed(6), this.options.radius].join(',');
} else {
return null;
}
},
toRadians: function (angleInDegrees) {
return (angleInDegrees * Math.PI) / 180;
},
toDegrees: function (angleInRadians) {
return (angleInRadians * 180) / Math.PI;
},
offset: function (c1, distance, bearing) {
var lon1 = this.toRadians(c1[0]);
var lat1 = this.toRadians(c1[1]);
var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
var lon =
lon1 +
Math.atan2(
Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
);
return [this.toDegrees(lon), this.toDegrees(lat)];
},
circleToPolygon: function (center, radius, numberOfSegments) {
var n = numberOfSegments ? numberOfSegments : 32;
var inner = [];
for (var i = 0; i < n; ++i) {
inner.push(this.offset(center, radius, (2 * Math.PI * -i) / n));
}
inner.push(inner[0]);
/* hack: it seems there is a bug when using a single closed ring line,
cf. https://github.com/nrenner/brouter-web/issues/349#issue-755514458
so instead we use 2 half rings to ensure we properly close the area */
return {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: inner.slice(n / 2 - 1),
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: inner.slice(0, n / 2 + 1),
},
},
],
};
},
});
BR.CircleGoArea.include(L.Evented.prototype);

View file

@ -1,4 +1,4 @@
BR.Heightgraph = function(map, layersControl, routing, pois) {
BR.Heightgraph = function (map, layersControl, routing, pois) {
Heightgraph = L.Control.Heightgraph.extend({
options: {
width: $('#map').outerWidth(),
@ -6,64 +6,64 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
top: 15,
right: 30,
bottom: 30,
left: 70
left: 70,
},
expandControls: false,
mappings: {
gradient: {
'-5': {
text: '- 16%+',
color: '#028306'
color: '#028306',
},
'-4': {
text: '- 10-15%',
color: '#2AA12E'
color: '#2AA12E',
},
'-3': {
text: '- 7-9%',
color: '#53BF56'
color: '#53BF56',
},
'-2': {
text: '- 4-6%',
color: '#7BDD7E'
color: '#7BDD7E',
},
'-1': {
text: '- 1-3%',
color: '#A4FBA6'
color: '#A4FBA6',
},
'0': {
0: {
text: '0%',
color: '#ffcc99'
color: '#ffcc99',
},
'1': {
1: {
text: '1-3%',
color: '#F29898'
color: '#F29898',
},
'2': {
2: {
text: '4-6%',
color: '#E07575'
color: '#E07575',
},
'3': {
3: {
text: '7-9%',
color: '#CF5352'
color: '#CF5352',
},
'4': {
4: {
text: '10-15%',
color: '#BE312F'
color: '#BE312F',
},
'5': {
5: {
text: '16%+',
color: '#AD0F0C'
}
}
color: '#AD0F0C',
},
},
},
// extra options
shortcut: {
toggle: 69 // char code for 'e'
}
toggle: 69, // char code for 'e'
},
},
addBelow: function(map) {
addBelow: function (map) {
// waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66
// this.width($('#map').outerWidth());
this.options.width = $('#content').outerWidth();
@ -89,12 +89,12 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
var self = this;
var container = $('#elevation-chart');
$(window).resize(function() {
$(window).resize(function () {
// avoid useless computations if the chart is not visible
if (container.is(':visible')) {
self.resize({
width: container.width(),
height: container.height()
height: container.height(),
});
}
});
@ -103,10 +103,10 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
// The resize must be called after the animation (i.e. 'shown.bs.collapse')
// and cannot be called before the animation (i.e. 'show.bs.collapse'),
// for the container has the old width pre animation and new width post animation.
container.on('shown.bs.collapse', function() {
container.on('shown.bs.collapse', function () {
self.resize({
width: container.width(),
height: container.height()
height: container.height(),
});
});
@ -114,9 +114,9 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
this.update();
},
initCollapse: function(map) {
initCollapse: function (map) {
var self = this;
var onHide = function() {
var onHide = function () {
$('#elevation-btn').removeClass('active');
// we must fetch tiles that are located behind elevation-chart
map._onResize();
@ -125,7 +125,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
localStorage.removeItem(this.id);
}
};
var onShow = function() {
var onShow = function () {
$('#elevation-btn').addClass('active');
if (this.id && BR.Util.localStorageAvailable()) {
@ -136,20 +136,20 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
$('#elevation-chart')
.on('hidden.bs.collapse', onHide)
.on('shown.bs.collapse', onShow)
.each(function() {
.each(function () {
if (this.id && BR.Util.localStorageAvailable() && localStorage[this.id] === 'true') {
self.shouldRestoreChart = true;
}
});
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggle) {
$('#elevation-btn').click();
}
},
update: function(track, layer) {
update: function (track, layer) {
// bring height indicator to front, because of track casing in BR.Routing
if (this._mouseHeightFocus) {
var g = this._mouseHeightFocus._groups[0][0].parentNode;
@ -186,7 +186,7 @@ BR.Heightgraph = function(map, layersControl, routing, pois) {
}
$('#elevation-chart').collapse('hide');
}
}
},
});
var heightgraphControl = new Heightgraph();

View file

@ -3,9 +3,9 @@ BR.NogoAreas = L.Control.extend({
shortcut: {
draw: {
enable: 78, // char code for 'n'
disable: 27 // char code for 'ESC'
}
}
disable: 27, // char code for 'ESC'
},
},
},
statics: {
@ -16,7 +16,7 @@ BR.NogoAreas = L.Control.extend({
MSG_ENABLED:
'&square; = move / resize, <span class="fa fa-trash-o"></span> = delete,<br>click nogo to quit editing',
STATE_CREATE: 'no-go-create',
STATE_CANCEL: 'cancel-no-go-create'
STATE_CANCEL: 'cancel-no-go-create',
},
style: {
@ -25,27 +25,27 @@ BR.NogoAreas = L.Control.extend({
opacity: 0.5,
fillColor: null, //same as color by default
fillOpacity: 0.2,
dashArray: null
dashArray: null,
},
editStyle: {
color: '#fe57a1',
opacity: 0.6,
dashArray: '10, 10',
fillOpacity: 0.1
fillOpacity: 0.1,
},
initialize: function() {
initialize: function () {
this._wasRouteDrawing = false;
},
onAdd: function(map) {
onAdd: function (map) {
var self = this;
$('#submitNogos').on('click', L.bind(this.uploadNogos, this));
this.drawnItems = new L.FeatureGroup().addTo(map);
this.drawnItems.on('click', function(e) {
this.drawnItems.on('click', function (e) {
L.DomEvent.stop(e);
e.layer.toggleEdit();
});
@ -54,10 +54,10 @@ BR.NogoAreas = L.Control.extend({
circleEditorClass: BR.DeletableCircleEditor,
// FeatureGroup instead of LayerGroup to propagate events to members
editLayer: new L.FeatureGroup().addTo(map),
featuresLayer: this.drawnItems
featuresLayer: this.drawnItems,
}));
this.startDrawing = function(control) {
this.startDrawing = function (control) {
// initial radius of 0 to detect click, see DeletableCircleEditor.onDrawingMouseUp
var opts = L.extend({ radius: 0 }, self.style);
editTools.startCircle(null, opts);
@ -65,7 +65,7 @@ BR.NogoAreas = L.Control.extend({
control.state('cancel-no-go-create');
};
this.stopDrawing = function(control) {
this.stopDrawing = function (control) {
editTools.stopDrawing();
control.state('no-go-create');
};
@ -76,15 +76,15 @@ BR.NogoAreas = L.Control.extend({
stateName: BR.NogoAreas.STATE_CREATE,
icon: 'fa-ban',
title: BR.NogoAreas.MSG_BUTTON,
onClick: this.startDrawing
onClick: this.startDrawing,
},
{
stateName: BR.NogoAreas.STATE_CANCEL,
icon: 'fa-ban active',
title: BR.NogoAreas.MSG_BUTTON_CANCEL,
onClick: this.stopDrawing
}
]
onClick: this.stopDrawing,
},
],
});
// prevent instant re-activate when turning off button by both Pointer and Click
@ -95,11 +95,11 @@ BR.NogoAreas = L.Control.extend({
this.editTools.on(
'editable:drawing:end',
function(e) {
function (e) {
self.button.state(BR.NogoAreas.STATE_CREATE);
setTimeout(
L.bind(function() {
L.bind(function () {
// turn editing off after create; async to still fire 'editable:vertex:dragend'
e.layer.disableEdit();
}, this),
@ -111,7 +111,7 @@ BR.NogoAreas = L.Control.extend({
this.editTools.on(
'editable:vertex:dragend editable:deleted',
function(e) {
function (e) {
this._fireUpdate();
},
this
@ -119,14 +119,14 @@ BR.NogoAreas = L.Control.extend({
this.editTools.on(
'editable:enable',
function(e) {
function (e) {
e.layer.setStyle(this.editStyle);
},
this
);
this.editTools.on(
'editable:disable',
function(e) {
function (e) {
e.layer.setStyle(this.style);
},
this
@ -139,7 +139,7 @@ BR.NogoAreas = L.Control.extend({
return L.DomUtil.create('div');
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (!BR.Util.keyboardShortcutsAllowed(e)) {
return;
}
@ -153,34 +153,38 @@ BR.NogoAreas = L.Control.extend({
}
},
displayUploadError: function(message) {
displayUploadError: function (message) {
$('#nogoError').text(message ? message : '');
$('#nogoError').css('display', message ? 'block' : 'none');
},
uploadNogos: function() {
uploadNogos: function () {
var self = this;
var geoJSONPromise;
var nogoJSON = $('#nogoJSON').val(); //hidden
var nogoURL = $('#nogoURL').val();
var nogoFile = $('#nogoFile')[0].files[0];
if (nogoURL) {
if (nogoJSON) {
geoJSONPromise = Promise.resolve(JSON.parse(nogoJSON));
$('#nogoJSON').val(undefined);
} else if (nogoURL) {
// TODO: Handle {{bbox}}
geoJSONPromise = fetch(nogoURL).then(function(response) {
geoJSONPromise = fetch(nogoURL).then(function (response) {
response.json();
});
} else if (nogoFile) {
geoJSONPromise = new Promise(function(resolve, reject) {
geoJSONPromise = new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function() {
reader.onload = function () {
resolve(reader.result);
};
reader.onerror = function() {
reader.onerror = function () {
self.displayUploadError('Could not load file: ' + reader.error.message);
};
reader.readAsText(nogoFile);
}).then(function(response) {
}).then(function (response) {
return JSON.parse(response);
});
} else {
@ -207,10 +211,10 @@ BR.NogoAreas = L.Control.extend({
return false;
}
geoJSONPromise.then(function(response) {
geoJSONPromise.then(function (response) {
// Iterate on features in order to discard features without geometry
var cleanedGeoJSONFeatures = [];
turf.flattenEach(response, function(feature) {
turf.flattenEach(response, function (feature) {
if (turf.getGeom(feature)) {
var maybeBufferedFeature = feature;
// Eventually buffer GeoJSON
@ -227,31 +231,31 @@ BR.NogoAreas = L.Control.extend({
}
var geoJSON = L.geoJson(turf.featureCollection(cleanedGeoJSONFeatures), {
onEachFeature: function(feature, layer) {
onEachFeature: function (feature, layer) {
layer.options.nogoWeight = feature.properties.nogoWeight || nogoWeight;
}
},
});
var nogosPoints = geoJSON.getLayers().filter(function(e) {
var nogosPoints = geoJSON.getLayers().filter(function (e) {
return e.feature.geometry.type === 'Point';
});
nogosPoints = nogosPoints.map(function(item) {
nogosPoints = nogosPoints.map(function (item) {
var radius = item.feature.properties.radius || nogoRadius;
if (radius > 0) {
return L.circle(item.getLatLng(), { radius: radius });
}
return null;
});
nogosPoints = nogosPoints.filter(function(e) {
nogosPoints = nogosPoints.filter(function (e) {
return e;
});
self.setOptions({
nogos: nogosPoints,
polygons: geoJSON.getLayers().filter(function(e) {
polygons: geoJSON.getLayers().filter(function (e) {
return e.feature.geometry.type === 'Polygon';
}),
polylines: geoJSON.getLayers().filter(function(e) {
polylines: geoJSON.getLayers().filter(function (e) {
return e.feature.geometry.type === 'LineString';
})
}),
});
self._fireUpdate();
self.displayUploadError(undefined);
@ -261,10 +265,10 @@ BR.NogoAreas = L.Control.extend({
},
// prevent route waypoint added after circle create (map click after up)
preventRoutePointOnCreate: function(routing) {
preventRoutePointOnCreate: function (routing) {
this.editTools.on(
'editable:drawing:start',
function(e) {
function (e) {
this._wasRouteDrawing = routing.isDrawing();
routing.draw(false);
},
@ -274,9 +278,9 @@ BR.NogoAreas = L.Control.extend({
// after create
this.editTools.on(
'editable:drawing:end',
function(e) {
function (e) {
if (this._wasRouteDrawing) {
setTimeout(function() {
setTimeout(function () {
routing.draw(true);
}, 0);
}
@ -285,21 +289,21 @@ BR.NogoAreas = L.Control.extend({
);
},
getOptions: function() {
getOptions: function () {
return {
nogos: this.drawnItems.getLayers().filter(function(e) {
nogos: this.drawnItems.getLayers().filter(function (e) {
return e instanceof L.Circle;
}),
polygons: this.drawnItems.getLayers().filter(function(e) {
polygons: this.drawnItems.getLayers().filter(function (e) {
return e instanceof L.Polygon;
}),
polylines: this.drawnItems.getLayers().filter(function(e) {
polylines: this.drawnItems.getLayers().filter(function (e) {
return e instanceof L.Polyline && !(e instanceof L.Polygon);
})
}),
};
},
setOptions: function(options) {
setOptions: function (options) {
var nogos = options.nogos;
var polylines = options.polylines;
var polygons = options.polygons;
@ -324,30 +328,30 @@ BR.NogoAreas = L.Control.extend({
}
},
_clear: function() {
_clear: function () {
this.drawnItems.clearLayers();
},
clear: function() {
clear: function () {
this._clear();
this._fireUpdate();
},
_fireUpdate: function() {
_fireUpdate: function () {
this.fire('update', { options: this.getOptions() });
},
getFeatureGroup: function() {
getFeatureGroup: function () {
return this.drawnItems;
},
getEditGroup: function() {
getEditGroup: function () {
return this.editTools.editLayer;
},
getButton: function() {
getButton: function () {
return this.button;
}
},
});
BR.NogoAreas.include(L.Evented.prototype);
@ -359,7 +363,7 @@ BR.Editable = L.Editable.extend({
// Also, we generally disable the Tap handler in the map options for route dragging,
// see Map.js, so we always need to enable for drawing.
initialize: function(map, options) {
initialize: function (map, options) {
L.Editable.prototype.initialize.call(this, map, options);
if (!this.map.tap) {
@ -368,7 +372,7 @@ BR.Editable = L.Editable.extend({
}
},
registerForDrawing: function(editor) {
registerForDrawing: function (editor) {
this._tapEnabled = this.map.tap.enabled();
if (!this._tapEnabled) {
this.map.tap.enable();
@ -377,7 +381,7 @@ BR.Editable = L.Editable.extend({
L.Editable.prototype.registerForDrawing.call(this, editor);
},
unregisterForDrawing: function(editor) {
unregisterForDrawing: function (editor) {
if (!this._tapEnabled) {
this.map.tap.disable();
}
@ -385,23 +389,23 @@ BR.Editable = L.Editable.extend({
L.Editable.prototype.unregisterForDrawing.call(this, editor);
},
createVertexIcon: function(options) {
createVertexIcon: function (options) {
return BR.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options);
}
},
});
BR.EditingTooltip = L.Handler.extend({
options: {
closeTimeout: 2000
closeTimeout: 2000,
},
initialize: function(map, editTools, button) {
initialize: function (map, editTools, button) {
this.map = map;
this.editTools = editTools;
this.button = button;
},
addHooks: function() {
addHooks: function () {
// hack: listen to EasyButton click (instead of editable:drawing:start),
// to get mouse position from event for initial tooltip location
L.DomEvent.addListener(this.button.button, 'click', this._addCreate, this);
@ -413,7 +417,7 @@ BR.EditingTooltip = L.Handler.extend({
this.editTools.on('editable:disable', this._disable, this);
},
removeHooks: function() {
removeHooks: function () {
L.DomEvent.removeListener(this.button.button, 'click', this._addCreate, this);
this.editTools.featuresLayer.off('layeradd', this._bind, this);
@ -423,19 +427,19 @@ BR.EditingTooltip = L.Handler.extend({
this.editTools.off('editable:disable', this._disable, this);
},
_bind: function(e) {
_bind: function (e) {
// Position tooltip at bottom of circle, less distracting than
// sticky with cursor or at center.
var layer = e.layer;
layer.bindTooltip(BR.NogoAreas.MSG_DISABLED, {
direction: 'bottom',
className: 'editing-tooltip'
className: 'editing-tooltip',
});
// Override to set position to south instead of center (circle latlng);
// works better with zooming than updating offset to match radius
layer.openTooltip = function(layer, latlng) {
layer.openTooltip = function (layer, latlng) {
if (!latlng && layer instanceof L.Layer) {
latlng = L.latLng(
layer.getBounds().getSouth(),
@ -446,7 +450,7 @@ BR.EditingTooltip = L.Handler.extend({
};
},
_addCreate: function(e) {
_addCreate: function (e) {
// button cancel
if (!this.editTools.drawing()) return;
@ -457,21 +461,21 @@ BR.EditingTooltip = L.Handler.extend({
// offset wrong with 'auto' when switching direction
direction: 'right',
offset: L.point(5, 28),
className: 'editing-tooltip-create'
className: 'editing-tooltip-create',
});
// self-reference hack for _moveTooltip, as tooltip is not bound to layer
tooltip._tooltip = tooltip;
// simulate sticky feature (follow mouse) for map tooltip without layer
var onOffMove = function(e) {
var onOffMove = function (e) {
var onOff = e.type === 'tooltipclose' ? 'off' : 'on';
this._map[onOff]('mousemove', this._moveTooltip, this);
};
this.map.on('tooltipopen', onOffMove, tooltip);
this.map.on('tooltipclose', onOffMove, tooltip);
var onTooltipRemove = function(e) {
var onTooltipRemove = function (e) {
this.map.off('tooltipopen', onOffMove, e.tooltip);
this.map.off('tooltipclose', onOffMove, e.tooltip);
this.map.off('tooltipclose', onTooltipRemove, this);
@ -482,7 +486,7 @@ BR.EditingTooltip = L.Handler.extend({
tooltip.setTooltipContent(BR.NogoAreas.MSG_CREATE);
this.map.openTooltip(tooltip, initialLatLng);
var closeTooltip = function() {
var closeTooltip = function () {
this.map.closeTooltip(tooltip);
};
this.editTools.once('editable:editing editable:drawing:cancel', closeTooltip, this);
@ -493,22 +497,22 @@ BR.EditingTooltip = L.Handler.extend({
}
},
_setCloseTimeout: function(layer) {
var timeoutId = setTimeout(function() {
_setCloseTimeout: function (layer) {
var timeoutId = setTimeout(function () {
layer.closeTooltip();
}, this.options.closeTimeout);
// prevent timer to close tooltip that changed in the meantime
layer.once('tooltipopen', function(e) {
layer.once('tooltipopen', function (e) {
clearTimeout(timeoutId);
});
},
_postCreate: function() {
_postCreate: function () {
// editing is disabled by another handler, tooltip won't stay open before
this.editTools.once(
'editable:disable',
function(e) {
function (e) {
// show for a few seconds, as mouse often not hovering circle after create
e.layer.openTooltip(e.layer);
this._setCloseTimeout(e.layer);
@ -517,54 +521,54 @@ BR.EditingTooltip = L.Handler.extend({
);
},
_enable: function(e) {
_enable: function (e) {
e.layer.setTooltipContent(BR.NogoAreas.MSG_ENABLED);
this.editTools.once(
'editable:editing',
function(e) {
function (e) {
e.layer.closeTooltip();
},
this
);
},
_disable: function(e) {
_disable: function (e) {
e.layer.setTooltipContent(BR.NogoAreas.MSG_DISABLED);
this._setCloseTimeout(e.layer);
}
},
});
BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
_computeDeleteLatLng: function() {
_computeDeleteLatLng: function () {
// While circle is not added to the map, _radius is not set.
var delta = (this.feature._radius || this.feature._mRadius) * Math.cos(Math.PI / 4),
point = this.map.project(this.feature._latlng);
return this.map.unproject([point.x - delta, point.y - delta]);
},
_updateDeleteLatLng: function() {
_updateDeleteLatLng: function () {
this._deleteLatLng.update(this._computeDeleteLatLng());
this._deleteLatLng.__vertex.update();
},
_addDeleteMarker: function() {
_addDeleteMarker: function () {
if (!this.enabled()) return;
this._deleteLatLng = this._computeDeleteLatLng();
return new BR.DeleteMarker(this._deleteLatLng, this);
},
_delete: function() {
_delete: function () {
this.disable();
this.tools.featuresLayer.removeLayer(this.feature);
},
delete: function() {
delete: function () {
this._delete();
this.fireAndForward('editable:deleted');
},
initialize: function(map, feature, options) {
initialize: function (map, feature, options) {
L.Editable.CircleEditor.prototype.initialize.call(this, map, feature, options);
this._deleteLatLng = this._computeDeleteLatLng();
@ -572,7 +576,7 @@ BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
this.editLayer = new L.FeatureGroup();
},
addHooks: function() {
addHooks: function () {
L.Editable.CircleEditor.prototype.addHooks.call(this);
if (this.feature) {
this._addDeleteMarker();
@ -580,12 +584,12 @@ BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
return this;
},
reset: function() {
reset: function () {
L.Editable.CircleEditor.prototype.reset.call(this);
this._addDeleteMarker();
},
onDrawingMouseDown: function(e) {
onDrawingMouseDown: function (e) {
this._deleteLatLng.update(e.latlng);
L.Editable.CircleEditor.prototype.onDrawingMouseDown.call(this, e);
},
@ -593,7 +597,7 @@ BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
// override to cancel/remove created circle when added by click instead of drag, because:
// - without resize, edit handles stacked on top of each other
// - makes event handling more complicated (editable:vertex:dragend not called)
onDrawingMouseUp: function(e) {
onDrawingMouseUp: function (e) {
if (this.feature.getRadius() > 0) {
this.commitDrawing(e);
} else {
@ -604,10 +608,10 @@ BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
},
onVertexMarkerDrag: function(e) {
onVertexMarkerDrag: function (e) {
this._updateDeleteLatLng();
L.Editable.CircleEditor.prototype.onVertexMarkerDrag.call(this, e);
}
},
});
BR.DeleteMarker = L.Marker.extend({
@ -615,11 +619,11 @@ BR.DeleteMarker = L.Marker.extend({
draggable: false,
icon: L.divIcon({
iconSize: BR.Browser.touch ? new L.Point(24, 24) : new L.Point(16, 16),
className: 'leaflet-div-icon fa fa-trash-o nogo-delete-marker'
})
className: 'leaflet-div-icon fa fa-trash-o nogo-delete-marker',
}),
},
initialize: function(latlng, editor, options) {
initialize: function (latlng, editor, options) {
// derived from L.Editable.VertexMarker.initialize
// We don't use this._latlng, because on drag Leaflet replace it while
@ -636,18 +640,18 @@ BR.DeleteMarker = L.Marker.extend({
this.setZIndexOffset(editor.tools._lastZIndex);
},
onAdd: function(map) {
onAdd: function (map) {
L.Marker.prototype.onAdd.call(this, map);
this.on('click', this.onClick);
},
onRemove: function(map) {
onRemove: function (map) {
delete this.latlng.__vertex;
this.off('click', this.onClick);
L.Marker.prototype.onRemove.call(this, map);
},
onClick: function(e) {
onClick: function (e) {
this.editor.delete();
}
},
});

View file

@ -1,17 +1,21 @@
BR.PoiMarkers = L.Control.extend({
markersLayer: null,
circlego: null,
options: {
routing: null,
shortcut: {
draw: {
enable: 80, // char code for 'p'
disable: 27 // char code for 'ESC'
}
}
disable: 27, // char code for 'ESC'
},
},
},
initialize: function (routing) {
this.routing = routing;
this.circlego = null;
},
onAdd: function(map) {
onAdd: function (map) {
var self = this;
this.map = map;
@ -22,26 +26,26 @@ BR.PoiMarkers = L.Control.extend({
{
stateName: 'activate-poi',
icon: 'fa-hand-o-right',
onClick: function() {
onClick: function () {
self.draw(true);
},
title: i18next.t('keyboard.generic-shortcut', { action: '$t(map.draw-poi-start)', key: 'P' })
title: i18next.t('keyboard.generic-shortcut', { action: '$t(map.draw-poi-start)', key: 'P' }),
},
{
stateName: 'deactivate-poi',
icon: 'fa-hand-o-right active',
onClick: function() {
onClick: function () {
self.draw(false);
},
title: i18next.t('keyboard.generic-shortcut', {
action: '$t(map.draw-poi-stop)',
key: '$t(keyboard.escape)'
})
}
]
key: '$t(keyboard.escape)',
}),
},
],
}).addTo(map);
map.on('routing:draw-start', function() {
map.on('routing:draw-start', function () {
self.draw(false);
});
@ -51,10 +55,11 @@ BR.PoiMarkers = L.Control.extend({
return container;
},
draw: function(enable) {
draw: function (enable) {
this.drawButton.state(enable ? 'deactivate-poi' : 'activate-poi');
if (enable) {
this.options.routing.draw(false);
this.routing.draw(false);
if (this.circlego) this.circlego.draw(false);
this.map.on('click', this.onMapClick, this);
L.DomUtil.addClass(this.map.getContainer(), 'pois-draw-enabled');
} else {
@ -63,7 +68,7 @@ BR.PoiMarkers = L.Control.extend({
}
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (!BR.Util.keyboardShortcutsAllowed(e)) {
return;
}
@ -74,22 +79,22 @@ BR.PoiMarkers = L.Control.extend({
}
},
onMapClick: function(e) {
onMapClick: function (e) {
var self = this;
bootbox.prompt({
title: i18next.t('map.enter-poi-name'),
callback: function(result) {
callback: function (result) {
if (result !== null) {
self.addMarker(e.latlng, result);
}
}
},
});
},
addMarker: function(latlng, name) {
addMarker: function (latlng, name) {
var icon = L.VectorMarkers.icon({
icon: 'star',
markerColor: BR.conf.markerColors.poi
markerColor: BR.conf.markerColors.poi,
});
var content = BR.Util.sanitizeHTMLContent(name) + '<br>';
@ -98,11 +103,11 @@ BR.PoiMarkers = L.Control.extend({
var self = this;
var marker = L.marker(latlng, { icon: icon, draggable: true, name: name })
.bindPopup(content)
.on('dragend', function() {
.on('dragend', function () {
self.fire('update');
})
.on('popupopen', function() {
$('#remove-poi-marker').on('click', function(e) {
.on('popupopen', function () {
$('#remove-poi-marker').on('click', function (e) {
self.markersLayer.removeLayer(marker);
e.preventDefault();
self.fire('update');
@ -111,11 +116,11 @@ BR.PoiMarkers = L.Control.extend({
.addTo(this.markersLayer);
},
clear: function() {
clear: function () {
this.markersLayer.clearLayers();
},
setMarkers: function(latLngNames) {
setMarkers: function (latLngNames) {
this.clear();
if (!latLngNames) return;
@ -126,14 +131,14 @@ BR.PoiMarkers = L.Control.extend({
}
},
getMarkers: function() {
return this.markersLayer.getLayers().map(function(it) {
getMarkers: function () {
return this.markersLayer.getLayers().map(function (it) {
return {
latlng: it._latlng,
name: it.options.name
latlng: it.getLatLng(),
name: it.options.name,
};
});
}
},
});
BR.PoiMarkers.include(L.Evented.prototype);

View file

@ -1,395 +1,393 @@
BR.routeLoader = function(map, layersControl, routing, pois) {
RouteLoader = L.Control.extend({
_layerName: 'Tracklayer',
_trackLayer: undefined,
_bounds: undefined,
_testLayer: L.layerGroup().addTo(map),
_trackPoints: [],
_maxTrackPoints: 200,
_closeCanceled: true,
_currentGeoJSON: {},
_options: {
format: undefined,
showTrackLayer: true,
showPointAsPoi: true,
simplifyTolerance: -1,
isTestMode: false,
simplifyLastKnownGood: 0.001
},
setDialogDraggable: function(jqDlgHeader) {
jqDlgHeader.on('mousedown', function(mousedownEvt) {
var $draggable = $(this);
var x = mousedownEvt.pageX - $draggable.offset().left,
y = mousedownEvt.pageY - $draggable.offset().top;
$('body').on('mousemove.draggable', function(mousemoveEvt) {
$draggable.closest('.modal-dialog').offset({
left: mousemoveEvt.pageX - x,
top: mousemoveEvt.pageY - y
});
});
$('body').one('mouseup', function() {
$('body').off('mousemove.draggable');
});
$draggable.closest('.modal').one('bs.modal.hide', function() {
$('body').off('mousemove.draggable');
});
});
},
getSimplifiedCoords: function(tolerance) {
var simplifiedLine = turf.simplify(this._trackPoints.geometry, {
tolerance: tolerance,
highQuality: true
});
return simplifiedLine.coordinates;
},
refreshTestLayer: function() {
this.onBusyChanged(true);
this._testLayer.clearLayers();
var simplifiedLatLngs = L.GeoJSON.coordsToLatLngs(
this.getSimplifiedCoords(this._options.simplifyTolerance)
);
if (simplifiedLatLngs.length > this._maxTrackPoints) {
this.onBusyChanged(false);
return false;
}
for (var i = 0; i < simplifiedLatLngs.length; i++) {
this._testLayer.addLayer(
L.circleMarker(simplifiedLatLngs[i], {
radius: 6,
fill: false,
color: '#FF0000'
})
);
}
this.onBusyChanged(false);
return true;
},
cleanup: function(e) {
this._testLayer.clearLayers();
if (
this._trackLayer &&
map.hasLayer(this._trackLayer) &&
(!this._options.showTrackLayer || this._closeCanceled)
) {
map.removeLayer(this._trackLayer);
layersControl.removeLayer(this._trackLayer);
this._trackLayer = undefined;
}
(this._bounds = undefined), (this._trackPoints = []);
this._currentGeoJSON = {};
this._options = {
ext: 'gpx',
showTrackLayer: true,
showPointAsPoi: true,
simplifyTolerance: -1,
isTestMode: false,
simplifyLastKnownGood: 0.001
};
},
setSliderRange: function() {
$slider = $('#simplify_tolerance');
$slider.prop('min', -500);
var guessedTolerance = this.guessSimplifyTolerance(this._trackPoints);
var guessedLength = this.getSimplifiedCoords(guessedTolerance).length;
if (guessedLength > this._maxTrackPoints) {
this._maxTrackPoints = guessedLength;
$slider.prop('min', 0);
}
$slider.data('lastknowngood', 0);
$slider.data('guess', guessedTolerance.toFixed(20));
$slider.val(0);
this._options.simplifyTolerance = guessedTolerance;
this.refreshTestLayer();
},
onToleranceSlider: function(e) {
var guess = parseFloat($(e.target).data('guess'));
var f = parseFloat(e.target.value);
var frac = parseFloat($(e.target).prop('max'));
if (f > guess)
this._options.simplifyTolerance = guess + Math.pow(f, 2) * (guess / (Math.pow(frac, 2) / 10));
else this._options.simplifyTolerance = Math.abs(guess + Math.pow(f, 3) * (guess / Math.pow(frac, 3)));
if (!this.refreshTestLayer()) {
var iterate = this.findLowestTolerance(
parseInt(e.target.value),
parseInt($(e.target).data('lastknowngood')),
guess,
frac
);
$(e.target).prop('min', iterate);
$(e.target).data('lastknowngood', iterate);
e.target.value = $(e.target).data('lastknowngood');
this._options.simplifyTolerance = this._options.simplifyLastKnownGood = Math.abs(
guess + Math.pow(iterate, 3) * (guess / Math.pow(frac, 3))
);
this.refreshTestLayer();
} else {
this._options.simplifyLastKnownGood = this._options.simplifyTolerance;
$(e.target).data('lastknowngood', e.target.value);
}
},
findLowestTolerance: function(min, max, guess, frac) {
if (Math.abs(max - min) <= 2) return max;
var meridian = Math.round((max + min) / 2);
var tolerance = Math.abs(guess + Math.pow(meridian, 3) * (guess / Math.pow(frac, 3)));
var simplifiedCoords = this.getSimplifiedCoords(tolerance);
if (simplifiedCoords.length > this._maxTrackPoints)
return this.findLowestTolerance(meridian, max, guess, frac);
else return this.findLowestTolerance(min, meridian, guess, frac);
},
onBusyChanged: function(isBusy) {
if (typeof isBusy === undefined) {
isBusy = false;
}
if (isBusy === true) $('#loadedittrackdlg #msg_busy').removeClass('invisible');
else $('#loadedittrackdlg #msg_busy').addClass('invisible');
},
onManualCollapse: function(e) {
//workaround for starting with closed collapse
if ($('#loadedittrackdlg').is(':hidden')) return;
this._options.isTestMode = $(e.target).hasClass('show');
if (this._options.isTestMode) {
this.once('file:loaded', this.setSliderRange, this);
this.convertTrackLocal();
} else this.cleanup();
},
onAdd: function(map) {
$('#loadedittrackdlg').on(
'hidden.bs.modal',
function(e) {
this.cleanup();
}.bind(this)
);
$('#loadedittrackdlg').on(
'show.bs.modal',
function(e) {
$('#manual_collapse').collapse('hide');
this._closeCanceled = true;
}.bind(this)
);
L.DomUtil.get('submitLoadEditTrack').onclick = L.bind(function() {
this._closeCanceled = false;
this.onBusyChanged(true);
if (this._testLayer.getLayers().length > 0) {
this._testLayer.clearLayers();
setTimeout(
function() {
this.addRoutingPoints();
this.onBusyChanged(false);
$('#loadedittrackdlg').modal('hide');
}.bind(this),
0
);
} else
setTimeout(
function() {
this.convertTrackLocal();
$('#loadedittrackdlg').modal('hide');
}.bind(this),
0
);
}, this);
L.DomUtil.get('simplify_tolerance').oninput = L.bind(this.onToleranceSlider, this);
L.DomUtil.get('loadedittrackFile').onchange = L.bind(this.onFileChanged, this);
this.onFileChanged({ target: L.DomUtil.get('loadedittrackFile') });
this.setDialogDraggable($('#loadedittrackdlg .modal-header'));
$('#manual_collapse').collapse('hide');
$('#manual_collapse').on(
'hidden.bs.collapse shown.bs.collapse',
function(e) {
this.onManualCollapse(e);
}.bind(this)
);
// dummy, no own representation, delegating to EasyButton
var dummy = L.DomUtil.create('div');
dummy.hidden = true;
return dummy;
},
onRemove: function(map) {
// Nothing to do here
},
onFileChanged: function(e) {
if (!e.target.files[0]) return;
$(e.target)
.next('label')
.text(e.target.files[0].name);
var testmode = this._options.isTestMode;
this.cleanup();
this._options.isTestMode = testmode;
if (this._options.isTestMode) {
this.once('file:loaded', this.setSliderRange, this);
this.convertTrackLocal();
}
},
setLayerNameFromGeojson: function(geoJSON) {
if (geoJSON.type == 'Feature' && geoJSON.properties && geoJSON.properties.name) {
this._layerName = geoJSON.properties.name;
return;
}
if (geoJSON.type == 'FeatureCollection') {
for (var i = 0; i < geoJSON.features.length; i++) {
if (geoJSON.features[i].properties && geoJSON.features[i].properties.name) {
this._layerName = geoJSON.features[i].properties.name;
return;
}
}
}
},
getOptions: function() {
this._options.showTrackLayer = $('#cb_showtracklayer')[0].checked;
this._options.showPointAsPoi = $('#cb_showpois')[0].checked;
this._options.simplifyTolerance = -1;
this._bounds = undefined;
},
convertTrackLocal: function() {
if ($('#loadedittrackFile')[0].files.length == 0) return;
this.onBusyChanged(true);
this.getOptions();
var trackFile = $('#loadedittrackFile')[0].files[0];
this._layerName = trackFile.name.replace(/\.[^\.]*$/, '');
if (!this._options.format) this._options.format = trackFile.name.split('.').pop();
var reader = new FileReader();
reader.onload = L.bind(this.processFile, this);
reader.readAsText(trackFile);
},
addTrackOverlay: function(geoJSON) {
this._trackLayer = L.geoJSON(geoJSON, BR.Track.getGeoJsonOptions(layersControl)).addTo(map);
layersControl.addOverlay(this._trackLayer, BR.Util.sanitizeHTMLContent(this._layerName));
this._bounds = this._trackLayer.getBounds();
if (this._bounds) map.fitBounds(this._bounds);
},
getLineStringsFromGeoJSON: function(geoJSON) {
var allLinePoints = [];
var flat = turf.flatten(geoJSON);
turf.featureEach(flat, function(feature, idx) {
if (turf.getType(feature) == 'LineString') {
feature = turf.cleanCoords(feature);
var lPoints = turf.coordAll(feature);
allLinePoints = allLinePoints.concat(lPoints);
}
});
var linesGeoJSON = turf.lineString(allLinePoints);
linesGeoJSON.length = allLinePoints.length;
return linesGeoJSON;
},
guessSimplifyTolerance: function(trackPoints) {
var tolerance = trackPoints.length / 1000000;
if (tolerance > 0.8) tolerance = 0.8;
return tolerance;
},
addRoutingPoints: function(geoJSON) {
if (this._options.simplifyTolerance < 0)
this._options.simplifyTolerance = this.guessSimplifyTolerance(this._trackPoints);
var routingPoints = [];
var simplifiedLatLngs = L.GeoJSON.coordsToLatLngs(
this.getSimplifiedCoords(this._options.simplifyTolerance)
);
for (var i = 0; i < simplifiedLatLngs.length; i++) {
routingPoints.push(simplifiedLatLngs[i]);
}
if (routingPoints.length > 0) {
routing.setWaypoints(routingPoints, function(event) {
if (!event) return;
var err = event.error;
BR.message.showError(
i18next.t('warning.tracks-load-error', {
error: err && err.message ? err.message : err
})
);
});
if (!this._bounds) this._bounds = L.latLngBounds(routingPoints);
}
if (!this._bounds) map.fitBounds(this._bounds);
if (this._options.showPointAsPoi) {
BR.Track.addPoiMarkers(pois, this._currentGeoJSON);
}
},
processFile: function(e) {
var res = e.target.result;
var geoJSON = null;
switch (this._options.format) {
case 'kml':
case 'gpx':
var xml = new DOMParser().parseFromString(res, 'text/xml');
geoJSON = toGeoJSON[this._options.format](xml);
break;
default:
geoJSON = JSON.parse(res);
break;
}
this._currentGeoJSON = geoJSON;
this.setLayerNameFromGeojson(geoJSON);
this._trackPoints = this.getLineStringsFromGeoJSON(geoJSON);
var length = turf.length(this._trackPoints, { units: 'kilometers' });
this._maxTrackPoints = Math.min(length * Math.max(-0.14 * length + 10, 1), 200);
this.fire('file:loaded');
if (this._options.showTrackLayer || this._options.isTestMode) this.addTrackOverlay(geoJSON);
if (!this._options.isTestMode) this.addRoutingPoints();
this.onBusyChanged(false);
}
});
RouteLoader.include(L.Evented.prototype);
var routeLoaderControl = new RouteLoader();
routeLoaderControl.addTo(map);
return routeLoaderControl;
};
BR.routeLoader = function (map, layersControl, routing, pois) {
RouteLoader = L.Control.extend({
_layerName: 'Tracklayer',
_trackLayer: undefined,
_bounds: undefined,
_testLayer: L.layerGroup().addTo(map),
_trackPoints: [],
_maxTrackPoints: 200,
_closeCanceled: true,
_currentGeoJSON: {},
_options: {
format: undefined,
showTrackLayer: true,
showPointAsPoi: true,
simplifyTolerance: -1,
isTestMode: false,
simplifyLastKnownGood: 0.001,
},
setDialogDraggable: function (jqDlgHeader) {
jqDlgHeader.on('mousedown', function (mousedownEvt) {
var $draggable = $(this);
var x = mousedownEvt.pageX - $draggable.offset().left,
y = mousedownEvt.pageY - $draggable.offset().top;
$('body').on('mousemove.draggable', function (mousemoveEvt) {
$draggable.closest('.modal-dialog').offset({
left: mousemoveEvt.pageX - x,
top: mousemoveEvt.pageY - y,
});
});
$('body').one('mouseup', function () {
$('body').off('mousemove.draggable');
});
$draggable.closest('.modal').one('bs.modal.hide', function () {
$('body').off('mousemove.draggable');
});
});
},
getSimplifiedCoords: function (tolerance) {
var simplifiedLine = turf.simplify(this._trackPoints.geometry, {
tolerance: tolerance,
highQuality: true,
});
return simplifiedLine.coordinates;
},
refreshTestLayer: function () {
this.onBusyChanged(true);
this._testLayer.clearLayers();
var simplifiedLatLngs = L.GeoJSON.coordsToLatLngs(
this.getSimplifiedCoords(this._options.simplifyTolerance)
);
if (simplifiedLatLngs.length > this._maxTrackPoints) {
this.onBusyChanged(false);
return false;
}
for (var i = 0; i < simplifiedLatLngs.length; i++) {
this._testLayer.addLayer(
L.circleMarker(simplifiedLatLngs[i], {
radius: 6,
fill: false,
color: '#FF0000',
})
);
}
this.onBusyChanged(false);
return true;
},
cleanup: function (e) {
this._testLayer.clearLayers();
if (
this._trackLayer &&
map.hasLayer(this._trackLayer) &&
(!this._options.showTrackLayer || this._closeCanceled)
) {
map.removeLayer(this._trackLayer);
layersControl.removeLayer(this._trackLayer);
this._trackLayer = undefined;
}
(this._bounds = undefined), (this._trackPoints = []);
this._currentGeoJSON = {};
this._options = {
ext: 'gpx',
showTrackLayer: true,
showPointAsPoi: true,
simplifyTolerance: -1,
isTestMode: false,
simplifyLastKnownGood: 0.001,
};
},
setSliderRange: function () {
$slider = $('#simplify_tolerance');
$slider.prop('min', -500);
var guessedTolerance = this.guessSimplifyTolerance(this._trackPoints);
var guessedLength = this.getSimplifiedCoords(guessedTolerance).length;
if (guessedLength > this._maxTrackPoints) {
this._maxTrackPoints = guessedLength;
$slider.prop('min', 0);
}
$slider.data('lastknowngood', 0);
$slider.data('guess', guessedTolerance.toFixed(20));
$slider.val(0);
this._options.simplifyTolerance = guessedTolerance;
this.refreshTestLayer();
},
onToleranceSlider: function (e) {
var guess = parseFloat($(e.target).data('guess'));
var f = parseFloat(e.target.value);
var frac = parseFloat($(e.target).prop('max'));
if (f > guess)
this._options.simplifyTolerance = guess + Math.pow(f, 2) * (guess / (Math.pow(frac, 2) / 10));
else this._options.simplifyTolerance = Math.abs(guess + Math.pow(f, 3) * (guess / Math.pow(frac, 3)));
if (!this.refreshTestLayer()) {
var iterate = this.findLowestTolerance(
parseInt(e.target.value),
parseInt($(e.target).data('lastknowngood')),
guess,
frac
);
$(e.target).prop('min', iterate);
$(e.target).data('lastknowngood', iterate);
e.target.value = $(e.target).data('lastknowngood');
this._options.simplifyTolerance = this._options.simplifyLastKnownGood = Math.abs(
guess + Math.pow(iterate, 3) * (guess / Math.pow(frac, 3))
);
this.refreshTestLayer();
} else {
this._options.simplifyLastKnownGood = this._options.simplifyTolerance;
$(e.target).data('lastknowngood', e.target.value);
}
},
findLowestTolerance: function (min, max, guess, frac) {
if (Math.abs(max - min) <= 2) return max;
var meridian = Math.round((max + min) / 2);
var tolerance = Math.abs(guess + Math.pow(meridian, 3) * (guess / Math.pow(frac, 3)));
var simplifiedCoords = this.getSimplifiedCoords(tolerance);
if (simplifiedCoords.length > this._maxTrackPoints)
return this.findLowestTolerance(meridian, max, guess, frac);
else return this.findLowestTolerance(min, meridian, guess, frac);
},
onBusyChanged: function (isBusy) {
if (typeof isBusy === undefined) {
isBusy = false;
}
if (isBusy === true) $('#loadedittrackdlg #msg_busy').removeClass('invisible');
else $('#loadedittrackdlg #msg_busy').addClass('invisible');
},
onManualCollapse: function (e) {
//workaround for starting with closed collapse
if ($('#loadedittrackdlg').is(':hidden')) return;
this._options.isTestMode = $(e.target).hasClass('show');
if (this._options.isTestMode) {
this.once('file:loaded', this.setSliderRange, this);
this.convertTrackLocal();
} else this.cleanup();
},
onAdd: function (map) {
$('#loadedittrackdlg').on(
'hidden.bs.modal',
function (e) {
this.cleanup();
}.bind(this)
);
$('#loadedittrackdlg').on(
'show.bs.modal',
function (e) {
$('#manual_collapse').collapse('hide');
this._closeCanceled = true;
}.bind(this)
);
L.DomUtil.get('submitLoadEditTrack').onclick = L.bind(function () {
this._closeCanceled = false;
this.onBusyChanged(true);
if (this._testLayer.getLayers().length > 0) {
this._testLayer.clearLayers();
setTimeout(
function () {
this.addRoutingPoints();
this.onBusyChanged(false);
$('#loadedittrackdlg').modal('hide');
}.bind(this),
0
);
} else
setTimeout(
function () {
this.convertTrackLocal();
$('#loadedittrackdlg').modal('hide');
}.bind(this),
0
);
}, this);
L.DomUtil.get('simplify_tolerance').oninput = L.bind(this.onToleranceSlider, this);
L.DomUtil.get('loadedittrackFile').onchange = L.bind(this.onFileChanged, this);
this.onFileChanged({ target: L.DomUtil.get('loadedittrackFile') });
this.setDialogDraggable($('#loadedittrackdlg .modal-header'));
$('#manual_collapse').collapse('hide');
$('#manual_collapse').on(
'hidden.bs.collapse shown.bs.collapse',
function (e) {
this.onManualCollapse(e);
}.bind(this)
);
// dummy, no own representation, delegating to EasyButton
var dummy = L.DomUtil.create('div');
dummy.hidden = true;
return dummy;
},
onRemove: function (map) {
// Nothing to do here
},
onFileChanged: function (e) {
if (!e.target.files[0]) return;
$(e.target).next('label').text(e.target.files[0].name);
var testmode = this._options.isTestMode;
this.cleanup();
this._options.isTestMode = testmode;
if (this._options.isTestMode) {
this.once('file:loaded', this.setSliderRange, this);
this.convertTrackLocal();
}
},
setLayerNameFromGeojson: function (geoJSON) {
if (geoJSON.type == 'Feature' && geoJSON.properties && geoJSON.properties.name) {
this._layerName = geoJSON.properties.name;
return;
}
if (geoJSON.type == 'FeatureCollection') {
for (var i = 0; i < geoJSON.features.length; i++) {
if (geoJSON.features[i].properties && geoJSON.features[i].properties.name) {
this._layerName = geoJSON.features[i].properties.name;
return;
}
}
}
},
getOptions: function () {
this._options.showTrackLayer = $('#cb_showtracklayer')[0].checked;
this._options.showPointAsPoi = $('#cb_showpois')[0].checked;
this._options.simplifyTolerance = -1;
this._bounds = undefined;
},
convertTrackLocal: function () {
if ($('#loadedittrackFile')[0].files.length == 0) return;
this.onBusyChanged(true);
this.getOptions();
var trackFile = $('#loadedittrackFile')[0].files[0];
this._layerName = trackFile.name.replace(/\.[^\.]*$/, '');
if (!this._options.format) this._options.format = trackFile.name.split('.').pop();
var reader = new FileReader();
reader.onload = L.bind(this.processFile, this);
reader.readAsText(trackFile);
},
addTrackOverlay: function (geoJSON) {
this._trackLayer = L.geoJSON(geoJSON, BR.Track.getGeoJsonOptions(layersControl)).addTo(map);
layersControl.addOverlay(this._trackLayer, BR.Util.sanitizeHTMLContent(this._layerName));
this._bounds = this._trackLayer.getBounds();
if (this._bounds) map.fitBounds(this._bounds);
},
getLineStringsFromGeoJSON: function (geoJSON) {
var allLinePoints = [];
var flat = turf.flatten(geoJSON);
turf.featureEach(flat, function (feature, idx) {
if (turf.getType(feature) == 'LineString') {
feature = turf.cleanCoords(feature);
var lPoints = turf.coordAll(feature);
allLinePoints = allLinePoints.concat(lPoints);
}
});
var linesGeoJSON = turf.lineString(allLinePoints);
linesGeoJSON.length = allLinePoints.length;
return linesGeoJSON;
},
guessSimplifyTolerance: function (trackPoints) {
var tolerance = trackPoints.length / 1000000;
if (tolerance > 0.8) tolerance = 0.8;
return tolerance;
},
addRoutingPoints: function (geoJSON) {
if (this._options.simplifyTolerance < 0)
this._options.simplifyTolerance = this.guessSimplifyTolerance(this._trackPoints);
var routingPoints = [];
var simplifiedLatLngs = L.GeoJSON.coordsToLatLngs(
this.getSimplifiedCoords(this._options.simplifyTolerance)
);
for (var i = 0; i < simplifiedLatLngs.length; i++) {
routingPoints.push(simplifiedLatLngs[i]);
}
if (routingPoints.length > 0) {
routing.setWaypoints(routingPoints, function (event) {
if (!event) return;
var err = event.error;
BR.message.showError(
i18next.t('warning.tracks-load-error', {
error: err && err.message ? err.message : err,
})
);
});
if (!this._bounds) this._bounds = L.latLngBounds(routingPoints);
}
if (!this._bounds) map.fitBounds(this._bounds);
if (this._options.showPointAsPoi) {
BR.Track.addPoiMarkers(pois, this._currentGeoJSON);
}
},
processFile: function (e) {
var res = e.target.result;
var geoJSON = null;
switch (this._options.format) {
case 'kml':
case 'gpx':
var xml = new DOMParser().parseFromString(res, 'text/xml');
geoJSON = toGeoJSON[this._options.format](xml);
break;
default:
geoJSON = JSON.parse(res);
break;
}
this._currentGeoJSON = geoJSON;
this.setLayerNameFromGeojson(geoJSON);
this._trackPoints = this.getLineStringsFromGeoJSON(geoJSON);
var length = turf.length(this._trackPoints, { units: 'kilometers' });
this._maxTrackPoints = Math.min(length * Math.max(-0.14 * length + 10, 1), 200);
this.fire('file:loaded');
if (this._options.showTrackLayer || this._options.isTestMode) this.addTrackOverlay(geoJSON);
if (!this._options.isTestMode) this.addRoutingPoints();
this.onBusyChanged(false);
},
});
RouteLoader.include(L.Evented.prototype);
var routeLoaderControl = new RouteLoader();
routeLoaderControl.addTo(map);
return routeLoaderControl;
};

View file

@ -1,4 +1,4 @@
L.Routing.Draw.prototype._hideTrailer = function() {
L.Routing.Draw.prototype._hideTrailer = function () {
if (this._trailer.options.opacity !== 0.0) {
this._trailer.setStyle({ opacity: 0.0 });
}
@ -12,7 +12,7 @@ BR.Routing = L.Routing.extend({
normal: L.VectorMarkers.icon({ icon: 'circle', markerColor: BR.conf.markerColors.via }),
end: L.VectorMarkers.icon({ icon: 'stop', markerColor: BR.conf.markerColors.stop }),
draw: false,
opacity: 1
opacity: 1,
},
snapping: null,
zIndexOffset: -2000,
@ -20,21 +20,21 @@ BR.Routing = L.Routing.extend({
// width as base number, multiplied by number of digits + one for padding
iconSize: [6, 18],
offset: 5000,
textFunction: function(distance) {
textFunction: function (distance) {
return distance / 1000;
}
},
},
shortcut: {
draw: {
enable: 68, // char code for 'd'
disable: 27 // char code for 'ESC'
disable: 27, // char code for 'ESC'
},
reverse: 82, // char code for 'r'
deleteLastPoint: 90 // char code for 'z'
}
deleteLastPoint: 90, // char code for 'z'
},
},
onAdd: function(map) {
onAdd: function (map) {
this._segmentsCasing = new L.FeatureGroup().addTo(map);
this._loadingTrailerGroup = new L.FeatureGroup().addTo(map);
@ -45,18 +45,18 @@ BR.Routing = L.Routing.extend({
this._waypoints.on('layeradd', this._setMarkerOpacity, this);
this.on('routing:routeWaypointStart routing:rerouteAllSegmentsStart', function(evt) {
this.on('routing:routeWaypointStart routing:rerouteAllSegmentsStart', function (evt) {
this._removeDistanceMarkers();
});
this.on('routing:routeWaypointEnd routing:setWaypointsEnd routing:rerouteAllSegmentsEnd', function(evt) {
this.on('routing:routeWaypointEnd routing:setWaypointsEnd routing:rerouteAllSegmentsEnd', function (evt) {
this._updateDistanceMarkers(evt);
});
// turn line mouse marker off while over waypoint marker
this.on(
'waypoint:mouseover',
function(e) {
function (e) {
// L.Routing.Edit._segmentOnMouseout without firing 'segment:mouseout' (enables draw)
if (this._dragging) {
return;
@ -71,7 +71,7 @@ BR.Routing = L.Routing.extend({
this.on(
'waypoint:mouseout',
function(e) {
function (e) {
this._segmentOnMouseover(e);
this._suspended = false;
},
@ -82,7 +82,7 @@ BR.Routing = L.Routing.extend({
L.divIcon({
className: 'line-mouse-marker',
iconAnchor: [8, 8], // size/2 + border/2
iconSize: [16, 16]
iconSize: [16, 16],
})
);
@ -90,7 +90,7 @@ BR.Routing = L.Routing.extend({
// update indicator), see also L.Routing.Edit._segmentOnMousemove
this._edit._mouseMarker.on(
'move',
L.bind(function(e) {
L.bind(function (e) {
var latLng = e.latlng;
if (latLng._feature) {
this._mouseMarker._feature = latLng._feature;
@ -98,7 +98,7 @@ BR.Routing = L.Routing.extend({
}
}, this._edit)
);
var mouseoutHandler = function(e) {
var mouseoutHandler = function (e) {
if (this._mouseMarker._feature) {
this._mouseMarker._feature.fire('mouseout', e, true);
this._mouseMarker._feature = null;
@ -108,7 +108,7 @@ BR.Routing = L.Routing.extend({
this._edit._mouseMarker.on('dragstart', mouseoutHandler, this._edit);
this.on('waypoint:mouseover', mouseoutHandler, this._edit);
this._draw.on('enabled', function() {
this._draw.on('enabled', function () {
// crosshair cursor
L.DomUtil.addClass(map.getContainer(), 'routing-draw-enabled');
@ -120,7 +120,7 @@ BR.Routing = L.Routing.extend({
this._parent.off('waypoint:mouseout', this._catchWaypointEvent, this);
this.on(
'waypoint:mouseout',
function(e) {
function (e) {
if (!this._parent._edit._suspended) {
this._catchWaypointEvent(e);
}
@ -128,7 +128,7 @@ BR.Routing = L.Routing.extend({
this
);
});
this._draw.on('disabled', function() {
this._draw.on('disabled', function () {
L.DomUtil.removeClass(map.getContainer(), 'routing-draw-enabled');
});
@ -143,13 +143,13 @@ BR.Routing = L.Routing.extend({
this._hide();
}
}
this._draw.on('enabled', function() {
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);
});
this._draw.on('disabled', function() {
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);
@ -163,7 +163,7 @@ BR.Routing = L.Routing.extend({
// although enabled.
this.on(
'waypoint:click',
function() {
function () {
if (this._hidden && !this._parent._waypoints._first) {
this._show();
this._hideTrailer();
@ -181,18 +181,18 @@ BR.Routing = L.Routing.extend({
return container;
},
_addSegmentCasing: function(e) {
_addSegmentCasing: function (e) {
var casing = L.polyline(e.layer.getLatLngs(), this.options.styles.trackCasing);
this._segmentsCasing.addLayer(casing);
e.layer._casing = casing;
this._segments.bringToFront();
},
_removeSegmentCasing: function(e) {
_removeSegmentCasing: function (e) {
this._segmentsCasing.removeLayer(e.layer._casing);
},
setOpacity: function(opacity) {
setOpacity: function (opacity) {
// Due to the second Polyline layer for casing, the combined opacity is less
// transparent than with a single layer and the slider is non-linear. The
// inverted formula is used to get the same result as with a single layer.
@ -205,12 +205,12 @@ BR.Routing = L.Routing.extend({
this.options.icons.opacity = opacity;
this._segments.setStyle({
opacity: sourceOpacity
opacity: sourceOpacity,
});
this._segmentsCasing.setStyle({
opacity: sourceOpacity
opacity: sourceOpacity,
});
this._waypoints.eachLayer(function(marker) {
this._waypoints.eachLayer(function (marker) {
marker.setOpacity(opacity);
});
@ -219,11 +219,11 @@ BR.Routing = L.Routing.extend({
}
},
_setMarkerOpacity: function(e) {
_setMarkerOpacity: function (e) {
e.layer.setOpacity(this.options.icons.opacity);
},
_removeMarkerEvents: function(marker) {
_removeMarkerEvents: function (marker) {
marker.off('mouseover', this._fireWaypointEvent, this);
marker.off('mouseout', this._fireWaypointEvent, this);
marker.off('dragstart', this._fireWaypointEvent, this);
@ -232,7 +232,7 @@ BR.Routing = L.Routing.extend({
marker.off('click', this._fireWaypointEvent, this);
},
clear: function() {
clear: function () {
var drawEnabled = this._draw._enabled;
var current = this._waypoints._first;
@ -259,13 +259,13 @@ BR.Routing = L.Routing.extend({
}
},
setWaypoints: function(latLngs, cb) {
setWaypoints: function (latLngs, cb) {
var i;
var callbackCount = 0;
var firstErr;
var $this = this;
var callback = function(err, data) {
var callback = function (err, data) {
callbackCount++;
firstErr = firstErr || err;
if (callbackCount >= latLngs.length) {
@ -299,10 +299,10 @@ BR.Routing = L.Routing.extend({
// patch to fix error when line is null or error line
// (when called while still segments to calculate, e.g. permalink or fast drawing)
toPolyline: function() {
toPolyline: function () {
var latLngs = [];
this._eachSegment(function(m1, m2, line) {
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) {
@ -313,7 +313,7 @@ BR.Routing = L.Routing.extend({
return L.polyline(latLngs);
},
_routeSegment: function(m1, m2, cb) {
_routeSegment: function (m1, m2, cb) {
var loadingTrailer;
// change segment color before request to indicate recalculation (mark old)
@ -327,7 +327,7 @@ BR.Routing = L.Routing.extend({
color: this.options.styles.track.color,
opacity: this.options.styles.trailer.opacity,
dashArray: [10, 10],
className: 'loading-trailer'
className: 'loading-trailer',
});
this._loadingTrailerGroup.addLayer(loadingTrailer);
}
@ -336,7 +336,7 @@ BR.Routing = L.Routing.extend({
this,
m1,
m2,
L.bind(function(err, data) {
L.bind(function (err, data) {
if (loadingTrailer) {
this._loadingTrailerGroup.removeLayer(loadingTrailer);
}
@ -345,10 +345,10 @@ BR.Routing = L.Routing.extend({
);
},
getSegments: function() {
getSegments: function () {
var segments = [];
this._eachSegment(function(m1, m2, line) {
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) {
@ -359,7 +359,7 @@ BR.Routing = L.Routing.extend({
return segments;
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (!BR.Util.keyboardShortcutsAllowed(e)) {
return;
}
@ -374,7 +374,7 @@ BR.Routing = L.Routing.extend({
}
},
_keyupListener: function(e) {
_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) {
@ -382,30 +382,30 @@ BR.Routing = L.Routing.extend({
}
},
isDrawing: function() {
isDrawing: function () {
return this._draw._enabled;
},
reverse: function() {
reverse: function () {
var waypoints = this.getWaypoints();
waypoints.reverse();
this.clear();
this.setWaypoints(waypoints);
},
deleteLastPoint: function() {
deleteLastPoint: function () {
if ((lastPoint = this.getLast())) {
this.removeWaypoint(lastPoint, function(err, data) {});
this.removeWaypoint(lastPoint, function (err, data) {});
}
},
_removeDistanceMarkers: function() {
_removeDistanceMarkers: function () {
if (this._map && this._distanceMarkers) {
this._map.removeLayer(this._distanceMarkers);
}
},
_updateDistanceMarkers: function(e) {
_updateDistanceMarkers: function (e) {
this._removeDistanceMarkers();
if (this._map) {
@ -413,5 +413,5 @@ BR.Routing = L.Routing.extend({
this._distanceMarkers = new L.DistanceMarkers(this.toPolyline(), this._map, distanceMarkersOpts);
this._map.addLayer(this._distanceMarkers);
}
}
},
});

View file

@ -2,11 +2,11 @@ BR.RoutingPathQuality = L.Control.extend({
options: {
shortcut: {
toggle: 67, // char code for 'c'
muteKeyCode: 77 // char code for 'm'
}
muteKeyCode: 77, // char code for 'm'
},
},
initialize: function(map, layersControl, options) {
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
@ -32,20 +32,20 @@ BR.RoutingPathQuality = L.Control.extend({
0.25: '#00ffff', // cyan
0.5: '#00ff00', // green
0.75: '#ffff00', // yellow
1.0: '#ff0000' // red
1.0: '#ff0000', // red
},
outlineColor: 'dimgray',
renderer: renderer
renderer: renderer,
},
valueFunction: function(latLng, prevLatLng) {
valueFunction: function (latLng, prevLatLng) {
var 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-shortcut', { action: '$t(map.route-quality-altitude)', key: 'C' }),
@ -53,12 +53,12 @@ BR.RoutingPathQuality = L.Control.extend({
provider: new HotLineQualityProvider({
hotlineOptions: {
outlineColor: 'dimgray',
renderer: renderer
renderer: renderer,
},
valueFunction: function(latLng) {
valueFunction: function (latLng) {
return latLng.alt;
}
})
},
}),
},
cost: {
title: i18next.t('map.route-quality-shortcut', { action: '$t(map.route-quality-cost)', key: 'C' }),
@ -66,9 +66,9 @@ BR.RoutingPathQuality = L.Control.extend({
provider: new HotLineQualityProvider({
hotlineOptions: {
outlineColor: 'dimgray',
renderer: renderer
renderer: renderer,
},
valueFunction: function(latLng) {
valueFunction: function (latLng) {
var feature = latLng.feature;
var cost = feature.cost.perKm;
var distance = feature.distance / 1000; // in km
@ -78,9 +78,9 @@ BR.RoutingPathQuality = L.Control.extend({
distance;
}
return cost;
}
})
}
},
}),
},
};
this._initialProvider = this.options.initialProvider || 'incline';
this.selectedProvider = this._initialProvider;
@ -89,12 +89,12 @@ BR.RoutingPathQuality = L.Control.extend({
this._muted = false;
},
onAdd: function(map) {
onAdd: function (map) {
this._map = map;
map.on(
'overlayadd',
function(evt) {
function (evt) {
if (evt.layer === this._routingSegments) {
this._activate(this.routingPathButton);
}
@ -103,7 +103,7 @@ BR.RoutingPathQuality = L.Control.extend({
);
map.on(
'overlayremove',
function(evt) {
function (evt) {
if (evt.layer === this._routingSegments) {
this._deactivate(this.routingPathButton);
}
@ -123,8 +123,8 @@ BR.RoutingPathQuality = L.Control.extend({
stateName: keys[i],
icon: provider.icon,
title: provider.title,
onClick: L.bind(function(state) {
return L.bind(function(btn) {
onClick: L.bind(function (state) {
return L.bind(function (btn) {
if (this._active) {
btn.state(state);
this.setProvider(state);
@ -138,7 +138,7 @@ BR.RoutingPathQuality = L.Control.extend({
this._activate(btn);
}
}, this);
}, this)(nextState)
}, this)(nextState),
});
}
@ -148,42 +148,42 @@ BR.RoutingPathQuality = L.Control.extend({
}
this.routingPathButton = new L.easyButton({
states: states
states: states,
}).addTo(map);
return new L.DomUtil.create('div');
},
_activate: function(btn) {
_activate: function (btn) {
this._active = true;
this._getIcon(btn).classList.add('active');
this._routingSegments.addTo(this._map);
},
_deactivate: function(btn) {
_deactivate: function (btn) {
this._active = false;
this._getIcon(btn).classList.remove('active');
this._map.removeLayer(this._routingSegments);
},
_getIcon: function(btn) {
_getIcon: function (btn) {
return btn.button.firstChild.firstChild;
},
update: function(track, layer) {
update: function (track, layer) {
var segments = [];
layer.eachLayer(function(layer) {
layer.eachLayer(function (layer) {
segments.push(layer);
});
this.segments = segments;
this._update(this.segments);
},
setProvider: function(provider) {
setProvider: function (provider) {
this.selectedProvider = provider;
this._update(this.segments);
},
_update: function(segments) {
_update: function (segments) {
this._routingSegments.clearLayers();
var layers = this.providers[this.selectedProvider].provider.computeLayers(segments);
if (layers) {
@ -193,7 +193,7 @@ BR.RoutingPathQuality = L.Control.extend({
}
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (!BR.Util.keyboardShortcutsAllowed(e)) {
return;
}
@ -206,21 +206,21 @@ BR.RoutingPathQuality = L.Control.extend({
}
},
_keyupListener: function(e) {
_keyupListener: function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && this._muted && e.keyCode === this.options.shortcut.muteKeyCode) {
this._muted = false;
this._activate(this.routingPathButton);
}
}
},
});
var HotLineQualityProvider = L.Class.extend({
initialize: function(options) {
initialize: function (options) {
this.hotlineOptions = options.hotlineOptions;
this.valueFunction = options.valueFunction;
},
computeLayers: function(segments) {
computeLayers: function (segments) {
var layers = [];
if (segments) {
var segmentLatLngs = [];
@ -250,7 +250,7 @@ var HotLineQualityProvider = L.Class.extend({
return layers;
},
_computeLatLngVals: function(segment) {
_computeLatLngVals: function (segment) {
var latLngVals = [],
segmentLatLngs = segment.getLatLngs(),
segmentLength = segmentLatLngs.length;
@ -268,11 +268,11 @@ var HotLineQualityProvider = L.Class.extend({
return latLngVals;
},
_convertToArray: function(latLng, val) {
_convertToArray: function (latLng, val) {
return [latLng.lat, latLng.lng, val];
},
_calcMinMaxValues: function(lines) {
_calcMinMaxValues: function (lines) {
var min = lines[0][2],
max = min;
for (var i = 1; lines && i < lines.length; i++) {
@ -285,7 +285,7 @@ var HotLineQualityProvider = L.Class.extend({
}
return {
min: min,
max: max
max: max,
};
}
},
});

View file

@ -2,30 +2,30 @@ BR.Search = L.Control.Geocoder.extend({
options: {
geocoder: new L.Control.Geocoder.LatLng({
next: new L.Control.Geocoder.Nominatim({
serviceUrl: 'https://nominatim.openstreetmap.org/'
serviceUrl: 'https://nominatim.openstreetmap.org/',
}),
sizeInMeters: 800
sizeInMeters: 800,
}),
position: 'topleft',
shortcut: {
search: 70 // char code for 'f'
}
search: 70, // char code for 'f'
},
},
initialize: function(options) {
initialize: function (options) {
L.Control.Geocoder.prototype.initialize.call(this, options);
L.setOptions(this, {
// i18next.t will only return 'undefined' if it is called in a static context
// (e.g. when added directly to "options:" above), so we have to call it here
placeholder: i18next.t('map.geocoder-placeholder')
placeholder: i18next.t('map.geocoder-placeholder'),
});
L.DomEvent.addListener(document, 'keydown', this._keydownListener, this);
},
markGeocode: function(result) {
markGeocode: function (result) {
this._map.fitBounds(result.geocode.bbox, {
maxZoom: 17
maxZoom: 17,
});
this.clear();
@ -33,22 +33,22 @@ BR.Search = L.Control.Geocoder.extend({
interactive: false,
color: 'red',
opacity: 1,
weight: 3
weight: 3,
}).addTo(this._map);
return this;
},
clear: function() {
clear: function () {
if (this._geocodeMarker) {
this._map.removeLayer(this._geocodeMarker);
}
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.search) {
$('#map .leaflet-control-geocoder')[0].dispatchEvent(new MouseEvent('mousedown'));
e.preventDefault();
}
}
},
});

View file

@ -9,15 +9,15 @@ BR.Sidebar = L.Control.Sidebar.extend({
defaultTabId: '',
shortcut: {
toggleTabs: 84 // char code for 't'
toggleTabs: 84, // char code for 't'
},
// Tabs to be notified when shown or hidden
// (tab div id -> object implementing show/hide methods)
listeningTabs: {}
listeningTabs: {},
},
initialize: function(id, options) {
initialize: function (id, options) {
L.Control.Sidebar.prototype.initialize.call(this, id, options);
this.oldTab = null;
@ -25,7 +25,7 @@ BR.Sidebar = L.Control.Sidebar.extend({
L.DomEvent.addListener(document, 'keydown', this._keydownListener, this);
},
addTo: function(map) {
addTo: function (map) {
L.Control.Sidebar.prototype.addTo.call(this, map);
this.on('content', this._notifyOnContent, this);
@ -34,7 +34,7 @@ BR.Sidebar = L.Control.Sidebar.extend({
this.on(
'closing',
function() {
function () {
this._map.getContainer().focus();
},
this
@ -43,7 +43,7 @@ BR.Sidebar = L.Control.Sidebar.extend({
this.recentTab = this.options.defaultTabId;
this.on(
'content',
function(tab) {
function (tab) {
this.recentTab = tab.id;
},
this
@ -59,14 +59,14 @@ BR.Sidebar = L.Control.Sidebar.extend({
return this;
},
showPanel: function(id) {
showPanel: function (id) {
var tab = this._getTab(id);
tab.hidden = false;
return this;
},
_rememberTabState: function() {
_rememberTabState: function () {
if (BR.Util.localStorageAvailable()) {
this.on('content closing', this._storeActiveTab, this);
@ -85,42 +85,42 @@ BR.Sidebar = L.Control.Sidebar.extend({
}
},
_notifyShow: function(tab) {
_notifyShow: function (tab) {
if (tab && tab.show) {
tab.show();
}
},
_notifyHide: function(tab) {
_notifyHide: function (tab) {
if (tab && tab.hide) {
tab.hide();
}
},
_notifyOnContent: function(e) {
_notifyOnContent: function (e) {
var tab = this.options.listeningTabs[e.id];
this._notifyHide(this.oldTab);
this._notifyShow(tab);
this.oldTab = tab;
},
_notifyOnClose: function(e) {
_notifyOnClose: function (e) {
this._notifyHide(this.oldTab);
this.oldTab = null;
},
_notifyOnResize: function(e) {
_notifyOnResize: function (e) {
var tab = this.oldTab;
if (tab && tab.onResize) {
tab.onResize();
}
},
_storeActiveTab: function(e) {
_storeActiveTab: function (e) {
localStorage.setItem(this.storageId, e.id || '');
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggleTabs) {
if ($('#sidebarTabs > ul > li[class=active]').length) {
// sidebar is currently open, close current tab
@ -142,9 +142,9 @@ BR.Sidebar = L.Control.Sidebar.extend({
this.open(nextTab.attr('href').slice(1));
}
}
}
},
});
BR.sidebar = function(divId, options) {
BR.sidebar = function (divId, options) {
return new BR.Sidebar(divId, options);
};

View file

@ -1,6 +1,6 @@
BR.tracksLoader = function(map, layersControl, routing, pois) {
BR.tracksLoader = function (map, layersControl, routing, pois) {
// proxy to L.geoJSON factory function, to get hold of raw GeoJSON object
var createGeoJsonLayer = function(geojson, options) {
var createGeoJsonLayer = function (geojson, options) {
BR.Track.addPoiMarkers(pois, geojson);
return L.geoJSON(geojson, options);
@ -15,11 +15,11 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
// File size limit in kb (default: 1024) ?
fileSizeLimit: 1024,
shortcut: {
open: 79 // char code for 'o'
}
open: 79, // char code for 'o'
},
},
_initContainer: function() {
_initContainer: function () {
var thisLoader = this.loader;
var fileInput;
@ -40,7 +40,7 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
// Load on file change
fileInput.addEventListener(
'change',
function() {
function () {
thisLoader.loadMultiple(this.files);
// reset so that the user can upload the same file again if they want to
this.value = '';
@ -50,7 +50,7 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
var link = L.DomUtil.get('navbarLoadTracks');
L.DomEvent.disableClickPropagation(link);
L.DomEvent.on(link, 'click', function(e) {
L.DomEvent.on(link, 'click', function (e) {
fileInput.click();
e.preventDefault();
});
@ -60,7 +60,7 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
return dummy;
},
_keydownListener: function(e) {
_keydownListener: function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.open) {
if (e.shiftKey) {
$('#loadNogos').modal('show');
@ -68,12 +68,12 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
$('#navbarLoadTracks')[0].click();
}
}
}
},
});
var tracksLoaderControl = new TracksLoader();
tracksLoaderControl.addTo(map);
tracksLoaderControl.loader.on('data:loaded', function(event) {
tracksLoaderControl.loader.on('data:loaded', function (event) {
var eventLayer = event.layer,
routingMarkers = [];
/* disabled for now, see issue #254
@ -101,11 +101,11 @@ BR.tracksLoader = function(map, layersControl, routing, pois) {
eventLayer.addTo(map);
});
tracksLoaderControl.loader.on('data:error', function(event) {
tracksLoaderControl.loader.on('data:error', function (event) {
var err = event.error;
BR.message.showError(
i18next.t('warning.tracks-load-error', {
error: err && err.message ? err.message : err
error: err && err.message ? err.message : err,
})
);
console.error(err);

View file

@ -24,7 +24,7 @@
*/
L.DistanceMarkers = L.LayerGroup.extend({
initialize: function(line, map, options) {
initialize: function (line, map, options) {
options = options || {};
var offset = options.offset || 1000;
var showAll = Math.min(map.getMaxZoom(), options.showAll || 12);
@ -32,7 +32,7 @@ L.DistanceMarkers = L.LayerGroup.extend({
var iconSize = options.iconSize !== undefined ? options.iconSize : [12, 12];
var textFunction =
options.textFunction ||
function(distance, i) {
function (distance, i) {
return i;
};
@ -81,7 +81,7 @@ L.DistanceMarkers = L.LayerGroup.extend({
var currentZoomLevel = 0;
var markerLayer = this;
var updateMarkerVisibility = function() {
var updateMarkerVisibility = function () {
var oldZoom = currentZoomLevel;
var newZoom = (currentZoomLevel = map.getZoom());
@ -106,20 +106,20 @@ L.DistanceMarkers = L.LayerGroup.extend({
updateMarkerVisibility();
},
setOpacity: function(opacity) {
setOpacity: function (opacity) {
var i,
keys = Object.keys(this._zoomLayers),
l = keys.length;
for (i = 0; i < l; ++i) {
var zoomLayer = this._zoomLayers[keys[i]];
zoomLayer.eachLayer(function(layer) {
zoomLayer.eachLayer(function (layer) {
layer.setOpacity(opacity);
});
}
},
_minimumZoomLevelForItem: function(item, showAllLevel) {
_minimumZoomLevelForItem: function (item, showAllLevel) {
var zoom = showAllLevel,
i = item;
while (i > 0 && i % 2 === 0) {
@ -127,5 +127,5 @@ L.DistanceMarkers = L.LayerGroup.extend({
i = Math.floor(i / 2);
}
return zoom;
}
},
});

View file

@ -1,10 +1,10 @@
(function(window) {
var HAS_HASHCHANGE = (function() {
(function (window) {
var HAS_HASHCHANGE = (function () {
var doc_mode = window.documentMode;
return 'onhashchange' in window && (doc_mode === undefined || doc_mode > 7);
})();
L.Hash = function(map, options) {
L.Hash = function (map, options) {
this.onHashChange = L.Util.bind(this.onHashChange, this);
if (map) {
@ -12,7 +12,7 @@
}
};
L.Hash.parseHash = function(hash) {
L.Hash.parseHash = function (hash) {
if (hash.indexOf('#map=') === 0) {
hash = hash.substr(5);
}
@ -31,7 +31,7 @@
center: new L.LatLng(lat, lon),
zoom: zoom,
layers: layers,
additional: additional
additional: additional,
};
}
} else {
@ -39,7 +39,7 @@
}
};
(L.Hash.formatHash = function(map) {
(L.Hash.formatHash = function (map) {
var center = map.getCenter(),
zoom = map.getZoom(),
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
@ -57,7 +57,7 @@
}),
(L.Hash.prototype = {
options: {
layerSeparator: ','
layerSeparator: ',',
},
map: null,
lastHash: null,
@ -65,7 +65,7 @@
parseHash: L.Hash.parseHash,
formatHash: L.Hash.formatHash,
init: function(map, options) {
init: function (map, options) {
this.map = map;
L.Util.setOptions(this, options);
@ -78,9 +78,9 @@
}
},
_parseLayers: function(layersParam, layerSeparator) {
_parseLayers: function (layersParam, layerSeparator) {
var layers = layersParam.split(layerSeparator).map(
L.bind(function(layerEncoded) {
L.bind(function (layerEncoded) {
var obj = null;
var layerString = decodeURIComponent(layerEncoded);
@ -95,8 +95,8 @@
return layers;
},
parseLayers: function(layersParam) {
var countFoundLayers = function(count, obj) {
parseLayers: function (layersParam) {
var countFoundLayers = function (count, obj) {
if (obj) {
count++;
}
@ -119,14 +119,14 @@
return layers;
},
activateLayers: function(layers) {
activateLayers: function (layers) {
var layersControl = this.options.layersControl;
var added = false;
layersControl.removeActiveLayers();
layers.forEach(
L.bind(function(obj, index, array) {
L.bind(function (obj, index, array) {
if (obj) {
layersControl.activateLayer(obj);
if (obj && !obj.overlay) {
@ -142,14 +142,14 @@
}
},
formatLayers: function() {
formatLayers: function () {
var objList = this.options.layersControl.getActiveLayers();
// exclude vector layers (loaded tracks), but not when id set (route quality coding)
objList = objList.filter(function(obj) {
objList = objList.filter(function (obj) {
return obj.layer instanceof L.GridLayer || obj.layer.id;
});
var layerList = objList.map(
L.bind(function(obj) {
L.bind(function (obj) {
return encodeURIComponent(this.options.layersControl.toLayerString(obj));
}, this)
);
@ -157,7 +157,7 @@
return layerList.join(this.options.layerSeparator);
},
removeFrom: function(map) {
removeFrom: function (map) {
if (this.changeTimeout) {
clearTimeout(this.changeTimeout);
}
@ -169,7 +169,7 @@
this.map = null;
},
onMapMove: function() {
onMapMove: function () {
// bail if we're moving the map (updating from a hash),
// or if the map is not yet loaded
@ -185,7 +185,7 @@
},
movingMap: false,
update: function() {
update: function () {
var hash = location.hash;
if (hash === this.lastHash) {
return;
@ -221,12 +221,12 @@
// defer hash change updates every 100ms
changeDefer: 100,
changeTimeout: null,
onHashChange: function() {
onHashChange: function () {
// throttle calls to update() so that they only happen every
// `changeDefer` ms
if (!this.changeTimeout) {
var that = this;
this.changeTimeout = setTimeout(function() {
this.changeTimeout = setTimeout(function () {
that.update();
that.changeTimeout = null;
}, this.changeDefer);
@ -235,7 +235,7 @@
isListening: false,
hashChangeInterval: null,
startListening: function() {
startListening: function () {
this.map.on('moveend layeradd layerremove', this.onMapMove, this);
if (HAS_HASHCHANGE) {
@ -247,7 +247,7 @@
this.isListening = true;
},
stopListening: function() {
stopListening: function () {
this.map.off('moveend layeradd layerremove', this.onMapMove, this);
if (HAS_HASHCHANGE) {
@ -258,7 +258,7 @@
this.isListening = false;
},
_keyByValue: function(obj, value) {
_keyByValue: function (obj, value) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] === value) {
@ -268,15 +268,15 @@
}
}
}
}
},
});
L.hash = function(map, options) {
L.hash = function (map, options) {
return new L.Hash(map, options);
};
L.Map.prototype.addHash = function() {
L.Map.prototype.addHash = function () {
this._hash = L.hash(this, this.options);
};
L.Map.prototype.removeHash = function() {
L.Map.prototype.removeHash = function () {
this._hash.removeFrom();
};
})(window);

View file

@ -1,29 +1,29 @@
BR.stravaSegments = function(map, layersControl) {
BR.stravaSegments = function (map, layersControl) {
var stravaControl = L.control
.stravaSegments({
runningTitle: i18next.t('map.strava-shortcut', { action: '$t(map.strava-running)', key: 'S' }),
bikingTitle: i18next.t('map.strava-shortcut', { action: '$t(map.strava-biking)', key: 'S' }),
loadingTitle: i18next.t('map.loading'),
stravaToken: BR.keys.strava
stravaToken: BR.keys.strava,
})
.addTo(map);
layersControl.addOverlay(stravaControl.stravaLayer, i18next.t('map.layer.strava-segments'));
stravaControl.onError = function(err) {
stravaControl.onError = function (err) {
BR.message.showError(
i18next.t('warning.strava-error', {
error: err && err.message ? err.message : err
error: err && err.message ? err.message : err,
})
);
};
L.setOptions(this, {
shortcut: {
toggleLayer: 83 // char code for 's'
}
toggleLayer: 83, // char code for 's'
},
});
// hide strava buttons when layer is inactive
var toggleStravaControl = function() {
var toggleStravaControl = function () {
var stravaBar = stravaControl.runningButton.button.parentElement;
stravaBar.hidden = !stravaBar.hidden;
};
@ -33,7 +33,7 @@ BR.stravaSegments = function(map, layersControl) {
L.DomEvent.addListener(
document,
'keydown',
function(e) {
function (e) {
if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggleLayer) {
if (map.hasLayer(stravaControl.stravaLayer)) {
map.removeLayer(stravaControl.stravaLayer);