initial commit

This commit is contained in:
Norbert Renner 2014-01-27 18:34:48 +01:00
parent 37980ff82b
commit 4cc16bccd0
17 changed files with 789 additions and 0 deletions

37
js/control/Control.js Normal file
View 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
View 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">&nbsp;</div><div class="value">';
if (urls.gpx) {
html += '<a href="' + urls.gpx + '" download="brouter.gpx" target="_blank">GPX</a> &middot; ';
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
View 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 = "&nbsp;";
return container;
}
});

View 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
View 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
View 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 &copy; <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 &copy; <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 &copy; <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> &copy; Arndt Brenschede, '
+ 'routing + map data &copy; <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
View 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
View 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
View 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
View 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
View 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
View 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
}
})();