initial commit
This commit is contained in:
parent
37980ff82b
commit
4cc16bccd0
17 changed files with 789 additions and 0 deletions
37
js/control/Control.js
Normal file
37
js/control/Control.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
BR.Control = L.Control.extend({
|
||||
options: {
|
||||
position: 'leftpane'
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = L.DomUtil.create('div', 'info'),
|
||||
heading,
|
||||
div;
|
||||
|
||||
if (this.options.heading) {
|
||||
heading = L.DomUtil.create('div', 'heading', container);
|
||||
heading.innerHTML = this.options.heading;
|
||||
this._content = L.DomUtil.create('div', 'content', container);
|
||||
} else {
|
||||
this._content = container;
|
||||
}
|
||||
|
||||
if (this.options.divId) {
|
||||
div = L.DomUtil.get(this.options.divId);
|
||||
L.DomUtil.removeClass(div, 'hidden');
|
||||
this._content.appendChild(div);
|
||||
}
|
||||
|
||||
var stop = L.DomEvent.stopPropagation;
|
||||
L.DomEvent
|
||||
.on(container, 'click', stop)
|
||||
.on(container, 'mousedown', stop)
|
||||
.on(container, 'dblclick', stop);
|
||||
// disabled because links not working, remove?
|
||||
//L.DomEvent.on(container, 'click', L.DomEvent.preventDefault);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
20
js/control/Download.js
Normal file
20
js/control/Download.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
BR.Download = BR.Control.extend({
|
||||
options: {
|
||||
heading: 'Download'
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = BR.Control.prototype.onAdd.call(this, map);
|
||||
return container;
|
||||
},
|
||||
|
||||
update: function (urls) {
|
||||
var html = '<div class="label"> </div><div class="value">';
|
||||
if (urls.gpx) {
|
||||
html += '<a href="' + urls.gpx + '" download="brouter.gpx" target="_blank">GPX</a> · ';
|
||||
html += '<a href="' + urls.kml + '" download="brouter.kml" target="_blank">KML</a>';
|
||||
}
|
||||
html += '</div>'
|
||||
this._content.innerHTML = html;
|
||||
}
|
||||
});
|
||||
11
js/control/Profile.js
Normal file
11
js/control/Profile.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
BR.Profile = BR.Control.extend({
|
||||
options: {
|
||||
heading: ''
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = BR.Control.prototype.onAdd.call(this, map);
|
||||
container.innerHTML = " ";
|
||||
return container;
|
||||
}
|
||||
});
|
||||
28
js/control/RoutingOptions.js
Normal file
28
js/control/RoutingOptions.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
BR.RoutingOptions = BR.Control.extend({
|
||||
options: {
|
||||
heading: 'Options',
|
||||
divId: 'route_options'
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
L.DomUtil.get('profile').onchange = this._getChangeHandler();
|
||||
L.DomUtil.get('alternative').onchange = this._getChangeHandler();
|
||||
|
||||
return BR.Control.prototype.onAdd.call(this, map);
|
||||
},
|
||||
|
||||
getOptions: function() {
|
||||
return {
|
||||
profile: L.DomUtil.get('profile').value,
|
||||
alternative: L.DomUtil.get('alternative').value
|
||||
};
|
||||
},
|
||||
|
||||
_getChangeHandler: function() {
|
||||
return L.bind(function(evt) {
|
||||
this.fire('update', {options: this.getOptions()});
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
BR.RoutingOptions.include(L.Mixin.Events);
|
||||
49
js/control/TrackStats.js
Normal file
49
js/control/TrackStats.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
BR.TrackStats = BR.Control.extend({
|
||||
options: {
|
||||
heading: 'Route'
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = BR.Control.prototype.onAdd.call(this, map);
|
||||
this.update();
|
||||
return container;
|
||||
},
|
||||
|
||||
update: function (polyline) {
|
||||
var stats = this.calcStats(polyline),
|
||||
html = '';
|
||||
|
||||
html += '<table id="stats">';
|
||||
html += '<tr><td>Length: </td><td>' + L.Util.formatNum(stats.distance/1000,1) + '</td><td>km</td></tr>';
|
||||
html += '<tr><td>Ascent: </td><td>' + Math.round(stats.elevationGain) + '</td><td>m</td></tr>';
|
||||
html += '<tr><td>Descent: </td><td>' + Math.round(stats.elevationLoss) + '</td><td>m</td></tr>';
|
||||
html += '</table>';
|
||||
|
||||
this._content.innerHTML = html;
|
||||
},
|
||||
|
||||
calcStats: function(polyline) {
|
||||
var stats = {
|
||||
distance: 0,
|
||||
elevationGain: 0,
|
||||
elevationLoss: 0
|
||||
};
|
||||
|
||||
var latLngs = polyline ? polyline.getLatLngs() : [];
|
||||
for (var i = 0, current, next, eleDiff; i < latLngs.length - 1; i++) {
|
||||
current = latLngs[i];
|
||||
next = latLngs[i + 1];
|
||||
stats.distance += current.distanceTo(next);
|
||||
|
||||
// from Leaflet.gpx plugin (writes to LatLng.meta.ele, LatLng now supports ele)
|
||||
eleDiff = (next.ele || next.meta.ele) - (current.ele || current.meta.ele);
|
||||
if (eleDiff > 0) {
|
||||
stats.elevationGain += eleDiff;
|
||||
} else {
|
||||
stats.elevationLoss += Math.abs(eleDiff);
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
});
|
||||
124
js/index.js
Normal file
124
js/index.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
BRouter web - web client for BRouter bike routing engine
|
||||
|
||||
Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
function initMap() {
|
||||
var odblAttribution = 'data © <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors '
|
||||
+ '(<a target="_blank" href="http://opendatacommons.org/licenses/odbl/">ODbL</a>)';
|
||||
|
||||
var landscape = L.tileLayer('http://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png', {
|
||||
maxZoom: 18,
|
||||
attribution: 'tiles © <a target="_blank" href="http://www.thunderforest.com">Thunderforest</a> '
|
||||
+ '(<a target="_blank" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>)' + odblAttribution
|
||||
});
|
||||
|
||||
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: 'tiles © <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
});
|
||||
|
||||
var map = new L.Map('map', {
|
||||
layers: [osm],
|
||||
center: new L.LatLng(50.99, 9.86),
|
||||
zoom: 6
|
||||
});
|
||||
map.attributionControl.addAttribution(
|
||||
'<a href="http://dr-brenschede.de/brouter/" target="_blank">BRouter</a> © Arndt Brenschede, '
|
||||
+ 'routing + map data © <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors '
|
||||
+ '(<a target="_blank" href="http://opendatacommons.org/licenses/odbl/">ODbL</a>)');
|
||||
|
||||
L.control.layers({
|
||||
'OpenStreetMap': osm,
|
||||
'Landscape (Thunderforest)': landscape
|
||||
}, {
|
||||
/*
|
||||
'Hiking (Waymarked Trails)': hiking
|
||||
*/
|
||||
}).addTo(map);
|
||||
|
||||
map.addControl(new L.Control.Permalink({text: 'Permalink', position: 'bottomright'})); //, layers: layersControl
|
||||
map.addControl(new BR.Search());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function initApp(map) {
|
||||
var router,
|
||||
routing,
|
||||
routesLayer,
|
||||
routingOptions,
|
||||
nogos,
|
||||
stats,
|
||||
elevation,
|
||||
download,
|
||||
profile,
|
||||
leftPaneId = 'leftpane';
|
||||
|
||||
// left sidebar as additional control position
|
||||
map._controlCorners[leftPaneId] = L.DomUtil.create('div', 'leaflet-' + leftPaneId, map._controlContainer);
|
||||
|
||||
router = L.bRouter(); //brouterCgi dummyRouter
|
||||
|
||||
function updateRoute(evt) {
|
||||
router.setOptions(evt.options);
|
||||
routing.routeAllSegments(onUpdate);
|
||||
}
|
||||
|
||||
routingOptions = new BR.RoutingOptions();
|
||||
routingOptions.on('update', updateRoute);
|
||||
|
||||
nogos = new BR.NogoAreas();
|
||||
nogos.on('update', updateRoute);
|
||||
|
||||
// initial option settings
|
||||
router.setOptions(nogos.getOptions());
|
||||
router.setOptions(routingOptions.getOptions());
|
||||
|
||||
stats = new BR.TrackStats();
|
||||
download = new BR.Download();
|
||||
elevation = new BR.Elevation();
|
||||
profile = new BR.Profile();
|
||||
|
||||
routing = new BR.Routing({routing: {
|
||||
router: L.bind(router.getRouteSegment, router)
|
||||
}});
|
||||
routing.on('routing:routeWaypointEnd', onUpdate);
|
||||
|
||||
function onUpdate() {
|
||||
var track = routing.toPolyline(),
|
||||
latLngs = routing.getWaypoints(),
|
||||
urls = {};
|
||||
|
||||
elevation.update(track);
|
||||
stats.update(track);
|
||||
|
||||
if (latLngs.length > 1) {
|
||||
urls.gpx = router.getUrl(latLngs, 'gpx');
|
||||
urls.kml = router.getUrl(latLngs, 'kml');
|
||||
}
|
||||
|
||||
download.update(urls);
|
||||
};
|
||||
|
||||
map.addControl(new BR.Control({
|
||||
heading: '',
|
||||
divId: 'header'
|
||||
}));
|
||||
routingOptions.addTo(map);
|
||||
stats.addTo(map);
|
||||
download.addTo(map);
|
||||
elevation.addTo(map);
|
||||
profile.addTo(map);
|
||||
|
||||
nogos.addTo(map);
|
||||
routing.addTo(map);
|
||||
}
|
||||
|
||||
map = initMap();
|
||||
initApp(map);
|
||||
|
||||
})();
|
||||
35
js/plugin/Elevation.js
Normal file
35
js/plugin/Elevation.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
BR.Elevation = L.Control.Elevation.extend({
|
||||
options: {
|
||||
position: "leftpane",
|
||||
width: 385,
|
||||
margins: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 30,
|
||||
left: 50
|
||||
},
|
||||
theme: "steelblue-theme" //purple
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._data = [];
|
||||
this._dist = 0;
|
||||
this._maxElevation = 0;
|
||||
|
||||
// workaround for 'Error: Problem parsing d=""' in Webkit when empty data
|
||||
// https://groups.google.com/d/msg/d3-js/7rFxpXKXFhI/HzIO_NPeDuMJ
|
||||
//this._areapath.datum(this._data).attr("d", this._area);
|
||||
this._areapath.attr("d", "M0 0");
|
||||
|
||||
this._x.domain([0,1]);
|
||||
this._y.domain([0,1]);
|
||||
this._updateAxis();
|
||||
},
|
||||
|
||||
update: function(track) {
|
||||
this.clear();
|
||||
if (track && track.getLatLngs().length > 0) {
|
||||
this.addData(track);
|
||||
}
|
||||
}
|
||||
});
|
||||
66
js/plugin/NogoAreas.js
Normal file
66
js/plugin/NogoAreas.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
L.drawLocal.draw.toolbar.buttons.circle = 'Draw no-go area (circle)';
|
||||
L.drawLocal.edit.toolbar.buttons.edit = 'Edit no-go areas';
|
||||
L.drawLocal.edit.toolbar.buttons.remove = 'Delete no-go areas';
|
||||
|
||||
BR.NogoAreas = L.Control.Draw.extend({
|
||||
initialize: function () {
|
||||
this.drawnItems = new L.FeatureGroup();
|
||||
|
||||
L.Control.Draw.prototype.initialize.call(this, {
|
||||
draw: {
|
||||
position: 'topleft',
|
||||
polyline: false,
|
||||
polygon: false,
|
||||
circle: true,
|
||||
rectangle: false,
|
||||
marker: false
|
||||
},
|
||||
edit: {
|
||||
featureGroup: this.drawnItems,
|
||||
//edit: false,
|
||||
edit: {
|
||||
selectedPathOptions: {
|
||||
//opacity: 0.8
|
||||
}
|
||||
},
|
||||
remove: true
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
map.addLayer(this.drawnItems);
|
||||
|
||||
map.on('draw:created', function (e) {
|
||||
var layer = e.layer;
|
||||
this.drawnItems.addLayer(layer);
|
||||
this._fireUpdate();
|
||||
}, this);
|
||||
|
||||
map.on('draw:editstart', function (e) {
|
||||
this.drawnItems.eachLayer(function (layer) {
|
||||
layer.on('edit', function(e) {
|
||||
this._fireUpdate();
|
||||
}, this);
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
map.on('draw:deleted', function (e) {
|
||||
this._fireUpdate();
|
||||
}, this);
|
||||
|
||||
return L.Control.Draw.prototype.onAdd.call(this, map);
|
||||
},
|
||||
|
||||
getOptions: function() {
|
||||
return {
|
||||
nogos: this.drawnItems.getLayers()
|
||||
};
|
||||
},
|
||||
|
||||
_fireUpdate: function () {
|
||||
this.fire('update', {options: this.getOptions()});
|
||||
}
|
||||
});
|
||||
|
||||
BR.NogoAreas.include(L.Mixin.Events);
|
||||
20
js/plugin/Routing.js
Normal file
20
js/plugin/Routing.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
BR.Routing = L.Routing.extend({
|
||||
options: {
|
||||
/* not implemented yet
|
||||
icons: {
|
||||
start: new L.Icon.Default({iconUrl: 'bower_components/leaflet-gpx/pin-icon-start.png'}),
|
||||
end: new L.Icon.Default(),
|
||||
normal: new L.Icon.Default()
|
||||
},*/
|
||||
snapping: null
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
var container = L.Routing.prototype.onAdd.call(this, map);
|
||||
|
||||
// enable drawing mode
|
||||
this.draw(true);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
17
js/plugin/Search.js
Normal file
17
js/plugin/Search.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
BR.Search = L.Control.Search.extend({
|
||||
options: {
|
||||
//url: 'http://nominatim.openstreetmap.org/search?format=json&q={s}',
|
||||
url: 'http://open.mapquestapi.com/nominatim/v1/search.php?format=json&q={s}',
|
||||
jsonpParam: 'json_callback',
|
||||
propertyName: 'display_name',
|
||||
propertyLoc: ['lat','lon'],
|
||||
markerLocation: false,
|
||||
autoType: false,
|
||||
autoCollapse: true,
|
||||
minLength: 2,
|
||||
zoom: 12
|
||||
},
|
||||
|
||||
// patch: interferes with draw plugin (adds all layers twice to map?)
|
||||
_onLayerAddRemove: function() {}
|
||||
});
|
||||
104
js/router/BRouter.js
Normal file
104
js/router/BRouter.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
L.BRouter = L.Class.extend({
|
||||
statics: {
|
||||
// http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml
|
||||
URL_TEMPLATE: 'http://localhost:17777/brouter?lonlats={lonlats}&nogos={nogos}&profile={profile}&alternativeidx={alternativeidx}&format={format}',
|
||||
PRECISION: 6,
|
||||
NUMBER_SEPARATOR: ',',
|
||||
GROUP_SEPARATOR: '|'
|
||||
},
|
||||
|
||||
options: {
|
||||
format: 'gpx'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
L.setOptions(this, options);
|
||||
},
|
||||
|
||||
setOptions: function(options) {
|
||||
L.setOptions(this, options);
|
||||
},
|
||||
|
||||
getUrl: function(latLngs, format) {
|
||||
var urlParams = {
|
||||
lonlats: this._getLonLatsString(latLngs),
|
||||
nogos: this._getNogosString(this.options.nogos),
|
||||
profile: this.options.profile,
|
||||
alternativeidx: this.options.alternative,
|
||||
format: format || this.options.format
|
||||
};
|
||||
var url = L.Util.template(L.BRouter.URL_TEMPLATE, urlParams);
|
||||
return url;
|
||||
},
|
||||
|
||||
getRoute: function(latLngs, cb) {
|
||||
var url = this.getUrl(latLngs);
|
||||
if (!url) {
|
||||
return cb(new Error('Error getting route URL'));
|
||||
}
|
||||
|
||||
var gpxLayer = new L.GPX(url, {
|
||||
async: true,
|
||||
polyline_options: {
|
||||
opacity: 0.6
|
||||
},
|
||||
marker_options: {
|
||||
startIconUrl: null,
|
||||
endIconUrl: null
|
||||
}
|
||||
}).on('loaded', function(e) {
|
||||
// leaflet.spin
|
||||
gpxLayer.fire('data:loaded');
|
||||
var gpx = e.target;
|
||||
|
||||
return cb(null, gpx.getLayers()[0]);
|
||||
})/* TODO no error handling in leaflet-gpx
|
||||
.on('error', function(e){
|
||||
console.error('error');
|
||||
gpxLayer.fire('data:loaded');
|
||||
return cb(new Error('Routing failed'));
|
||||
})*/;
|
||||
},
|
||||
|
||||
getRouteSegment: function(l1, l2, cb) {
|
||||
return this.getRoute([l1, l2], cb);
|
||||
},
|
||||
|
||||
_getLonLatsString: function(latLngs) {
|
||||
var s = '';
|
||||
for (var i = 0; i < latLngs.length; i++) {
|
||||
s += this._formatLatLng(latLngs[i]);
|
||||
if (i < (latLngs.length - 1)) {
|
||||
s += L.BRouter.GROUP_SEPARATOR;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
},
|
||||
|
||||
_getNogosString: function(nogos) {
|
||||
var s = '';
|
||||
for (var i = 0, circle; i < nogos.length; i++) {
|
||||
circle = nogos[i];
|
||||
s += this._formatLatLng(circle.getLatLng());
|
||||
s += L.BRouter.NUMBER_SEPARATOR;
|
||||
s += Math.round(circle.getRadius());
|
||||
if (i < (nogos.length - 1)) {
|
||||
s += L.BRouter.GROUP_SEPARATOR;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
},
|
||||
|
||||
// formats L.LatLng object as lng,lat string
|
||||
_formatLatLng: function(latLng) {
|
||||
var s = '';
|
||||
s += L.Util.formatNum(latLng.lng, L.BRouter.PRECISION);
|
||||
s += L.BRouter.NUMBER_SEPARATOR;
|
||||
s += L.Util.formatNum(latLng.lat, L.BRouter.PRECISION);
|
||||
return s;
|
||||
}
|
||||
});
|
||||
|
||||
L.bRouter = function (options) {
|
||||
return new L.BRouter(options);
|
||||
};
|
||||
29
js/router/brouterCgi.js
Normal file
29
js/router/brouterCgi.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// BRouter online demo interface
|
||||
// TODO remove or adopt to new structure (only supports two waypoints!)
|
||||
var brouterCgi = (function() {
|
||||
// http://h2096617.stratoserver.net/cgi-bin/brouter.sh?coords=13.404681_52.520185_13.340278_52.512356_trekking_0
|
||||
//var URL_TEMPLATE = '/cgi-bin/proxy.cgi?url=' + 'http://h2096617.stratoserver.net/cgi-bin/brouter.sh?coords={fromLng}_{fromLat}_{toLng}_{toLat}_{profile}_{alt}';
|
||||
var URL_TEMPLATE = '/proxy.php?url=' + 'cgi-bin/brouter.sh?coords={fromLng}_{fromLat}_{toLng}_{toLat}_{profile}_{alt}';
|
||||
var PRECISION = 6;
|
||||
|
||||
function getUrl(polyline) {
|
||||
var latLngs = polyline.getLatLngs();
|
||||
var urlParams = {
|
||||
fromLat: L.Util.formatNum(latLngs[0].lat, PRECISION),
|
||||
fromLng: L.Util.formatNum(latLngs[0].lng, PRECISION),
|
||||
toLat: L.Util.formatNum(latLngs[1].lat, PRECISION),
|
||||
toLng: L.Util.formatNum(latLngs[1].lng, PRECISION),
|
||||
profile: 'trekking',
|
||||
alt: '0'
|
||||
};
|
||||
var url = L.Util.template(URL_TEMPLATE, urlParams);
|
||||
//console.log(url);
|
||||
//return 'test/test.gpx';
|
||||
return url;
|
||||
}
|
||||
|
||||
return {
|
||||
getUrl: getUrl
|
||||
}
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue