This commit is contained in:
Götz 2017-10-14 01:39:11 +02:00
parent 20054971b1
commit 8c88520f8c
39 changed files with 4558 additions and 84 deletions

View File

@ -0,0 +1,82 @@
/**
* Created by michigg on 14.10.17.
*/
var endLat = '';
var endLon = '';
var startLat = 49.8955663;
var startLon = 10.886907899999999;
document.addEventListener('DOMContentLoaded', loadData);
document.addEventListener('DOMContentLoaded', resizeMap);
document.addEventListener('DOMContentLoaded', getPos);
window.onresize = resizeMap;
function loadData() {
var address = document.getElementById('nav_data').getAttribute('data-address')
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
generateMap(JSON.parse(this.response))
}
};
xhttp.open("GET", "https://nominatim.openstreetmap.org/search/?format=json&city=Bamberg&street=" + address, true);
xhttp.send();
}
function generateMap(streets) {
var address = document.getElementById('nav_data').getAttribute('data-address')
var address_short = document.getElementById('nav_data').getAttribute('data-short')
console.log(streets)
if (streets.length > 0) {
endLon = streets[0]['lon'];
endLat = streets[0]['lat'];
console.log(endLat)
var map = L.map('map').setView([endLat, endLon], 16);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker([endLat, endLon]).addTo(map)
.bindPopup(address_short + '</br>' + address)
.openPopup();
L.marker([startLat, startLon]).addTo(map)
.bindPopup('You are here!')
.openPopup();
}
}
function resizeMap() {
var height = window.innerHeight
document.getElementById('map').style.height = height + 'px'
}
function getPos() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
console.log(position);
startLat = position.coords.latitude;
startLon = position.coords.longitude;
}, function (err) {
console.log(err.code)
console.log(err.message)
})
} else {
document.getElementById('map').innerHTML('Geolocation not available')
}
}
function makeRoute() {
console.log('make route')
console.log('set waypoints:\nStart: ' + startLat + ' Lo: ' + startLon + " \nEnd: " + endLat + ' Lo: ' + endLon)
L.Routing.control({
waypoints: [
L.latLng(startLat, startLon),
L.latLng(endLat, endLon)
]
}).addTo(map);
}

View File

@ -0,0 +1,3 @@
node_modules/
dist/*.js
.grunt/

View File

@ -0,0 +1,55 @@
{
/*
* ENVIRONMENTS
* =================
*/
// Define globals exposed by modern browsers.
"browser": true,
// Define globals exposed by Node.js.
"node": true,
"globals": {"L": false},
/*
* ENFORCING OPTIONS
* =================
*/
// Force all variable names to use either camelCase style or UPPER_CASE
// with underscores.
"camelcase": true,
// Prohibit use of == and != in favor of === and !==.
"eqeqeq": true,
// Suppress warnings about == null comparisons.
"eqnull": true,
// Enforce tab width of 2 spaces.
"indent": 2,
"smarttabs": true,
// Prohibit use of a variable before it is defined.
"latedef": true,
// Require capitalized names for constructor functions.
"newcap": true,
// Enforce use of single quotation marks for strings.
"quotmark": "single",
// Prohibit trailing whitespace.
"trailing": true,
// Prohibit use of explicitly undeclared variables.
"undef": true,
// Warn when variables are defined but never used.
"unused": true,
// All loops and conditionals should have braces.
"curly": true
}

View File

@ -0,0 +1 @@
.grunt/

View File

@ -0,0 +1,17 @@
before_install:
- npm install -g grunt-cli
before_deploy:
- grunt before-deploy
after_deploy:
- grunt after-deploy
language: node_js
node_js:
- 6
notifications:
email: false
deploy:
provider: npm
email: "per@liedman.net"
"on":
all_branches: true
tags: true

View File

@ -0,0 +1,13 @@
## 2.2.0 (2015-03-03)
#### Bug Fixes
* **OSRM:** make sure waypoint options exist before checking them. ([2dd9d778]([object Object]/commit/2dd9d778baae30f0ef7f2d535a9d12ee7283a500))
#### Features
* **plan:** routeWhileDragging also used when adding a new waypoint by dragging the route li ([6ab58fb2]([object Object]/commit/6ab58fb25d56f650ab0af4e168748e2997070582))

View File

@ -0,0 +1,8 @@
## ISC License
Copyright (c) 2014, Per Liedman (per@liedman.net)
Turn instruction icons Copyright (c) 2014, Mapbox (mapbox.com)
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,36 @@
[Leaflet Routing Machine]((http://www.liedman.net/leaflet-routing-machine/)) [![NPM version](https://img.shields.io/npm/v/leaflet-routing-machine.svg)](https://www.npmjs.com/package/leaflet-routing-machine) ![Leaflet 1.0.0 compatible!](https://img.shields.io/badge/Leaflet%201.0.0-%E2%9C%93-1EB300.svg?style=flat)
=======================
Find the way from A to B on a Leaflet map. The plugin supports multiple backends:
* [OSRM](http://project-osrm.org/) - builtin and used by default
* [Mapbox Directions API](https://www.mapbox.com/developers/api/directions/) - builtin with the class `L.Routing.Mapbox`
* [GraphHopper](https://graphhopper.com/) - through plugin [lrm-graphopper](https://github.com/perliedman/lrm-graphhopper)
* [Mapzen Valhalla](https://mapzen.com/projects/valhalla/) - through plugin [lrm-valhalla](https://github.com/valhalla/lrm-valhalla)
* [TomTom Online Routing API](http://developer.tomtom.com/io-docs) - through plugin [lrm-tomtom](https://github.com/mrohnstock/lrm-tomtom) by [Mathias Rohnstock](https://github.com/mrohnstock)
## Features
* Standard Leaflet control, with Leaflet look and feel
* Routing from start to destination, with possibility of via points
* Add, edit and remove waypoints through both address input and using the map
* Multiple language support
* Highly customizable for advanced use
* Customizable look (theming / skins)
* Open Source released under ISC License (more or less equivalent with the MIT license)
__Go to the [Leaflet Routing Machine site](http://www.liedman.net/leaflet-routing-machine/) for more information, demos, tutorials and more.__
## Support and New Features
Leaflet Routing Machine is in many ways already a feature complete routing UI. Most likely, your requirements are already covered and require very little adaptation.
If you have more complex requirements, need new features or just need some support, I am open to doing paid custom work and support around Leaflet Routing Machine for your organization. Contact me at [per@liedman.net](mailto:per@liedman.net) and we'll sort this out!
## Building
```sh
npm install
```
This requires [Node and npm](http://nodejs.org/), as well as `grunt`.

View File

@ -0,0 +1,33 @@
{
"name": "leaflet-routing-machine",
"version": "3.2.5",
"homepage": "https://github.com/perliedman/leaflet-routing-machine",
"authors": [
"Per Liedman <per@liedman.net> (http://www.liedman.net/)"
],
"description": "Find the way from A to B on a Leaflet map, using OSRM as backend.",
"keywords": [
"leaflet",
"route"
],
"license": "MIT",
"main": [
"dist/leaflet.routing.icons.png",
"dist/leaflet-routing-machine.css",
"dist/leaflet.routing.icons.svg",
"dist/leaflet-routing-machine.js",
"dist/routing-icon.png"
],
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"src",
"css",
"Gruntfile.js",
"package.json",
"build.sh"
]
}

View File

@ -0,0 +1,733 @@
(function (factory) {
// Packaging/modules magic dance
var L;
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet');
module.exports = factory(L);
} else {
// Browser globals
if (typeof window.L === 'undefined')
throw 'Leaflet must be loaded first';
factory(window.L);
}
}(function (L) {
'use strict';
L.Control.Geocoder = L.Control.extend({
options: {
showResultIcons: false,
collapsed: true,
expand: 'click',
position: 'topright',
placeholder: 'Search...',
errorMessage: 'Nothing found.'
},
_callbackId: 0,
initialize: function (options) {
L.Util.setOptions(this, options);
if (!this.options.geocoder) {
this.options.geocoder = new L.Control.Geocoder.Nominatim();
}
},
onAdd: function (map) {
var className = 'leaflet-control-geocoder',
container = L.DomUtil.create('div', className),
icon = L.DomUtil.create('div', 'leaflet-control-geocoder-icon', container),
form = this._form = L.DomUtil.create('form', className + '-form', container),
input;
this._map = map;
this._container = container;
input = this._input = L.DomUtil.create('input');
input.type = 'text';
input.placeholder = this.options.placeholder;
L.DomEvent.addListener(input, 'keydown', this._keydown, this);
//L.DomEvent.addListener(input, 'onpaste', this._clearResults, this);
//L.DomEvent.addListener(input, 'oninput', this._clearResults, this);
this._errorElement = document.createElement('div');
this._errorElement.className = className + '-form-no-error';
this._errorElement.innerHTML = this.options.errorMessage;
this._alts = L.DomUtil.create('ul', className + '-alternatives leaflet-control-geocoder-alternatives-minimized');
form.appendChild(input);
form.appendChild(this._errorElement);
container.appendChild(this._alts);
L.DomEvent.addListener(form, 'submit', this._geocode, this);
if (this.options.collapsed) {
if (this.options.expand === 'click') {
L.DomEvent.addListener(icon, 'click', function(e) {
// TODO: touch
if (e.button === 0 && e.detail === 1) {
this._toggle();
}
}, this);
} else {
L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
this._map.on('movestart', this._collapse, this);
}
} else {
this._expand();
}
L.DomEvent.disableClickPropagation(container);
return container;
},
_geocodeResult: function (results) {
L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
if (results.length === 1) {
this._geocodeResultSelected(results[0]);
} else if (results.length > 0) {
this._alts.innerHTML = '';
this._results = results;
L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
for (var i = 0; i < results.length; i++) {
this._alts.appendChild(this._createAlt(results[i], i));
}
} else {
L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
}
},
markGeocode: function(result) {
this._map.fitBounds(result.bbox);
if (this._geocodeMarker) {
this._map.removeLayer(this._geocodeMarker);
}
this._geocodeMarker = new L.Marker(result.center)
.bindPopup(result.html || result.name)
.addTo(this._map)
.openPopup();
return this;
},
_geocode: function(event) {
L.DomEvent.preventDefault(event);
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
this._clearResults();
this.options.geocoder.geocode(this._input.value, this._geocodeResult, this);
return false;
},
_geocodeResultSelected: function(result) {
if (this.options.collapsed) {
this._collapse();
} else {
this._clearResults();
}
this.markGeocode(result);
},
_toggle: function() {
if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) {
this._collapse();
} else {
this._expand();
}
},
_expand: function () {
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
this._input.select();
},
_collapse: function () {
this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', '');
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
},
_clearResults: function () {
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
this._selection = null;
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
},
_createAlt: function(result, index) {
var li = document.createElement('li'),
a = L.DomUtil.create('a', '', li),
icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
text = result.html ? undefined : document.createTextNode(result.name);
if (icon) {
icon.src = result.icon;
}
a.href = '#';
a.setAttribute('data-result-index', index);
if (result.html) {
a.innerHTML = result.html;
} else {
a.appendChild(text);
}
L.DomEvent.addListener(li, 'click', function clickHandler(e) {
L.DomEvent.preventDefault(e);
this._geocodeResultSelected(result);
}, this);
return li;
},
_keydown: function(e) {
var _this = this,
select = function select(dir) {
if (_this._selection) {
L.DomUtil.removeClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
_this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
}
if (!_this._selection) {
_this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
}
if (_this._selection) {
L.DomUtil.addClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
}
};
switch (e.keyCode) {
// Escape
case 27:
this._collapse();
break;
// Up
case 38:
select(-1);
L.DomEvent.preventDefault(e);
break;
// Up
case 40:
select(1);
L.DomEvent.preventDefault(e);
break;
// Enter
case 13:
if (this._selection) {
var index = parseInt(this._selection.firstChild.getAttribute('data-result-index'), 10);
this._geocodeResultSelected(this._results[index]);
this._clearResults();
L.DomEvent.preventDefault(e);
}
}
return true;
}
});
L.Control.geocoder = function(id, options) {
return new L.Control.Geocoder(id, options);
};
L.Control.Geocoder.callbackId = 0;
L.Control.Geocoder.jsonp = function(url, params, callback, context, jsonpParam) {
var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++);
params[jsonpParam || 'callback'] = callbackId;
window[callbackId] = L.Util.bind(callback, context);
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url + L.Util.getParamString(params);
script.id = callbackId;
document.getElementsByTagName('head')[0].appendChild(script);
};
L.Control.Geocoder.getJSON = function(url, params, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url + L.Util.getParamString(params), true);
xmlHttp.send(null);
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState != 4) return;
if (xmlHttp.status != 200 && req.status != 304) return;
callback(JSON.parse(xmlHttp.response));
};
};
L.Control.Geocoder.template = function (str, data, htmlEscape) {
return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
var value = data[key];
if (value === undefined) {
value = '';
} else if (typeof value === 'function') {
value = value(data);
}
return L.Control.Geocoder.htmlEscape(value);
});
};
// Adapted from handlebars.js
// https://github.com/wycats/handlebars.js/
L.Control.Geocoder.htmlEscape = (function() {
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escape = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#x27;',
'`': '&#x60;'
};
function escapeChar(chr) {
return escape[chr];
}
return function(string) {
if (string == null) {
return '';
} else if (!string) {
return string + '';
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
string = '' + string;
if (!possible.test(string)) {
return string;
}
return string.replace(badChars, escapeChar);
};
})();
L.Control.Geocoder.Nominatim = L.Class.extend({
options: {
serviceUrl: '//nominatim.openstreetmap.org/',
geocodingQueryParams: {},
reverseQueryParams: {},
htmlTemplate: function(r) {
var a = r.address,
parts = [];
if (a.road || a.building) {
parts.push('{building} {road} {house_number}');
}
if (a.city || a.town || a.village) {
parts.push('<span class="' + (parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : '') +
'">{postcode} {city}{town}{village}</span>');
}
if (a.state || a.country) {
parts.push('<span class="' + (parts.length > 0 ? 'leaflet-control-geocoder-address-context' : '') +
'">{state} {country}</span>');
}
return L.Control.Geocoder.template(parts.join('<br/>'), a, true);
}
},
initialize: function(options) {
L.Util.setOptions(this, options);
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'search/', L.extend({
q: query,
limit: 5,
format: 'json',
addressdetails: 1
}, this.options.geocodingQueryParams),
function(data) {
var results = [];
for (var i = data.length - 1; i >= 0; i--) {
var bbox = data[i].boundingbox;
for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]);
results[i] = {
icon: data[i].icon,
name: data[i].display_name,
html: this.options.htmlTemplate ?
this.options.htmlTemplate(data[i])
: undefined,
bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]),
center: L.latLng(data[i].lat, data[i].lon),
properties: data[i]
};
}
cb.call(context, results);
}, this, 'json_callback');
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'reverse/', L.extend({
lat: location.lat,
lon: location.lng,
zoom: Math.round(Math.log(scale / 256) / Math.log(2)),
addressdetails: 1,
format: 'json'
}, this.options.reverseQueryParams), function(data) {
var result = [],
loc;
if (data && data.lat && data.lon) {
loc = L.latLng(data.lat, data.lon);
result.push({
name: data.display_name,
html: this.options.htmlTemplate ?
this.options.htmlTemplate(data)
: undefined,
center: loc,
bounds: L.latLngBounds(loc, loc),
properties: data
});
}
cb.call(context, result);
}, this, 'json_callback');
}
});
L.Control.Geocoder.nominatim = function(options) {
return new L.Control.Geocoder.Nominatim(options);
};
L.Control.Geocoder.Bing = L.Class.extend({
initialize: function(key) {
this.key = key;
},
geocode : function (query, cb, context) {
L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations', {
query: query,
key : this.key
}, function(data) {
var results = [];
for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
var resource = data.resourceSets[0].resources[i],
bbox = resource.bbox;
results[i] = {
name: resource.name,
bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
center: L.latLng(resource.point.coordinates)
};
}
cb.call(context, results);
}, this, 'jsonp');
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations/' + location.lat + ',' + location.lng, {
key : this.key
}, function(data) {
var results = [];
for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
var resource = data.resourceSets[0].resources[i],
bbox = resource.bbox;
results[i] = {
name: resource.name,
bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
center: L.latLng(resource.point.coordinates)
};
}
cb.call(context, results);
}, this, 'jsonp');
}
});
L.Control.Geocoder.bing = function(key) {
return new L.Control.Geocoder.Bing(key);
};
L.Control.Geocoder.RaveGeo = L.Class.extend({
options: {
querySuffix: '',
deepSearch: true,
wordBased: false
},
jsonp: function(params, callback, context) {
var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++),
paramParts = [];
params.prepend = callbackId + '(';
params.append = ')';
for (var p in params) {
paramParts.push(p + '=' + escape(params[p]));
}
window[callbackId] = L.Util.bind(callback, context);
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = this._serviceUrl + '?' + paramParts.join('&');
script.id = callbackId;
document.getElementsByTagName('head')[0].appendChild(script);
},
initialize: function(serviceUrl, scheme, options) {
L.Util.setOptions(this, options);
this._serviceUrl = serviceUrl;
this._scheme = scheme;
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp(this._serviceUrl, {
address: query + this.options.querySuffix,
scheme: this._scheme,
outputFormat: 'jsonp',
deepSearch: this.options.deepSearch,
wordBased: this.options.wordBased
}, function(data) {
var results = [];
for (var i = data.length - 1; i >= 0; i--) {
var r = data[i],
c = L.latLng(r.y, r.x);
results[i] = {
name: r.address,
bbox: L.latLngBounds([c]),
center: c
};
}
cb.call(context, results);
}, this);
}
});
L.Control.Geocoder.raveGeo = function(serviceUrl, scheme, options) {
return new L.Control.Geocoder.RaveGeo(serviceUrl, scheme, options);
};
L.Control.Geocoder.MapQuest = L.Class.extend({
initialize: function(key) {
// MapQuest seems to provide URI encoded API keys,
// so to avoid encoding them twice, we decode them here
this._key = decodeURIComponent(key);
},
_formatName: function() {
var r = [],
i;
for (i = 0; i < arguments.length; i++) {
if (arguments[i]) {
r.push(arguments[i]);
}
}
return r.join(', ');
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/address', {
key: this._key,
location: query,
limit: 5,
outFormat: 'json'
}, function(data) {
var results = [],
loc,
latLng;
if (data.results && data.results[0].locations) {
for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
loc = data.results[0].locations[i];
latLng = L.latLng(loc.latLng);
results[i] = {
name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
bbox: L.latLngBounds(latLng, latLng),
center: latLng
};
}
}
cb.call(context, results);
}, this);
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/reverse', {
key: this._key,
location: location.lat + ',' + location.lng,
outputFormat: 'json'
}, function(data) {
var results = [],
loc,
latLng;
if (data.results && data.results[0].locations) {
for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
loc = data.results[0].locations[i];
latLng = L.latLng(loc.latLng);
results[i] = {
name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
bbox: L.latLngBounds(latLng, latLng),
center: latLng
};
}
}
cb.call(context, results);
}, this);
}
});
L.Control.Geocoder.mapQuest = function(key) {
return new L.Control.Geocoder.MapQuest(key);
};
L.Control.Geocoder.Mapbox = L.Class.extend({
options: {
service_url: 'https://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/'
},
initialize: function(access_token) {
this._access_token = access_token;
},
geocode: function(query, cb, context) {
L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(query) + '.json', {
access_token: this._access_token,
}, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.features && data.features.length) {
for (var i = 0; i <= data.features.length - 1; i++) {
loc = data.features[i];
latLng = L.latLng(loc.center.reverse());
if(loc.hasOwnProperty('bbox'))
{
latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
}
else
{
latLngBounds = L.latLngBounds(latLng, latLng);
}
results[i] = {
name: loc.place_name,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(location.lng) + ',' + encodeURIComponent(location.lat) + '.json', {
access_token: this._access_token,
}, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.features && data.features.length) {
for (var i = 0; i <= data.features.length - 1; i++) {
loc = data.features[i];
latLng = L.latLng(loc.center.reverse());
if(loc.hasOwnProperty('bbox'))
{
latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
}
else
{
latLngBounds = L.latLngBounds(latLng, latLng);
}
results[i] = {
name: loc.place_name,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
}
});
L.Control.Geocoder.mapbox = function(access_token) {
return new L.Control.Geocoder.Mapbox(access_token);
};
L.Control.Geocoder.Google = L.Class.extend({
options: {
service_url: 'https://maps.googleapis.com/maps/api/geocode/json'
},
initialize: function(key) {
this._key = key;
},
geocode: function(query, cb, context) {
var params = {
address: query,
};
if(this._key && this._key.length)
{
params['key'] = this._key
}
L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.results && data.results.length) {
for (var i = 0; i <= data.results.length - 1; i++) {
loc = data.results[i];
latLng = L.latLng(loc.geometry.location);
latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
results[i] = {
name: loc.formatted_address,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
},
reverse: function(location, scale, cb, context) {
var params = {
latlng: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng)
};
if(this._key && this._key.length)
{
params['key'] = this._key
}
L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.results && data.results.length) {
for (var i = 0; i <= data.results.length - 1; i++) {
loc = data.results[i];
latLng = L.latLng(loc.geometry.location);
latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
results[i] = {
name: loc.formatted_address,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
}
});
L.Control.Geocoder.google = function(key) {
return new L.Control.Geocoder.Google(key);
};
return L.Control.Geocoder;
}));

View File

@ -0,0 +1,4 @@
window.lrmConfig = {
// serviceUrl: 'https://api.mapbox.com/directions/v5',
// profile: 'mapbox/driving',
};

View File

@ -0,0 +1,23 @@
html {
font-family: sans-serif;
font-size: 11pt;
}
.map {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.results {
background-color: white;
opacity: 0.8;
position: absolute;
top: 12px;
right: 12px;
width: 320px;
height: 480px;
overflow-y: scroll;
}

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Leaflet OSRM Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.css" />
<link rel="stylesheet" href="../dist/leaflet-routing-machine.css" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="map" class="map"></div>
<script src="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.js"></script>
<script src="../dist/leaflet-routing-machine.js"></script>
<script src="Control.Geocoder.js"></script>
<script src="config.js"></script>
<script src="index.js"></script>
</body>
</html>

View File

@ -0,0 +1,25 @@
var map = L.map('map');
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
var control = L.Routing.control(L.extend(window.lrmConfig, {
waypoints: [
L.latLng(57.74, 11.94),
L.latLng(57.6792, 11.949)
],
geocoder: L.Control.Geocoder.nominatim(),
routeWhileDragging: true,
reverseWaypoints: true,
showAlternatives: true,
altLineOptions: {
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'blue', opacity: 0.5, weight: 2}
]
}
})).addTo(map);
L.Routing.errorControl(control).addTo(map);

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Leaflet OSRM Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.css" />
<link rel="stylesheet" href="../dist/leaflet-routing-machine.css" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="map" class="map"></div>
<script src="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.js"></script>
<script src="../dist/leaflet-routing-machine.js"></script>
<script src="Control.Geocoder.js"></script>
<script src="config.js"></script>
<script src="localized.js"></script>
</body>
</html>

View File

@ -0,0 +1,26 @@
var map = L.map('map');
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
var control = L.Routing.control(L.extend(window.lrmConfig, {
waypoints: [
L.latLng(57.74, 11.94),
L.latLng(57.6792, 11.949)
],
language: 'de',
geocoder: L.Control.Geocoder.nominatim(),
routeWhileDragging: true,
reverseWaypoints: true,
showAlternatives: true,
altLineOptions: {
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'blue', opacity: 0.5, weight: 2}
]
}
})).addTo(map);
L.Routing.errorControl(control).addTo(map);

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Leaflet OSRM Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.css" />
<link rel="stylesheet" href="../dist/leaflet-routing-machine.css" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="map" class="map"></div>
<script src="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.js"></script>
<script src="../dist/leaflet-routing-machine.js"></script>
<script src="Control.Geocoder.js"></script>
<script src="mapbox.js"></script>
</body>
</html>

View File

@ -0,0 +1,26 @@
var map = L.map('map');
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
var control = L.Routing.control({
waypoints: [
L.latLng(57.74, 11.94),
L.latLng(57.6792, 11.949)
],
geocoder: L.Control.Geocoder.nominatim(),
routeWhileDragging: true,
reverseWaypoints: true,
showAlternatives: true,
altLineOptions: {
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'blue', opacity: 0.5, weight: 2}
]
},
router: L.Routing.mapbox('your-token-here')
}).addTo(map);
L.Routing.errorControl(control).addTo(map);

View File

@ -0,0 +1,48 @@
{
"name": "leaflet-routing-machine",
"version": "3.2.5",
"description": "Routing for Leaflet",
"directories": {
"example": "examples",
"dist": "dist"
},
"scripts": {
"prepublish": "browserify -t browserify-shim -p browserify-derequire -o dist/leaflet-routing-machine.js src/index.js && uglifyjs dist/leaflet-routing-machine.js >dist/leaflet-routing-machine.min.js",
"publish": "scripts/publish.sh",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/perliedman/leaflet-routing-machine.git"
},
"keywords": [
"leaflet",
"routing",
"osrm"
],
"author": {
"name": "Per Liedman",
"email": "per@liedman.net"
},
"license": "ISC",
"bugs": {
"url": "https://github.com/perliedman/leaflet-routing-machine/issues"
},
"homepage": "https://github.com/perliedman/leaflet-routing-machine",
"browserify-shim": {
"leaflet": "global:L"
},
"main": "./dist/leaflet-routing-machine.js",
"devDependencies": {
"browserify": "^13.3.0",
"browserify-derequire": "^0.9.4",
"browserify-shim": "^3.7.0",
"corslite": "0.0.7",
"derequire": "^2.0.6",
"polyline": "0.2.0",
"uglify-js": "^2.7.5"
},
"dependencies": {
"osrm-text-instructions": "^0.1.0"
}
}

View File

@ -0,0 +1,38 @@
#!/bin/bash
VERSION=`echo "console.log(require('./package.json').version)" | node`
ORIGIN=`git remote -v|grep origin|head -n1|cut -f2|cut -d" " -f1`
TMP=/tmp/.gh-pages-update
CWD=`pwd`
git checkout -b build
echo Building dist files for $VERSION...
grunt
echo Done.
git add dist/* -f
git add bower.json -f
git commit -m "v$VERSION"
git tag v$VERSION -f
git push origin build --tags -f
echo Updating dist files on gh-pages...
rm -rf $TMP
git clone -b gh-pages . $TMP
cd $TMP
git remote set-url origin $ORIGIN
git fetch origin gh-pages
git rebase origin/gh-pages
cp -a $CWD/dist $TMP
git add -f dist/
git commit -m "Dist files $VERSION"
git push origin gh-pages
cd $CWD
rm -rf $TMP
git checkout master
git branch -D build

View File

@ -0,0 +1,210 @@
(function() {
'use strict';
var L = require('leaflet');
module.exports = L.Class.extend({
options: {
timeout: 500,
blurTimeout: 100,
noResultsMessage: 'No results found.'
},
initialize: function(elem, callback, context, options) {
L.setOptions(this, options);
this._elem = elem;
this._resultFn = options.resultFn ? L.Util.bind(options.resultFn, options.resultContext) : null;
this._autocomplete = options.autocompleteFn ? L.Util.bind(options.autocompleteFn, options.autocompleteContext) : null;
this._selectFn = L.Util.bind(callback, context);
this._container = L.DomUtil.create('div', 'leaflet-routing-geocoder-result');
this._resultTable = L.DomUtil.create('table', '', this._container);
// TODO: looks a bit like a kludge to register same for input and keypress -
// browsers supporting both will get duplicate events; just registering
// input will not catch enter, though.
L.DomEvent.addListener(this._elem, 'input', this._keyPressed, this);
L.DomEvent.addListener(this._elem, 'keypress', this._keyPressed, this);
L.DomEvent.addListener(this._elem, 'keydown', this._keyDown, this);
L.DomEvent.addListener(this._elem, 'blur', function() {
if (this._isOpen) {
this.close();
}
}, this);
},
close: function() {
L.DomUtil.removeClass(this._container, 'leaflet-routing-geocoder-result-open');
this._isOpen = false;
},
_open: function() {
var rect = this._elem.getBoundingClientRect();
if (!this._container.parentElement) {
// See notes section under https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX
// This abomination is required to support all flavors of IE
var scrollX = (window.pageXOffset !== undefined) ? window.pageXOffset
: (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var scrollY = (window.pageYOffset !== undefined) ? window.pageYOffset
: (document.documentElement || document.body.parentNode || document.body).scrollTop;
this._container.style.left = (rect.left + scrollX) + 'px';
this._container.style.top = (rect.bottom + scrollY) + 'px';
this._container.style.width = (rect.right - rect.left) + 'px';
document.body.appendChild(this._container);
}
L.DomUtil.addClass(this._container, 'leaflet-routing-geocoder-result-open');
this._isOpen = true;
},
_setResults: function(results) {
var i,
tr,
td,
text;
delete this._selection;
this._results = results;
while (this._resultTable.firstChild) {
this._resultTable.removeChild(this._resultTable.firstChild);
}
for (i = 0; i < results.length; i++) {
tr = L.DomUtil.create('tr', '', this._resultTable);
tr.setAttribute('data-result-index', i);
td = L.DomUtil.create('td', '', tr);
text = document.createTextNode(results[i].name);
td.appendChild(text);
// mousedown + click because:
// http://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event
L.DomEvent.addListener(td, 'mousedown', L.DomEvent.preventDefault);
L.DomEvent.addListener(td, 'click', this._createClickListener(results[i]));
}
if (!i) {
tr = L.DomUtil.create('tr', '', this._resultTable);
td = L.DomUtil.create('td', 'leaflet-routing-geocoder-no-results', tr);
td.innerHTML = this.options.noResultsMessage;
}
this._open();
if (results.length > 0) {
// Select the first entry
this._select(1);
}
},
_createClickListener: function(r) {
var resultSelected = this._resultSelected(r);
return L.bind(function() {
this._elem.blur();
resultSelected();
}, this);
},
_resultSelected: function(r) {
return L.bind(function() {
this.close();
this._elem.value = r.name;
this._lastCompletedText = r.name;
this._selectFn(r);
}, this);
},
_keyPressed: function(e) {
var index;
if (this._isOpen && e.keyCode === 13 && this._selection) {
index = parseInt(this._selection.getAttribute('data-result-index'), 10);
this._resultSelected(this._results[index])();
L.DomEvent.preventDefault(e);
return;
}
if (e.keyCode === 13) {
this._complete(this._resultFn, true);
return;
}
if (this._autocomplete && document.activeElement === this._elem) {
if (this._timer) {
clearTimeout(this._timer);
}
this._timer = setTimeout(L.Util.bind(function() { this._complete(this._autocomplete); }, this),
this.options.timeout);
return;
}
this._unselect();
},
_select: function(dir) {
var sel = this._selection;
if (sel) {
L.DomUtil.removeClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
sel = sel[dir > 0 ? 'nextSibling' : 'previousSibling'];
}
if (!sel) {
sel = this._resultTable[dir > 0 ? 'firstChild' : 'lastChild'];
}
if (sel) {
L.DomUtil.addClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
this._selection = sel;
}
},
_unselect: function() {
if (this._selection) {
L.DomUtil.removeClass(this._selection.firstChild, 'leaflet-routing-geocoder-selected');
}
delete this._selection;
},
_keyDown: function(e) {
if (this._isOpen) {
switch (e.keyCode) {
// Escape
case 27:
this.close();
L.DomEvent.preventDefault(e);
return;
// Up
case 38:
this._select(-1);
L.DomEvent.preventDefault(e);
return;
// Down
case 40:
this._select(1);
L.DomEvent.preventDefault(e);
return;
}
}
},
_complete: function(completeFn, trySelect) {
var v = this._elem.value;
function completeResults(results) {
this._lastCompletedText = v;
if (trySelect && results.length === 1) {
this._resultSelected(results[0])();
} else {
this._setResults(results);
}
}
if (!v) {
return;
}
if (v !== this._lastCompletedText) {
completeFn(v, completeResults, this);
} else if (trySelect) {
completeResults.call(this, this._results);
}
}
});
})();

View File

@ -0,0 +1,347 @@
(function() {
'use strict';
var L = require('leaflet');
var Itinerary = require('./itinerary');
var Line = require('./line');
var Plan = require('./plan');
var OSRMv1 = require('./osrm-v1');
module.exports = Itinerary.extend({
options: {
fitSelectedRoutes: 'smart',
routeLine: function(route, options) { return new Line(route, options); },
autoRoute: true,
routeWhileDragging: false,
routeDragInterval: 500,
waypointMode: 'connect',
showAlternatives: false,
defaultErrorHandler: function(e) {
console.error('Routing error:', e.error);
}
},
initialize: function(options) {
L.Util.setOptions(this, options);
this._router = this.options.router || new OSRMv1(options);
this._plan = this.options.plan || new Plan(this.options.waypoints, options);
this._requestCount = 0;
Itinerary.prototype.initialize.call(this, options);
this.on('routeselected', this._routeSelected, this);
if (this.options.defaultErrorHandler) {
this.on('routingerror', this.options.defaultErrorHandler);
}
this._plan.on('waypointschanged', this._onWaypointsChanged, this);
if (options.routeWhileDragging) {
this._setupRouteDragging();
}
if (this.options.autoRoute) {
this.route();
}
},
_onZoomEnd: function() {
if (!this._selectedRoute ||
!this._router.requiresMoreDetail) {
return;
}
var map = this._map;
if (this._router.requiresMoreDetail(this._selectedRoute,
map.getZoom(), map.getBounds())) {
this.route({
callback: L.bind(function(err, routes) {
var i;
if (!err) {
for (i = 0; i < routes.length; i++) {
this._routes[i].properties = routes[i].properties;
}
this._updateLineCallback(err, routes);
}
}, this),
simplifyGeometry: false,
geometryOnly: true
});
}
},
onAdd: function(map) {
var container = Itinerary.prototype.onAdd.call(this, map);
this._map = map;
this._map.addLayer(this._plan);
this._map.on('zoomend', this._onZoomEnd, this);
if (this._plan.options.geocoder) {
container.insertBefore(this._plan.createGeocoders(), container.firstChild);
}
return container;
},
onRemove: function(map) {
map.off('zoomend', this._onZoomEnd, this);
if (this._line) {
map.removeLayer(this._line);
}
map.removeLayer(this._plan);
if (this._alternatives && this._alternatives.length > 0) {
for (var i = 0, len = this._alternatives.length; i < len; i++) {
map.removeLayer(this._alternatives[i]);
}
}
return Itinerary.prototype.onRemove.call(this, map);
},
getWaypoints: function() {
return this._plan.getWaypoints();
},
setWaypoints: function(waypoints) {
this._plan.setWaypoints(waypoints);
return this;
},
spliceWaypoints: function() {
var removed = this._plan.spliceWaypoints.apply(this._plan, arguments);
return removed;
},
getPlan: function() {
return this._plan;
},
getRouter: function() {
return this._router;
},
_routeSelected: function(e) {
var route = this._selectedRoute = e.route,
alternatives = this.options.showAlternatives && e.alternatives,
fitMode = this.options.fitSelectedRoutes,
fitBounds =
(fitMode === 'smart' && !this._waypointsVisible()) ||
(fitMode !== 'smart' && fitMode);
this._updateLines({route: route, alternatives: alternatives});
if (fitBounds) {
this._map.fitBounds(this._line.getBounds());
}
if (this.options.waypointMode === 'snap') {
this._plan.off('waypointschanged', this._onWaypointsChanged, this);
this.setWaypoints(route.waypoints);
this._plan.on('waypointschanged', this._onWaypointsChanged, this);
}
},
_waypointsVisible: function() {
var wps = this.getWaypoints(),
mapSize,
bounds,
boundsSize,
i,
p;
try {
mapSize = this._map.getSize();
for (i = 0; i < wps.length; i++) {
p = this._map.latLngToLayerPoint(wps[i].latLng);
if (bounds) {
bounds.extend(p);
} else {
bounds = L.bounds([p]);
}
}
boundsSize = bounds.getSize();
return (boundsSize.x > mapSize.x / 5 ||
boundsSize.y > mapSize.y / 5) && this._waypointsInViewport();
} catch (e) {
return false;
}
},
_waypointsInViewport: function() {
var wps = this.getWaypoints(),
mapBounds,
i;
try {
mapBounds = this._map.getBounds();
} catch (e) {
return false;
}
for (i = 0; i < wps.length; i++) {
if (mapBounds.contains(wps[i].latLng)) {
return true;
}
}
return false;
},
_updateLines: function(routes) {
var addWaypoints = this.options.addWaypoints !== undefined ?
this.options.addWaypoints : true;
this._clearLines();
// add alternatives first so they lie below the main route
this._alternatives = [];
if (routes.alternatives) routes.alternatives.forEach(function(alt, i) {
this._alternatives[i] = this.options.routeLine(alt,
L.extend({
isAlternative: true
}, this.options.altLineOptions || this.options.lineOptions));
this._alternatives[i].addTo(this._map);
this._hookAltEvents(this._alternatives[i]);
}, this);
this._line = this.options.routeLine(routes.route,
L.extend({
addWaypoints: addWaypoints,
extendToWaypoints: this.options.waypointMode === 'connect'
}, this.options.lineOptions));
this._line.addTo(this._map);
this._hookEvents(this._line);
},
_hookEvents: function(l) {
l.on('linetouched', function(e) {
this._plan.dragNewWaypoint(e);
}, this);
},
_hookAltEvents: function(l) {
l.on('linetouched', function(e) {
var alts = this._routes.slice();
var selected = alts.splice(e.target._route.routesIndex, 1)[0];
this.fire('routeselected', {route: selected, alternatives: alts});
}, this);
},
_onWaypointsChanged: function(e) {
if (this.options.autoRoute) {
this.route({});
}
if (!this._plan.isReady()) {
this._clearLines();
this._clearAlts();
}
this.fire('waypointschanged', {waypoints: e.waypoints});
},
_setupRouteDragging: function() {
var timer = 0,
waypoints;
this._plan.on('waypointdrag', L.bind(function(e) {
waypoints = e.waypoints;
if (!timer) {
timer = setTimeout(L.bind(function() {
this.route({
waypoints: waypoints,
geometryOnly: true,
callback: L.bind(this._updateLineCallback, this)
});
timer = undefined;
}, this), this.options.routeDragInterval);
}
}, this));
this._plan.on('waypointdragend', function() {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
this.route();
}, this);
},
_updateLineCallback: function(err, routes) {
if (!err) {
routes = routes.slice();
var selected = routes.splice(this._selectedRoute.routesIndex, 1)[0];
this._updateLines({route: selected, alternatives: routes });
} else if (err.type !== 'abort') {
this._clearLines();
}
},
route: function(options) {
var ts = ++this._requestCount,
wps;
if (this._pendingRequest && this._pendingRequest.abort) {
this._pendingRequest.abort();
this._pendingRequest = null;
}
options = options || {};
if (this._plan.isReady()) {
if (this.options.useZoomParameter) {
options.z = this._map && this._map.getZoom();
}
wps = options && options.waypoints || this._plan.getWaypoints();
this.fire('routingstart', {waypoints: wps});
this._pendingRequest = this._router.route(wps, function(err, routes) {
this._pendingRequest = null;
if (options.callback) {
return options.callback.call(this, err, routes);
}
// Prevent race among multiple requests,
// by checking the current request's count
// against the last request's; ignore result if
// this isn't the last request.
if (ts === this._requestCount) {
this._clearLines();
this._clearAlts();
if (err && err.type !== 'abort') {
this.fire('routingerror', {error: err});
return;
}
routes.forEach(function(route, i) { route.routesIndex = i; });
if (!options.geometryOnly) {
this.fire('routesfound', {waypoints: wps, routes: routes});
this.setAlternatives(routes);
} else {
var selectedRoute = routes.splice(0,1)[0];
this._routeSelected({route: selectedRoute, alternatives: routes});
}
}
}, this, options);
}
},
_clearLines: function() {
if (this._line) {
this._map.removeLayer(this._line);
delete this._line;
}
if (this._alternatives && this._alternatives.length) {
for (var i in this._alternatives) {
this._map.removeLayer(this._alternatives[i]);
}
this._alternatives = [];
}
}
});
})();

View File

@ -0,0 +1,55 @@
(function() {
'use strict';
var L = require('leaflet');
module.exports = L.Control.extend({
options: {
header: 'Routing error',
formatMessage: function(error) {
if (error.status < 0) {
return 'Calculating the route caused an error. Technical description follows: <code><pre>' +
error.message + '</pre></code';
} else {
return 'The route could not be calculated. ' +
error.message;
}
}
},
initialize: function(routingControl, options) {
L.Control.prototype.initialize.call(this, options);
routingControl
.on('routingerror', L.bind(function(e) {
if (this._element) {
this._element.children[1].innerHTML = this.options.formatMessage(e.error);
this._element.style.visibility = 'visible';
}
}, this))
.on('routingstart', L.bind(function() {
if (this._element) {
this._element.style.visibility = 'hidden';
}
}, this));
},
onAdd: function() {
var header,
message;
this._element = L.DomUtil.create('div', 'leaflet-bar leaflet-routing-error');
this._element.style.visibility = 'hidden';
header = L.DomUtil.create('h3', null, this._element);
message = L.DomUtil.create('span', null, this._element);
header.innerHTML = this.options.header;
return this._element;
},
onRemove: function() {
delete this._element;
}
});
})();

View File

@ -0,0 +1,159 @@
(function() {
'use strict';
var L = require('leaflet');
var Localization = require('./localization');
module.exports = L.Class.extend({
options: {
units: 'metric',
unitNames: null,
language: 'en',
roundingSensitivity: 1,
distanceTemplate: '{value} {unit}'
},
initialize: function(options) {
L.setOptions(this, options);
var langs = L.Util.isArray(this.options.language) ?
this.options.language :
[this.options.language, 'en'];
this._localization = new Localization(langs);
},
formatDistance: function(d /* Number (meters) */, sensitivity) {
var un = this.options.unitNames || this._localization.localize('units'),
simpleRounding = sensitivity <= 0,
round = simpleRounding ? function(v) { return v; } : L.bind(this._round, this),
v,
yards,
data,
pow10;
if (this.options.units === 'imperial') {
yards = d / 0.9144;
if (yards >= 1000) {
data = {
value: round(d / 1609.344, sensitivity),
unit: un.miles
};
} else {
data = {
value: round(yards, sensitivity),
unit: un.yards
};
}
} else {
v = round(d, sensitivity);
data = {
value: v >= 1000 ? (v / 1000) : v,
unit: v >= 1000 ? un.kilometers : un.meters
};
}
if (simpleRounding) {
data.value = data.value.toFixed(-sensitivity);
}
return L.Util.template(this.options.distanceTemplate, data);
},
_round: function(d, sensitivity) {
var s = sensitivity || this.options.roundingSensitivity,
pow10 = Math.pow(10, (Math.floor(d / s) + '').length - 1),
r = Math.floor(d / pow10),
p = (r > 5) ? pow10 : pow10 / 2;
return Math.round(d / p) * p;
},
formatTime: function(t /* Number (seconds) */) {
var un = this.options.unitNames || this._localization.localize('units');
// More than 30 seconds precision looks ridiculous
t = Math.round(t / 30) * 30;
if (t > 86400) {
return Math.round(t / 3600) + ' ' + un.hours;
} else if (t > 3600) {
return Math.floor(t / 3600) + ' ' + un.hours + ' ' +
Math.round((t % 3600) / 60) + ' ' + un.minutes;
} else if (t > 300) {
return Math.round(t / 60) + ' ' + un.minutes;
} else if (t > 60) {
return Math.floor(t / 60) + ' ' + un.minutes +
(t % 60 !== 0 ? ' ' + (t % 60) + ' ' + un.seconds : '');
} else {
return t + ' ' + un.seconds;
}
},
formatInstruction: function(instr, i) {
if (instr.text === undefined) {
return this.capitalize(L.Util.template(this._getInstructionTemplate(instr, i),
L.extend({}, instr, {
exitStr: instr.exit ? this._localization.localize('formatOrder')(instr.exit) : '',
dir: this._localization.localize(['directions', instr.direction]),
modifier: this._localization.localize(['directions', instr.modifier])
})));
} else {
return instr.text;
}
},
getIconName: function(instr, i) {
switch (instr.type) {
case 'Head':
if (i === 0) {
return 'depart';
}
break;
case 'WaypointReached':
return 'via';
case 'Roundabout':
return 'enter-roundabout';
case 'DestinationReached':
return 'arrive';
}
switch (instr.modifier) {
case 'Straight':
return 'continue';
case 'SlightRight':
return 'bear-right';
case 'Right':
return 'turn-right';
case 'SharpRight':
return 'sharp-right';
case 'TurnAround':
case 'Uturn':
return 'u-turn';
case 'SharpLeft':
return 'sharp-left';
case 'Left':
return 'turn-left';
case 'SlightLeft':
return 'bear-left';
}
},
capitalize: function(s) {
return s.charAt(0).toUpperCase() + s.substring(1);
},
_getInstructionTemplate: function(instr, i) {
var type = instr.type === 'Straight' ? (i === 0 ? 'Head' : 'Continue') : instr.type,
strings = this._localization.localize(['instructions', type]);
if (!strings) {
strings = [
this._localization.localize(['directions', type]),
' ' + this._localization.localize(['instructions', 'Onto'])
];
}
return strings[0] + (strings.length > 1 && instr.road ? strings[1] : '');
}
});
})();

View File

@ -0,0 +1,146 @@
(function() {
'use strict';
var L = require('leaflet');
var Autocomplete = require('./autocomplete');
var Localization = require('./localization');
function selectInputText(input) {
if (input.setSelectionRange) {
// On iOS, select() doesn't work
input.setSelectionRange(0, 9999);
} else {
// On at least IE8, setSeleectionRange doesn't exist
input.select();
}
}
module.exports = L.Class.extend({
includes: L.Mixin.Events,
options: {
createGeocoder: function(i, nWps, options) {
var container = L.DomUtil.create('div', 'leaflet-routing-geocoder'),
input = L.DomUtil.create('input', '', container),
remove = options.addWaypoints ? L.DomUtil.create('span', 'leaflet-routing-remove-waypoint', container) : undefined;
input.disabled = !options.addWaypoints;
return {
container: container,
input: input,
closeButton: remove
};
},
geocoderPlaceholder: function(i, numberWaypoints, geocoderElement) {
var l = new Localization(geocoderElement.options.language).localize('ui');
return i === 0 ?
l.startPlaceholder :
(i < numberWaypoints - 1 ?
L.Util.template(l.viaPlaceholder, {viaNumber: i}) :
l.endPlaceholder);
},
geocoderClass: function() {
return '';
},
waypointNameFallback: function(latLng) {
var ns = latLng.lat < 0 ? 'S' : 'N',
ew = latLng.lng < 0 ? 'W' : 'E',
lat = (Math.round(Math.abs(latLng.lat) * 10000) / 10000).toString(),
lng = (Math.round(Math.abs(latLng.lng) * 10000) / 10000).toString();
return ns + lat + ', ' + ew + lng;
},
maxGeocoderTolerance: 200,
autocompleteOptions: {},
language: 'en',
},
initialize: function(wp, i, nWps, options) {
L.setOptions(this, options);
var g = this.options.createGeocoder(i, nWps, this.options),
closeButton = g.closeButton,
geocoderInput = g.input;
geocoderInput.setAttribute('placeholder', this.options.geocoderPlaceholder(i, nWps, this));
geocoderInput.className = this.options.geocoderClass(i, nWps);
this._element = g;
this._waypoint = wp;
this.update();
// This has to be here, or geocoder's value will not be properly
// initialized.
// TODO: look into why and make _updateWaypointName fix this.
geocoderInput.value = wp.name;
L.DomEvent.addListener(geocoderInput, 'click', function() {
selectInputText(this);
}, geocoderInput);
if (closeButton) {
L.DomEvent.addListener(closeButton, 'click', function() {
this.fire('delete', { waypoint: this._waypoint });
}, this);
}
new Autocomplete(geocoderInput, function(r) {
geocoderInput.value = r.name;
wp.name = r.name;
wp.latLng = r.center;
this.fire('geocoded', { waypoint: wp, value: r });
}, this, L.extend({
resultFn: this.options.geocoder.geocode,
resultContext: this.options.geocoder,
autocompleteFn: this.options.geocoder.suggest,
autocompleteContext: this.options.geocoder
}, this.options.autocompleteOptions));
},
getContainer: function() {
return this._element.container;
},
setValue: function(v) {
this._element.input.value = v;
},
update: function(force) {
var wp = this._waypoint,
wpCoords;
wp.name = wp.name || '';
if (wp.latLng && (force || !wp.name)) {
wpCoords = this.options.waypointNameFallback(wp.latLng);
if (this.options.geocoder && this.options.geocoder.reverse) {
this.options.geocoder.reverse(wp.latLng, 67108864 /* zoom 18 */, function(rs) {
if (rs.length > 0 && rs[0].center.distanceTo(wp.latLng) < this.options.maxGeocoderTolerance) {
wp.name = rs[0].name;
} else {
wp.name = wpCoords;
}
this._update();
}, this);
} else {
wp.name = wpCoords;
this._update();
}
}
},
focus: function() {
var input = this._element.input;
input.focus();
selectInputText(input);
},
_update: function() {
var wp = this._waypoint,
value = wp && wp.name ? wp.name : '';
this.setValue(value);
this.fire('reversegeocoded', {waypoint: wp, value: value});
}
});
})();

View File

@ -0,0 +1,80 @@
var L = require('leaflet'),
Control = require('./control'),
Itinerary = require('./itinerary'),
Line = require('./line'),
OSRMv1 = require('./osrm-v1'),
Plan = require('./plan'),
Waypoint = require('./waypoint'),
Autocomplete = require('./autocomplete'),
Formatter = require('./formatter'),
GeocoderElement = require('./geocoder-element'),
Localization = require('./localization'),
ItineraryBuilder = require('./itinerary-builder'),
Mapbox = require('./mapbox'),
ErrorControl = require('./error-control');
L.routing = {
control: function(options) { return new Control(options); },
itinerary: function(options) {
return Itinerary(options);
},
line: function(route, options) {
return new Line(route, options);
},
plan: function(waypoints, options) {
return new Plan(waypoints, options);
},
waypoint: function(latLng, name, options) {
return new Waypoint(latLng, name, options);
},
osrmv1: function(options) {
return new OSRMv1(options);
},
localization: function(options) {
return new Localization(options);
},
formatter: function(options) {
return new Formatter(options);
},
geocoderElement: function(wp, i, nWps, plan) {
return new L.Routing.GeocoderElement(wp, i, nWps, plan);
},
itineraryBuilder: function(options) {
return new ItineraryBuilder(options);
},
mapbox: function(accessToken, options) {
return new Mapbox(accessToken, options);
},
errorControl: function(routingControl, options) {
return new ErrorControl(routingControl, options);
},
autocomplete: function(elem, callback, context, options) {
return new Autocomplete(elem, callback, context, options);
}
};
module.exports = L.Routing = {
Control: Control,
Itinerary: Itinerary,
Line: Line,
OSRMv1: OSRMv1,
Plan: Plan,
Waypoint: Waypoint,
Autocomplete: Autocomplete,
Formatter: Formatter,
GeocoderElement: GeocoderElement,
Localization: Localization,
Formatter: Formatter,
ItineraryBuilder: ItineraryBuilder,
// Legacy; remove these in next major release
control: L.routing.control,
itinerary: L.routing.itinerary,
line: L.routing.line,
plan: L.routing.plan,
waypoint: L.routing.waypoint,
osrmv1: L.routing.osrmv1,
geocoderElement: L.routing.geocoderElement,
mapbox: L.routing.mapbox,
errorControl: L.routing.errorControl,
};

View File

@ -0,0 +1,44 @@
(function() {
'use strict';
var L = require('leaflet');
module.exports = L.Class.extend({
options: {
containerClassName: ''
},
initialize: function(options) {
L.setOptions(this, options);
},
createContainer: function(className) {
var table = L.DomUtil.create('table', className || ''),
colgroup = L.DomUtil.create('colgroup', '', table);
L.DomUtil.create('col', 'leaflet-routing-instruction-icon', colgroup);
L.DomUtil.create('col', 'leaflet-routing-instruction-text', colgroup);
L.DomUtil.create('col', 'leaflet-routing-instruction-distance', colgroup);
return table;
},
createStepsContainer: function() {
return L.DomUtil.create('tbody', '');
},
createStep: function(text, distance, icon, steps) {
var row = L.DomUtil.create('tr', '', steps),
span,
td;
td = L.DomUtil.create('td', '', row);
span = L.DomUtil.create('span', 'leaflet-routing-icon leaflet-routing-icon-'+icon, td);
td.appendChild(span);
td = L.DomUtil.create('td', '', row);
td.appendChild(document.createTextNode(text));
td = L.DomUtil.create('td', '', row);
td.appendChild(document.createTextNode(distance));
return row;
}
});
})();

View File

@ -0,0 +1,225 @@
(function() {
'use strict';
var L = require('leaflet');
var Formatter = require('./formatter');
var ItineraryBuilder = require('./itinerary-builder');
module.exports = L.Control.extend({
includes: L.Mixin.Events,
options: {
pointMarkerStyle: {
radius: 5,
color: '#03f',
fillColor: 'white',
opacity: 1,
fillOpacity: 0.7
},
summaryTemplate: '<h2>{name}</h2><h3>{distance}, {time}</h3>',
timeTemplate: '{time}',
containerClassName: '',
alternativeClassName: '',
minimizedClassName: '',
itineraryClassName: '',
totalDistanceRoundingSensitivity: -1,
show: true,
collapsible: undefined,
collapseBtn: function(itinerary) {
var collapseBtn = L.DomUtil.create('span', itinerary.options.collapseBtnClass);
L.DomEvent.on(collapseBtn, 'click', itinerary._toggle, itinerary);
itinerary._container.insertBefore(collapseBtn, itinerary._container.firstChild);
},
collapseBtnClass: 'leaflet-routing-collapse-btn'
},
initialize: function(options) {
L.setOptions(this, options);
this._formatter = this.options.formatter || new Formatter(this.options);
this._itineraryBuilder = this.options.itineraryBuilder || new ItineraryBuilder({
containerClassName: this.options.itineraryClassName
});
},
onAdd: function(map) {
var collapsible = this.options.collapsible;
collapsible = collapsible || (collapsible === undefined && map.getSize().x <= 640);
this._container = L.DomUtil.create('div', 'leaflet-routing-container leaflet-bar ' +
(!this.options.show ? 'leaflet-routing-container-hide ' : '') +
(collapsible ? 'leaflet-routing-collapsible ' : '') +
this.options.containerClassName);
this._altContainer = this.createAlternativesContainer();
this._container.appendChild(this._altContainer);
L.DomEvent.disableClickPropagation(this._container);
L.DomEvent.addListener(this._container, 'mousewheel', function(e) {
L.DomEvent.stopPropagation(e);
});
if (collapsible) {
this.options.collapseBtn(this);
}
return this._container;
},
onRemove: function() {
},
createAlternativesContainer: function() {
return L.DomUtil.create('div', 'leaflet-routing-alternatives-container');
},
setAlternatives: function(routes) {
var i,
alt,
altDiv;
this._clearAlts();
this._routes = routes;
for (i = 0; i < this._routes.length; i++) {
alt = this._routes[i];
altDiv = this._createAlternative(alt, i);
this._altContainer.appendChild(altDiv);
this._altElements.push(altDiv);
}
this._selectRoute({route: this._routes[0], alternatives: this._routes.slice(1)});
return this;
},
show: function() {
L.DomUtil.removeClass(this._container, 'leaflet-routing-container-hide');
},
hide: function() {
L.DomUtil.addClass(this._container, 'leaflet-routing-container-hide');
},
_toggle: function() {
var collapsed = L.DomUtil.hasClass(this._container, 'leaflet-routing-container-hide');
this[collapsed ? 'show' : 'hide']();
},
_createAlternative: function(alt, i) {
var altDiv = L.DomUtil.create('div', 'leaflet-routing-alt ' +
this.options.alternativeClassName +
(i > 0 ? ' leaflet-routing-alt-minimized ' + this.options.minimizedClassName : '')),
template = this.options.summaryTemplate,
data = L.extend({
name: alt.name,
distance: this._formatter.formatDistance(alt.summary.totalDistance, this.options.totalDistanceRoundingSensitivity),
time: this._formatter.formatTime(alt.summary.totalTime)
}, alt);
altDiv.innerHTML = typeof(template) === 'function' ? template(data) : L.Util.template(template, data);
L.DomEvent.addListener(altDiv, 'click', this._onAltClicked, this);
this.on('routeselected', this._selectAlt, this);
altDiv.appendChild(this._createItineraryContainer(alt));
return altDiv;
},
_clearAlts: function() {
var el = this._altContainer;
while (el && el.firstChild) {
el.removeChild(el.firstChild);
}
this._altElements = [];
},
_createItineraryContainer: function(r) {
var container = this._itineraryBuilder.createContainer(),
steps = this._itineraryBuilder.createStepsContainer(),
i,
instr,
step,
distance,
text,
icon;
container.appendChild(steps);
for (i = 0; i < r.instructions.length; i++) {
instr = r.instructions[i];
text = this._formatter.formatInstruction(instr, i);
distance = this._formatter.formatDistance(instr.distance);
icon = this._formatter.getIconName(instr, i);
step = this._itineraryBuilder.createStep(text, distance, icon, steps);
this._addRowListeners(step, r.coordinates[instr.index]);
}
return container;
},
_addRowListeners: function(row, coordinate) {
L.DomEvent.addListener(row, 'mouseover', function() {
this._marker = L.circleMarker(coordinate,
this.options.pointMarkerStyle).addTo(this._map);
}, this);
L.DomEvent.addListener(row, 'mouseout', function() {
if (this._marker) {
this._map.removeLayer(this._marker);
delete this._marker;
}
}, this);
L.DomEvent.addListener(row, 'click', function(e) {
this._map.panTo(coordinate);
L.DomEvent.stopPropagation(e);
}, this);
},
_onAltClicked: function(e) {
var altElem = e.target || window.event.srcElement;
while (!L.DomUtil.hasClass(altElem, 'leaflet-routing-alt')) {
altElem = altElem.parentElement;
}
var j = this._altElements.indexOf(altElem);
var alts = this._routes.slice();
var route = alts.splice(j, 1)[0];
this.fire('routeselected', {
route: route,
alternatives: alts
});
},
_selectAlt: function(e) {
var altElem,
j,
n,
classFn;
altElem = this._altElements[e.route.routesIndex];
if (L.DomUtil.hasClass(altElem, 'leaflet-routing-alt-minimized')) {
for (j = 0; j < this._altElements.length; j++) {
n = this._altElements[j];
classFn = j === e.route.routesIndex ? 'removeClass' : 'addClass';
L.DomUtil[classFn](n, 'leaflet-routing-alt-minimized');
if (this.options.minimizedClassName) {
L.DomUtil[classFn](n, this.options.minimizedClassName);
}
if (j !== e.route.routesIndex) n.scrollTop = 0;
}
}
L.DomEvent.stop(e);
},
_selectRoute: function(routes) {
if (this._marker) {
this._map.removeLayer(this._marker);
delete this._marker;
}
this.fire('routeselected', routes);
}
});
})();

View File

@ -0,0 +1,130 @@
(function() {
'use strict';
var L = require('leaflet');
module.exports = L.LayerGroup.extend({
includes: L.Mixin.Events,
options: {
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'red', opacity: 1, weight: 2}
],
missingRouteStyles: [
{color: 'black', opacity: 0.15, weight: 7},
{color: 'white', opacity: 0.6, weight: 4},
{color: 'gray', opacity: 0.8, weight: 2, dashArray: '7,12'}
],
addWaypoints: true,
extendToWaypoints: true,
missingRouteTolerance: 10
},
initialize: function(route, options) {
L.setOptions(this, options);
L.LayerGroup.prototype.initialize.call(this, options);
this._route = route;
if (this.options.extendToWaypoints) {
this._extendToWaypoints();
}
this._addSegment(
route.coordinates,
this.options.styles,
this.options.addWaypoints);
},
getBounds: function() {
return L.latLngBounds(this._route.coordinates);
},
_findWaypointIndices: function() {
var wps = this._route.inputWaypoints,
indices = [],
i;
for (i = 0; i < wps.length; i++) {
indices.push(this._findClosestRoutePoint(wps[i].latLng));
}
return indices;
},
_findClosestRoutePoint: function(latlng) {
var minDist = Number.MAX_VALUE,
minIndex,
i,
d;
for (i = this._route.coordinates.length - 1; i >= 0 ; i--) {
// TODO: maybe do this in pixel space instead?
d = latlng.distanceTo(this._route.coordinates[i]);
if (d < minDist) {
minIndex = i;
minDist = d;
}
}
return minIndex;
},
_extendToWaypoints: function() {
var wps = this._route.inputWaypoints,
wpIndices = this._getWaypointIndices(),
i,
wpLatLng,
routeCoord;
for (i = 0; i < wps.length; i++) {
wpLatLng = wps[i].latLng;
routeCoord = L.latLng(this._route.coordinates[wpIndices[i]]);
if (wpLatLng.distanceTo(routeCoord) >
this.options.missingRouteTolerance) {
this._addSegment([wpLatLng, routeCoord],
this.options.missingRouteStyles);
}
}
},
_addSegment: function(coords, styles, mouselistener) {
var i,
pl;
for (i = 0; i < styles.length; i++) {
pl = L.polyline(coords, styles[i]);
this.addLayer(pl);
if (mouselistener) {
pl.on('mousedown', this._onLineTouched, this);
}
}
},
_findNearestWpBefore: function(i) {
var wpIndices = this._getWaypointIndices(),
j = wpIndices.length - 1;
while (j >= 0 && wpIndices[j] > i) {
j--;
}
return j;
},
_onLineTouched: function(e) {
var afterIndex = this._findNearestWpBefore(this._findClosestRoutePoint(e.latlng));
this.fire('linetouched', {
afterIndex: afterIndex,
latlng: e.latlng
});
},
_getWaypointIndices: function() {
if (!this._wpIndices) {
this._wpIndices = this._route.waypointIndices || this._findWaypointIndices();
}
return this._wpIndices;
}
});
})();

View File

@ -0,0 +1,713 @@
(function() {
'use strict';
var spanish = {
directions: {
N: 'norte',
NE: 'noreste',
E: 'este',
SE: 'sureste',
S: 'sur',
SW: 'suroeste',
W: 'oeste',
NW: 'noroeste',
SlightRight: 'leve giro a la derecha',
Right: 'derecha',
SharpRight: 'giro pronunciado a la derecha',
SlightLeft: 'leve giro a la izquierda',
Left: 'izquierda',
SharpLeft: 'giro pronunciado a la izquierda',
Uturn: 'media vuelta'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Derecho {dir}', ' sobre {road}'],
'Continue':
['Continuar {dir}', ' en {road}'],
'TurnAround':
['Dar vuelta'],
'WaypointReached':
['Llegó a un punto del camino'],
'Roundabout':
['Tomar {exitStr} salida en la rotonda', ' en {road}'],
'DestinationReached':
['Llegada a destino'],
'Fork': ['En el cruce gira a {modifier}', ' hacia {road}'],
'Merge': ['Incorpórate {modifier}', ' hacia {road}'],
'OnRamp': ['Gira {modifier} en la salida', ' hacia {road}'],
'OffRamp': ['Toma la salida {modifier}', ' hacia {road}'],
'EndOfRoad': ['Gira {modifier} al final de la carretera', ' hacia {road}'],
'Onto': 'hacia {road}'
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Inicio',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'Destino'
},
units: {
meters: 'm',
kilometers: 'km',
yards: 'yd',
miles: 'mi',
hours: 'h',
minutes: 'min',
seconds: 's'
}
};
L.Routing = L.Routing || {};
var Localization = L.Class.extend({
initialize: function(langs) {
this._langs = L.Util.isArray(langs) ? langs : [langs, 'en'];
for (var i = 0, l = this._langs.length; i < l; i++) {
if (!Localization[this._langs[i]]) {
throw new Error('No localization for language "' + this._langs[i] + '".');
}
}
},
localize: function(keys) {
var dict,
key,
value;
keys = L.Util.isArray(keys) ? keys : [keys];
for (var i = 0, l = this._langs.length; i < l; i++) {
dict = Localization[this._langs[i]];
for (var j = 0, nKeys = keys.length; dict && j < nKeys; j++) {
key = keys[j];
value = dict[key];
dict = value;
}
if (value) {
return value;
}
}
}
});
module.exports = L.extend(Localization, {
'en': {
directions: {
N: 'north',
NE: 'northeast',
E: 'east',
SE: 'southeast',
S: 'south',
SW: 'southwest',
W: 'west',
NW: 'northwest',
SlightRight: 'slight right',
Right: 'right',
SharpRight: 'sharp right',
SlightLeft: 'slight left',
Left: 'left',
SharpLeft: 'sharp left',
Uturn: 'Turn around'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Head {dir}', ' on {road}'],
'Continue':
['Continue {dir}'],
'TurnAround':
['Turn around'],
'WaypointReached':
['Waypoint reached'],
'Roundabout':
['Take the {exitStr} exit in the roundabout', ' onto {road}'],
'DestinationReached':
['Destination reached'],
'Fork': ['At the fork, turn {modifier}', ' onto {road}'],
'Merge': ['Merge {modifier}', ' onto {road}'],
'OnRamp': ['Turn {modifier} on the ramp', ' onto {road}'],
'OffRamp': ['Take the ramp on the {modifier}', ' onto {road}'],
'EndOfRoad': ['Turn {modifier} at the end of the road', ' onto {road}'],
'Onto': 'onto {road}'
},
formatOrder: function(n) {
var i = n % 10 - 1,
suffix = ['st', 'nd', 'rd'];
return suffix[i] ? n + suffix[i] : n + 'th';
},
ui: {
startPlaceholder: 'Start',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'End'
},
units: {
meters: 'm',
kilometers: 'km',
yards: 'yd',
miles: 'mi',
hours: 'h',
minutes: 'min',
seconds: 's'
}
},
'de': {
directions: {
N: 'Norden',
NE: 'Nordosten',
E: 'Osten',
SE: 'Südosten',
S: 'Süden',
SW: 'Südwesten',
W: 'Westen',
NW: 'Nordwesten',
SlightRight: 'leicht rechts',
Right: 'rechts',
SharpRight: 'scharf rechts',
SlightLeft: 'leicht links',
Left: 'links',
SharpLeft: 'scharf links',
Uturn: 'Wenden'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Richtung {dir}', ' auf {road}'],
'Continue':
['Geradeaus Richtung {dir}', ' auf {road}'],
'SlightRight':
['Leicht rechts abbiegen', ' auf {road}'],
'Right':
['Rechts abbiegen', ' auf {road}'],
'SharpRight':
['Scharf rechts abbiegen', ' auf {road}'],
'TurnAround':
['Wenden'],
'SharpLeft':
['Scharf links abbiegen', ' auf {road}'],
'Left':
['Links abbiegen', ' auf {road}'],
'SlightLeft':
['Leicht links abbiegen', ' auf {road}'],
'WaypointReached':
['Zwischenhalt erreicht'],
'Roundabout':
['Nehmen Sie die {exitStr} Ausfahrt im Kreisverkehr', ' auf {road}'],
'DestinationReached':
['Sie haben ihr Ziel erreicht'],
'Fork': ['An der Kreuzung {modifier}', ' auf {road}'],
'Merge': ['Fahren Sie {modifier} weiter', ' auf {road}'],
'OnRamp': ['Fahren Sie {modifier} auf die Auffahrt', ' auf {road}'],
'OffRamp': ['Nehmen Sie die Ausfahrt {modifier}', ' auf {road}'],
'EndOfRoad': ['Fahren Sie {modifier} am Ende der Straße', ' auf {road}'],
'Onto': 'auf {road}'
},
formatOrder: function(n) {
return n + '.';
},
ui: {
startPlaceholder: 'Start',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'Ziel'
}
},
'sv': {
directions: {
N: 'norr',
NE: 'nordost',
E: 'öst',
SE: 'sydost',
S: 'syd',
SW: 'sydväst',
W: 'väst',
NW: 'nordväst',
SlightRight: 'svagt höger',
Right: 'höger',
SharpRight: 'skarpt höger',
SlightLeft: 'svagt vänster',
Left: 'vänster',
SharpLeft: 'skarpt vänster',
Uturn: 'Vänd'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Åk åt {dir}', ' till {road}'],
'Continue':
['Fortsätt {dir}'],
'SlightRight':
['Svagt höger', ' till {road}'],
'Right':
['Sväng höger', ' till {road}'],
'SharpRight':
['Skarpt höger', ' till {road}'],
'TurnAround':
['Vänd'],
'SharpLeft':
['Skarpt vänster', ' till {road}'],
'Left':
['Sväng vänster', ' till {road}'],
'SlightLeft':
['Svagt vänster', ' till {road}'],
'WaypointReached':
['Viapunkt nådd'],
'Roundabout':
['Tag {exitStr} avfarten i rondellen', ' till {road}'],
'DestinationReached':
['Framme vid resans mål'],
'Fork': ['Tag av {modifier}', ' till {road}'],
'Merge': ['Anslut {modifier} ', ' till {road}'],
'OnRamp': ['Tag påfarten {modifier}', ' till {road}'],
'OffRamp': ['Tag avfarten {modifier}', ' till {road}'],
'EndOfRoad': ['Sväng {modifier} vid vägens slut', ' till {road}'],
'Onto': 'till {road}'
},
formatOrder: function(n) {
return ['första', 'andra', 'tredje', 'fjärde', 'femte',
'sjätte', 'sjunde', 'åttonde', 'nionde', 'tionde'
/* Can't possibly be more than ten exits, can there? */][n - 1];
},
ui: {
startPlaceholder: 'Från',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'Till'
}
},
'es': spanish,
'sp': spanish,
'nl': {
directions: {
N: 'noordelijke',
NE: 'noordoostelijke',
E: 'oostelijke',
SE: 'zuidoostelijke',
S: 'zuidelijke',
SW: 'zuidewestelijke',
W: 'westelijke',
NW: 'noordwestelijke'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Vertrek in {dir} richting', ' de {road} op'],
'Continue':
['Ga in {dir} richting', ' de {road} op'],
'SlightRight':
['Volg de weg naar rechts', ' de {road} op'],
'Right':
['Ga rechtsaf', ' de {road} op'],
'SharpRight':
['Ga scherpe bocht naar rechts', ' de {road} op'],
'TurnAround':
['Keer om'],
'SharpLeft':
['Ga scherpe bocht naar links', ' de {road} op'],
'Left':
['Ga linksaf', ' de {road} op'],
'SlightLeft':
['Volg de weg naar links', ' de {road} op'],
'WaypointReached':
['Aangekomen bij tussenpunt'],
'Roundabout':
['Neem de {exitStr} afslag op de rotonde', ' de {road} op'],
'DestinationReached':
['Aangekomen op eindpunt'],
},
formatOrder: function(n) {
if (n === 1 || n >= 20) {
return n + 'ste';
} else {
return n + 'de';
}
},
ui: {
startPlaceholder: 'Vertrekpunt',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'Bestemming'
}
},
'fr': {
directions: {
N: 'nord',
NE: 'nord-est',
E: 'est',
SE: 'sud-est',
S: 'sud',
SW: 'sud-ouest',
W: 'ouest',
NW: 'nord-ouest'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Tout droit au {dir}', ' sur {road}'],
'Continue':
['Continuer au {dir}', ' sur {road}'],
'SlightRight':
['Légèrement à droite', ' sur {road}'],
'Right':
['A droite', ' sur {road}'],
'SharpRight':
['Complètement à droite', ' sur {road}'],
'TurnAround':
['Faire demi-tour'],
'SharpLeft':
['Complètement à gauche', ' sur {road}'],
'Left':
['A gauche', ' sur {road}'],
'SlightLeft':
['Légèrement à gauche', ' sur {road}'],
'WaypointReached':
['Point d\'étape atteint'],
'Roundabout':
['Au rond-point, prenez la {exitStr} sortie', ' sur {road}'],
'DestinationReached':
['Destination atteinte'],
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Départ',
viaPlaceholder: 'Intermédiaire {viaNumber}',
endPlaceholder: 'Arrivée'
}
},
'it': {
directions: {
N: 'nord',
NE: 'nord-est',
E: 'est',
SE: 'sud-est',
S: 'sud',
SW: 'sud-ovest',
W: 'ovest',
NW: 'nord-ovest'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Dritto verso {dir}', ' su {road}'],
'Continue':
['Continuare verso {dir}', ' su {road}'],
'SlightRight':
['Mantenere la destra', ' su {road}'],
'Right':
['A destra', ' su {road}'],
'SharpRight':
['Strettamente a destra', ' su {road}'],
'TurnAround':
['Fare inversione di marcia'],
'SharpLeft':
['Strettamente a sinistra', ' su {road}'],
'Left':
['A sinistra', ' sur {road}'],
'SlightLeft':
['Mantenere la sinistra', ' su {road}'],
'WaypointReached':
['Punto di passaggio raggiunto'],
'Roundabout':
['Alla rotonda, prendere la {exitStr} uscita'],
'DestinationReached':
['Destinazione raggiunta'],
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Partenza',
viaPlaceholder: 'Intermedia {viaNumber}',
endPlaceholder: 'Destinazione'
}
},
'pt': {
directions: {
N: 'norte',
NE: 'nordeste',
E: 'leste',
SE: 'sudeste',
S: 'sul',
SW: 'sudoeste',
W: 'oeste',
NW: 'noroeste',
SlightRight: 'curva ligeira a direita',
Right: 'direita',
SharpRight: 'curva fechada a direita',
SlightLeft: 'ligeira a esquerda',
Left: 'esquerda',
SharpLeft: 'curva fechada a esquerda',
Uturn: 'Meia volta'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Siga {dir}', ' na {road}'],
'Continue':
['Continue {dir}', ' na {road}'],
'SlightRight':
['Curva ligeira a direita', ' na {road}'],
'Right':
['Curva a direita', ' na {road}'],
'SharpRight':
['Curva fechada a direita', ' na {road}'],
'TurnAround':
['Retorne'],
'SharpLeft':
['Curva fechada a esquerda', ' na {road}'],
'Left':
['Curva a esquerda', ' na {road}'],
'SlightLeft':
['Curva ligueira a esquerda', ' na {road}'],
'WaypointReached':
['Ponto de interesse atingido'],
'Roundabout':
['Pegue a {exitStr} saída na rotatória', ' na {road}'],
'DestinationReached':
['Destino atingido'],
'Fork': ['Na encruzilhada, vire a {modifier}', ' na {road}'],
'Merge': ['Entre à {modifier}', ' na {road}'],
'OnRamp': ['Vire {modifier} na rampa', ' na {road}'],
'OffRamp': ['Entre na rampa na {modifier}', ' na {road}'],
'EndOfRoad': ['Vire {modifier} no fim da rua', ' na {road}'],
'Onto': 'na {road}'
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Origem',
viaPlaceholder: 'Intermédio {viaNumber}',
endPlaceholder: 'Destino'
}
},
'sk': {
directions: {
N: 'sever',
NE: 'serverovýchod',
E: 'východ',
SE: 'juhovýchod',
S: 'juh',
SW: 'juhozápad',
W: 'západ',
NW: 'serverozápad'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Mierte na {dir}', ' na {road}'],
'Continue':
['Pokračujte na {dir}', ' na {road}'],
'SlightRight':
['Mierne doprava', ' na {road}'],
'Right':
['Doprava', ' na {road}'],
'SharpRight':
['Prudko doprava', ' na {road}'],
'TurnAround':
['Otočte sa'],
'SharpLeft':
['Prudko doľava', ' na {road}'],
'Left':
['Doľava', ' na {road}'],
'SlightLeft':
['Mierne doľava', ' na {road}'],
'WaypointReached':
['Ste v prejazdovom bode.'],
'Roundabout':
['Odbočte na {exitStr} výjazde', ' na {road}'],
'DestinationReached':
['Prišli ste do cieľa.'],
},
formatOrder: function(n) {
var i = n % 10 - 1,
suffix = ['.', '.', '.'];
return suffix[i] ? n + suffix[i] : n + '.';
},
ui: {
startPlaceholder: 'Začiatok',
viaPlaceholder: 'Cez {viaNumber}',
endPlaceholder: 'Koniec'
}
},
'el': {
directions: {
N: 'βόρεια',
NE: 'βορειοανατολικά',
E: 'ανατολικά',
SE: 'νοτιοανατολικά',
S: 'νότια',
SW: 'νοτιοδυτικά',
W: 'δυτικά',
NW: 'βορειοδυτικά'
},
instructions: {
// instruction, postfix if the road is named
'Head':
['Κατευθυνθείτε {dir}', ' στην {road}'],
'Continue':
['Συνεχίστε {dir}', ' στην {road}'],
'SlightRight':
['Ελαφρώς δεξιά', ' στην {road}'],
'Right':
['Δεξιά', ' στην {road}'],
'SharpRight':
['Απότομη δεξιά στροφή', ' στην {road}'],
'TurnAround':
['Κάντε αναστροφή'],
'SharpLeft':
['Απότομη αριστερή στροφή', ' στην {road}'],
'Left':
['Αριστερά', ' στην {road}'],
'SlightLeft':
['Ελαφρώς αριστερά', ' στην {road}'],
'WaypointReached':
['Φτάσατε στο σημείο αναφοράς'],
'Roundabout':
['Ακολουθήστε την {exitStr} έξοδο στο κυκλικό κόμβο', ' στην {road}'],
'DestinationReached':
['Φτάσατε στον προορισμό σας'],
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Αφετηρία',
viaPlaceholder: 'μέσω {viaNumber}',
endPlaceholder: 'Προορισμός'
}
},
'ca': {
directions: {
N: 'nord',
NE: 'nord-est',
E: 'est',
SE: 'sud-est',
S: 'sud',
SW: 'sud-oest',
W: 'oest',
NW: 'nord-oest',
SlightRight: 'lleu gir a la dreta',
Right: 'dreta',
SharpRight: 'gir pronunciat a la dreta',
SlightLeft: 'gir pronunciat a l\'esquerra',
Left: 'esquerra',
SharpLeft: 'lleu gir a l\'esquerra',
Uturn: 'mitja volta'
},
instructions: {
'Head':
['Recte {dir}', ' sobre {road}'],
'Continue':
['Continuar {dir}'],
'TurnAround':
['donar la volta'],
'WaypointReached':
['Ha arribat a un punt del camí'],
'Roundabout':
['Agafar {exitStr} sortida a la rotonda', ' a {road}'],
'DestinationReached':
['Arribada al destí'],
'Fork': ['A la cruïlla gira a la {modifier}', ' cap a {road}'],
'Merge': ['Incorpora\'t {modifier}', ' a {road}'],
'OnRamp': ['Gira {modifier} a la sortida', ' cap a {road}'],
'OffRamp': ['Pren la sortida {modifier}', ' cap a {road}'],
'EndOfRoad': ['Gira {modifier} al final de la carretera', ' cap a {road}'],
'Onto': 'cap a {road}'
},
formatOrder: function(n) {
return n + 'º';
},
ui: {
startPlaceholder: 'Origen',
viaPlaceholder: 'Via {viaNumber}',
endPlaceholder: 'Destí'
},
units: {
meters: 'm',
kilometers: 'km',
yards: 'yd',
miles: 'mi',
hours: 'h',
minutes: 'min',
seconds: 's'
}
},
'ru': {
directions: {
N: 'север',
NE: 'северовосток',
E: 'восток',
SE: 'юговосток',
S: 'юг',
SW: 'югозапад',
W: 'запад',
NW: 'северозапад',
SlightRight: 'плавно направо',
Right: 'направо',
SharpRight: 'резко направо',
SlightLeft: 'плавно налево',
Left: 'налево',
SharpLeft: 'резко налево',
Uturn: 'развернуться'
},
instructions: {
'Head':
['Начать движение на {dir}', ' по {road}'],
'Continue':
['Продолжать движение на {dir}', ' по {road}'],
'SlightRight':
['Плавный поворот направо', ' на {road}'],
'Right':
['Направо', ' на {road}'],
'SharpRight':
['Резкий поворот направо', ' на {road}'],
'TurnAround':
['Развернуться'],
'SharpLeft':
['Резкий поворот налево', ' на {road}'],
'Left':
['Поворот налево', ' на {road}'],
'SlightLeft':
['Плавный поворот налево', ' на {road}'],
'WaypointReached':
['Точка достигнута'],
'Roundabout':
['{exitStr} съезд с кольца', ' на {road}'],
'DestinationReached':
['Окончание маршрута'],
'Fork': ['На развилке поверните {modifier}', ' на {road}'],
'Merge': ['Перестройтесь {modifier}', ' на {road}'],
'OnRamp': ['Поверните {modifier} на съезд', ' на {road}'],
'OffRamp': ['Съезжайте на {modifier}', ' на {road}'],
'EndOfRoad': ['Поверните {modifier} в конце дороги', ' на {road}'],
'Onto': 'на {road}'
},
formatOrder: function(n) {
return n + '-й';
},
ui: {
startPlaceholder: 'Начало',
viaPlaceholder: 'Через {viaNumber}',
endPlaceholder: 'Конец'
},
units: {
meters: 'м',
kilometers: 'км',
yards: 'ярд',
miles: 'ми',
hours: 'ч',
minutes: 'м',
seconds: 'с'
}
}
});
})();

View File

@ -0,0 +1,27 @@
(function() {
'use strict';
var L = require('leaflet');
var OSRMv1 = require('./osrm-v1');
/**
* Works against OSRM's new API in version 5.0; this has
* the API version v1.
*/
module.exports = OSRMv1.extend({
options: {
serviceUrl: 'https://api.mapbox.com/directions/v5',
profile: 'mapbox/driving',
useHints: false
},
initialize: function(accessToken, options) {
L.Routing.OSRMv1.prototype.initialize.call(this, options);
this.options.requestParameters = this.options.requestParameters || {};
/* jshint camelcase: false */
this.options.requestParameters.access_token = accessToken;
/* jshint camelcase: true */
}
});
})();

View File

@ -0,0 +1,368 @@
(function() {
'use strict';
var L = require('leaflet'),
corslite = require('corslite'),
polyline = require('polyline'),
osrmTextInstructions = require('osrm-text-instructions');
// Ignore camelcase naming for this file, since OSRM's API uses
// underscores.
/* jshint camelcase: false */
var Waypoint = require('./waypoint');
/**
* Works against OSRM's new API in version 5.0; this has
* the API version v1.
*/
module.exports = L.Class.extend({
options: {
serviceUrl: 'https://router.project-osrm.org/route/v1',
profile: 'driving',
timeout: 30 * 1000,
routingOptions: {
alternatives: true,
steps: true
},
polylinePrecision: 5,
useHints: true,
suppressDemoServerWarning: false,
language: 'en'
},
initialize: function(options) {
L.Util.setOptions(this, options);
this._hints = {
locations: {}
};
if (!this.options.suppressDemoServerWarning &&
this.options.serviceUrl.indexOf('//router.project-osrm.org') >= 0) {
console.warn('You are using OSRM\'s demo server. ' +
'Please note that it is **NOT SUITABLE FOR PRODUCTION USE**.\n' +
'Refer to the demo server\'s usage policy: ' +
'https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy\n\n' +
'To change, set the serviceUrl option.\n\n' +
'Please do not report issues with this server to neither ' +
'Leaflet Routing Machine or OSRM - it\'s for\n' +
'demo only, and will sometimes not be available, or work in ' +
'unexpected ways.\n\n' +
'Please set up your own OSRM server, or use a paid service ' +
'provider for production.');
}
},
route: function(waypoints, callback, context, options) {
var timedOut = false,
wps = [],
url,
timer,
wp,
i,
xhr;
options = L.extend({}, this.options.routingOptions, options);
url = this.buildRouteUrl(waypoints, options);
if (this.options.requestParameters) {
url += L.Util.getParamString(this.options.requestParameters, url);
}
timer = setTimeout(function() {
timedOut = true;
callback.call(context || callback, {
status: -1,
message: 'OSRM request timed out.'
});
}, this.options.timeout);
// Create a copy of the waypoints, since they
// might otherwise be asynchronously modified while
// the request is being processed.
for (i = 0; i < waypoints.length; i++) {
wp = waypoints[i];
wps.push(new Waypoint(wp.latLng, wp.name, wp.options));
}
return xhr = corslite(url, L.bind(function(err, resp) {
var data,
error = {};
clearTimeout(timer);
if (!timedOut) {
if (!err) {
try {
data = JSON.parse(resp.responseText);
try {
return this._routeDone(data, wps, options, callback, context);
} catch (ex) {
error.status = -3;
error.message = ex.toString();
}
} catch (ex) {
error.status = -2;
error.message = 'Error parsing OSRM response: ' + ex.toString();
}
} else {
error.message = 'HTTP request failed: ' + err.type +
(err.target && err.target.status ? ' HTTP ' + err.target.status + ': ' + err.target.statusText : '');
error.url = url;
error.status = -1;
error.target = err;
}
callback.call(context || callback, error);
} else {
xhr.abort();
}
}, this));
},
requiresMoreDetail: function(route, zoom, bounds) {
if (!route.properties.isSimplified) {
return false;
}
var waypoints = route.inputWaypoints,
i;
for (i = 0; i < waypoints.length; ++i) {
if (!bounds.contains(waypoints[i].latLng)) {
return true;
}
}
return false;
},
_routeDone: function(response, inputWaypoints, options, callback, context) {
var alts = [],
actualWaypoints,
i,
route;
context = context || callback;
if (response.code !== 'Ok') {
callback.call(context, {
status: response.code
});
return;
}
actualWaypoints = this._toWaypoints(inputWaypoints, response.waypoints);
for (i = 0; i < response.routes.length; i++) {
route = this._convertRoute(response.routes[i]);
route.inputWaypoints = inputWaypoints;
route.waypoints = actualWaypoints;
route.properties = {isSimplified: !options || !options.geometryOnly || options.simplifyGeometry};
alts.push(route);
}
this._saveHintData(response.waypoints, inputWaypoints);
callback.call(context, null, alts);
},
_convertRoute: function(responseRoute) {
var result = {
name: '',
coordinates: [],
instructions: [],
summary: {
totalDistance: responseRoute.distance,
totalTime: responseRoute.duration
}
},
legNames = [],
waypointIndices = [],
index = 0,
legCount = responseRoute.legs.length,
hasSteps = responseRoute.legs[0].steps.length > 0,
i,
j,
leg,
step,
geometry,
type,
modifier,
text,
stepToText;
if (this.options.stepToText) {
stepToText = this.options.stepToText;
} else {
var textInstructions = osrmTextInstructions('v5', this.options.language);
stepToText = textInstructions.compile.bind(textInstructions);
}
for (i = 0; i < legCount; i++) {
leg = responseRoute.legs[i];
legNames.push(leg.summary && leg.summary.charAt(0).toUpperCase() + leg.summary.substring(1));
for (j = 0; j < leg.steps.length; j++) {
step = leg.steps[j];
geometry = this._decodePolyline(step.geometry);
result.coordinates.push.apply(result.coordinates, geometry);
type = this._maneuverToInstructionType(step.maneuver, i === legCount - 1);
modifier = this._maneuverToModifier(step.maneuver);
text = stepToText(step);
if (type) {
if ((i == 0 && step.maneuver.type == 'depart') || step.maneuver.type == 'arrive') {
waypointIndices.push(index);
}
result.instructions.push({
type: type,
distance: step.distance,
time: step.duration,
road: step.name,
direction: this._bearingToDirection(step.maneuver.bearing_after),
exit: step.maneuver.exit,
index: index,
mode: step.mode,
modifier: modifier,
text: text
});
}
index += geometry.length;
}
}
result.name = legNames.join(', ');
if (!hasSteps) {
result.coordinates = this._decodePolyline(responseRoute.geometry);
} else {
result.waypointIndices = waypointIndices;
}
return result;
},
_bearingToDirection: function(bearing) {
var oct = Math.round(bearing / 45) % 8;
return ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'][oct];
},
_maneuverToInstructionType: function(maneuver, lastLeg) {
switch (maneuver.type) {
case 'new name':
return 'Continue';
case 'depart':
return 'Head';
case 'arrive':
return lastLeg ? 'DestinationReached' : 'WaypointReached';
case 'roundabout':
case 'rotary':
return 'Roundabout';
case 'merge':
case 'fork':
case 'on ramp':
case 'off ramp':
case 'end of road':
return this._camelCase(maneuver.type);
// These are all reduced to the same instruction in the current model
//case 'turn':
//case 'ramp': // deprecated in v5.1
default:
return this._camelCase(maneuver.modifier);
}
},
_maneuverToModifier: function(maneuver) {
var modifier = maneuver.modifier;
switch (maneuver.type) {
case 'merge':
case 'fork':
case 'on ramp':
case 'off ramp':
case 'end of road':
modifier = this._leftOrRight(modifier);
}
return modifier && this._camelCase(modifier);
},
_camelCase: function(s) {
var words = s.split(' '),
result = '';
for (var i = 0, l = words.length; i < l; i++) {
result += words[i].charAt(0).toUpperCase() + words[i].substring(1);
}
return result;
},
_leftOrRight: function(d) {
return d.indexOf('left') >= 0 ? 'Left' : 'Right';
},
_decodePolyline: function(routeGeometry) {
var cs = polyline.decode(routeGeometry, this.options.polylinePrecision),
result = new Array(cs.length),
i;
for (i = cs.length - 1; i >= 0; i--) {
result[i] = L.latLng(cs[i]);
}
return result;
},
_toWaypoints: function(inputWaypoints, vias) {
var wps = [],
i,
viaLoc;
for (i = 0; i < vias.length; i++) {
viaLoc = vias[i].location;
wps.push(new Waypoint(L.latLng(viaLoc[1], viaLoc[0]),
inputWaypoints[i].name,
inputWaypoints[i].options));
}
return wps;
},
buildRouteUrl: function(waypoints, options) {
var locs = [],
hints = [],
wp,
latLng,
computeInstructions,
computeAlternative = true;
for (var i = 0; i < waypoints.length; i++) {
wp = waypoints[i];
latLng = wp.latLng;
locs.push(latLng.lng + ',' + latLng.lat);
hints.push(this._hints.locations[this._locationKey(latLng)] || '');
}
computeInstructions =
true;
return this.options.serviceUrl + '/' + this.options.profile + '/' +
locs.join(';') + '?' +
(options.geometryOnly ? (options.simplifyGeometry ? '' : 'overview=full') : 'overview=false') +
'&alternatives=' + computeAlternative.toString() +
'&steps=' + computeInstructions.toString() +
(this.options.useHints ? '&hints=' + hints.join(';') : '') +
(options.allowUTurns ? '&continue_straight=' + !options.allowUTurns : '');
},
_locationKey: function(location) {
return location.lat + ',' + location.lng;
},
_saveHintData: function(actualWaypoints, waypoints) {
var loc;
this._hints = {
locations: {}
};
for (var i = actualWaypoints.length - 1; i >= 0; i--) {
loc = waypoints[i].latLng;
this._hints.locations[this._locationKey(loc)] = actualWaypoints[i].hint;
}
},
});
})();

View File

@ -0,0 +1,321 @@
(function() {
'use strict';
var L = require('leaflet'),
corslite = require('corslite'),
polyline = require('polyline');
// Ignore camelcase naming for this file, since OSRM's API uses
// underscores.
/* jshint camelcase: false */
L.Routing = L.Routing || {};
L.extend(L.Routing, require('./L.Routing.Waypoint'));
/**
* This class works against OSRM version 4 and lower,
* for version 5, use the L.Routing.OSRMv1 class.
*/
L.Routing.OSRM = L.Class.extend({
options: {
serviceUrl: 'https://router.project-osrm.org/viaroute',
timeout: 30 * 1000,
routingOptions: {},
polylinePrecision: 6
},
initialize: function(options) {
L.Util.setOptions(this, options);
this._hints = {
locations: {}
};
},
route: function(waypoints, callback, context, options) {
var timedOut = false,
wps = [],
url,
timer,
wp,
i;
url = this.buildRouteUrl(waypoints, L.extend({}, this.options.routingOptions, options));
timer = setTimeout(function() {
timedOut = true;
callback.call(context || callback, {
status: -1,
message: 'OSRM request timed out.'
});
}, this.options.timeout);
// Create a copy of the waypoints, since they
// might otherwise be asynchronously modified while
// the request is being processed.
for (i = 0; i < waypoints.length; i++) {
wp = waypoints[i];
wps.push(new L.Routing.Waypoint(wp.latLng, wp.name, wp.options));
}
return corslite(url, L.bind(function(err, resp) {
var data,
errorMessage,
statusCode;
clearTimeout(timer);
if (!timedOut) {
errorMessage = 'HTTP request failed: ' + err;
statusCode = -1;
if (!err) {
try {
data = JSON.parse(resp.responseText);
try {
return this._routeDone(data, wps, callback, context);
} catch (ex) {
statusCode = -3;
errorMessage = ex.toString();
}
} catch (ex) {
statusCode = -2;
errorMessage = 'Error parsing OSRM response: ' + ex.toString();
}
}
callback.call(context || callback, {
status: statusCode,
message: errorMessage
});
}
}, this));
},
_routeDone: function(response, inputWaypoints, callback, context) {
var coordinates,
alts,
actualWaypoints,
i;
context = context || callback;
if (response.status !== 0 && response.status !== 200) {
callback.call(context, {
status: response.status,
message: response.status_message
});
return;
}
coordinates = this._decodePolyline(response.route_geometry);
actualWaypoints = this._toWaypoints(inputWaypoints, response.via_points);
alts = [{
name: this._createName(response.route_name),
coordinates: coordinates,
instructions: response.route_instructions ? this._convertInstructions(response.route_instructions) : [],
summary: response.route_summary ? this._convertSummary(response.route_summary) : [],
inputWaypoints: inputWaypoints,
waypoints: actualWaypoints,
waypointIndices: this._clampIndices(response.via_indices, coordinates)
}];
if (response.alternative_geometries) {
for (i = 0; i < response.alternative_geometries.length; i++) {
coordinates = this._decodePolyline(response.alternative_geometries[i]);
alts.push({
name: this._createName(response.alternative_names[i]),
coordinates: coordinates,
instructions: response.alternative_instructions[i] ? this._convertInstructions(response.alternative_instructions[i]) : [],
summary: response.alternative_summaries[i] ? this._convertSummary(response.alternative_summaries[i]) : [],
inputWaypoints: inputWaypoints,
waypoints: actualWaypoints,
waypointIndices: this._clampIndices(response.alternative_geometries.length === 1 ?
// Unsure if this is a bug in OSRM or not, but alternative_indices
// does not appear to be an array of arrays, at least not when there is
// a single alternative route.
response.alternative_indices : response.alternative_indices[i],
coordinates)
});
}
}
// only versions <4.5.0 will support this flag
if (response.hint_data) {
this._saveHintData(response.hint_data, inputWaypoints);
}
callback.call(context, null, alts);
},
_decodePolyline: function(routeGeometry) {
var cs = polyline.decode(routeGeometry, this.options.polylinePrecision),
result = new Array(cs.length),
i;
for (i = cs.length - 1; i >= 0; i--) {
result[i] = L.latLng(cs[i]);
}
return result;
},
_toWaypoints: function(inputWaypoints, vias) {
var wps = [],
i;
for (i = 0; i < vias.length; i++) {
wps.push(L.Routing.waypoint(L.latLng(vias[i]),
inputWaypoints[i].name,
inputWaypoints[i].options));
}
return wps;
},
_createName: function(nameParts) {
var name = '',
i;
for (i = 0; i < nameParts.length; i++) {
if (nameParts[i]) {
if (name) {
name += ', ';
}
name += nameParts[i].charAt(0).toUpperCase() + nameParts[i].slice(1);
}
}
return name;
},
buildRouteUrl: function(waypoints, options) {
var locs = [],
wp,
computeInstructions,
computeAlternative,
locationKey,
hint;
for (var i = 0; i < waypoints.length; i++) {
wp = waypoints[i];
locationKey = this._locationKey(wp.latLng);
locs.push('loc=' + locationKey);
hint = this._hints.locations[locationKey];
if (hint) {
locs.push('hint=' + hint);
}
if (wp.options && wp.options.allowUTurn) {
locs.push('u=true');
}
}
computeAlternative = computeInstructions =
!(options && options.geometryOnly);
return this.options.serviceUrl + '?' +
'instructions=' + computeInstructions.toString() + '&' +
'alt=' + computeAlternative.toString() + '&' +
(options.z ? 'z=' + options.z + '&' : '') +
locs.join('&') +
(this._hints.checksum !== undefined ? '&checksum=' + this._hints.checksum : '') +
(options.fileformat ? '&output=' + options.fileformat : '') +
(options.allowUTurns ? '&uturns=' + options.allowUTurns : '');
},
_locationKey: function(location) {
return location.lat + ',' + location.lng;
},
_saveHintData: function(hintData, waypoints) {
var loc;
this._hints = {
checksum: hintData.checksum,
locations: {}
};
for (var i = hintData.locations.length - 1; i >= 0; i--) {
loc = waypoints[i].latLng;
this._hints.locations[this._locationKey(loc)] = hintData.locations[i];
}
},
_convertSummary: function(osrmSummary) {
return {
totalDistance: osrmSummary.total_distance,
totalTime: osrmSummary.total_time
};
},
_convertInstructions: function(osrmInstructions) {
var result = [],
i,
instr,
type,
driveDir;
for (i = 0; i < osrmInstructions.length; i++) {
instr = osrmInstructions[i];
type = this._drivingDirectionType(instr[0]);
driveDir = instr[0].split('-');
if (type) {
result.push({
type: type,
distance: instr[2],
time: instr[4],
road: instr[1],
direction: instr[6],
exit: driveDir.length > 1 ? driveDir[1] : undefined,
index: instr[3]
});
}
}
return result;
},
_drivingDirectionType: function(d) {
switch (parseInt(d, 10)) {
case 1:
return 'Straight';
case 2:
return 'SlightRight';
case 3:
return 'Right';
case 4:
return 'SharpRight';
case 5:
return 'TurnAround';
case 6:
return 'SharpLeft';
case 7:
return 'Left';
case 8:
return 'SlightLeft';
case 9:
return 'WaypointReached';
case 10:
// TODO: "Head on"
// https://github.com/DennisOSRM/Project-OSRM/blob/master/DataStructures/TurnInstructions.h#L48
return 'Straight';
case 11:
case 12:
return 'Roundabout';
case 15:
return 'DestinationReached';
default:
return null;
}
},
_clampIndices: function(indices, coords) {
var maxCoordIndex = coords.length - 1,
i;
for (i = 0; i < indices.length; i++) {
indices[i] = Math.min(maxCoordIndex, Math.max(indices[i], 0));
}
return indices;
}
});
L.Routing.osrm = function(options) {
return new L.Routing.OSRM(options);
};
module.exports = L.Routing;
})();

View File

@ -0,0 +1,356 @@
(function() {
'use strict';
var L = require('leaflet');
var GeocoderElement = require('./geocoder-element');
var Waypoint = require('./waypoint');
module.exports = (L.Layer || L.Class).extend({
includes: L.Mixin.Events,
options: {
dragStyles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'red', opacity: 1, weight: 2, dashArray: '7,12'}
],
draggableWaypoints: true,
routeWhileDragging: false,
addWaypoints: true,
reverseWaypoints: false,
addButtonClassName: '',
language: 'en',
createGeocoderElement: function(wp, i, nWps, plan) {
return new GeocoderElement(wp, i, nWps, plan);
},
createMarker: function(i, wp) {
var options = {
draggable: this.draggableWaypoints
},
marker = L.marker(wp.latLng, options);
return marker;
},
geocodersClassName: ''
},
initialize: function(waypoints, options) {
L.Util.setOptions(this, options);
this._waypoints = [];
this.setWaypoints(waypoints);
},
isReady: function() {
var i;
for (i = 0; i < this._waypoints.length; i++) {
if (!this._waypoints[i].latLng) {
return false;
}
}
return true;
},
getWaypoints: function() {
var i,
wps = [];
for (i = 0; i < this._waypoints.length; i++) {
wps.push(this._waypoints[i]);
}
return wps;
},
setWaypoints: function(waypoints) {
var args = [0, this._waypoints.length].concat(waypoints);
this.spliceWaypoints.apply(this, args);
return this;
},
spliceWaypoints: function() {
var args = [arguments[0], arguments[1]],
i;
for (i = 2; i < arguments.length; i++) {
args.push(arguments[i] && arguments[i].hasOwnProperty('latLng') ? arguments[i] : new Waypoint(arguments[i]));
}
[].splice.apply(this._waypoints, args);
// Make sure there's always at least two waypoints
while (this._waypoints.length < 2) {
this.spliceWaypoints(this._waypoints.length, 0, null);
}
this._updateMarkers();
this._fireChanged.apply(this, args);
},
onAdd: function(map) {
this._map = map;
this._updateMarkers();
},
onRemove: function() {
var i;
this._removeMarkers();
if (this._newWp) {
for (i = 0; i < this._newWp.lines.length; i++) {
this._map.removeLayer(this._newWp.lines[i]);
}
}
delete this._map;
},
createGeocoders: function() {
var container = L.DomUtil.create('div', 'leaflet-routing-geocoders ' + this.options.geocodersClassName),
waypoints = this._waypoints,
addWpBtn,
reverseBtn;
this._geocoderContainer = container;
this._geocoderElems = [];
if (this.options.addWaypoints) {
addWpBtn = L.DomUtil.create('button', 'leaflet-routing-add-waypoint ' + this.options.addButtonClassName, container);
addWpBtn.setAttribute('type', 'button');
L.DomEvent.addListener(addWpBtn, 'click', function() {
this.spliceWaypoints(waypoints.length, 0, null);
}, this);
}
if (this.options.reverseWaypoints) {
reverseBtn = L.DomUtil.create('button', 'leaflet-routing-reverse-waypoints', container);
reverseBtn.setAttribute('type', 'button');
L.DomEvent.addListener(reverseBtn, 'click', function() {
this._waypoints.reverse();
this.setWaypoints(this._waypoints);
}, this);
}
this._updateGeocoders();
this.on('waypointsspliced', this._updateGeocoders);
return container;
},
_createGeocoder: function(i) {
var geocoder = this.options.createGeocoderElement(this._waypoints[i], i, this._waypoints.length, this.options);
geocoder
.on('delete', function() {
if (i > 0 || this._waypoints.length > 2) {
this.spliceWaypoints(i, 1);
} else {
this.spliceWaypoints(i, 1, new Waypoint());
}
}, this)
.on('geocoded', function(e) {
this._updateMarkers();
this._fireChanged();
this._focusGeocoder(i + 1);
this.fire('waypointgeocoded', {
waypointIndex: i,
waypoint: e.waypoint
});
}, this)
.on('reversegeocoded', function(e) {
this.fire('waypointgeocoded', {
waypointIndex: i,
waypoint: e.waypoint
});
}, this);
return geocoder;
},
_updateGeocoders: function() {
var elems = [],
i,
geocoderElem;
for (i = 0; i < this._geocoderElems.length; i++) {
this._geocoderContainer.removeChild(this._geocoderElems[i].getContainer());
}
for (i = this._waypoints.length - 1; i >= 0; i--) {
geocoderElem = this._createGeocoder(i);
this._geocoderContainer.insertBefore(geocoderElem.getContainer(), this._geocoderContainer.firstChild);
elems.push(geocoderElem);
}
this._geocoderElems = elems.reverse();
},
_removeMarkers: function() {
var i;
if (this._markers) {
for (i = 0; i < this._markers.length; i++) {
if (this._markers[i]) {
this._map.removeLayer(this._markers[i]);
}
}
}
this._markers = [];
},
_updateMarkers: function() {
var i,
m;
if (!this._map) {
return;
}
this._removeMarkers();
for (i = 0; i < this._waypoints.length; i++) {
if (this._waypoints[i].latLng) {
m = this.options.createMarker(i, this._waypoints[i], this._waypoints.length);
if (m) {
m.addTo(this._map);
if (this.options.draggableWaypoints) {
this._hookWaypointEvents(m, i);
}
}
} else {
m = null;
}
this._markers.push(m);
}
},
_fireChanged: function() {
this.fire('waypointschanged', {waypoints: this.getWaypoints()});
if (arguments.length >= 2) {
this.fire('waypointsspliced', {
index: Array.prototype.shift.call(arguments),
nRemoved: Array.prototype.shift.call(arguments),
added: arguments
});
}
},
_hookWaypointEvents: function(m, i, trackMouseMove) {
var eventLatLng = function(e) {
return trackMouseMove ? e.latlng : e.target.getLatLng();
},
dragStart = L.bind(function(e) {
this.fire('waypointdragstart', {index: i, latlng: eventLatLng(e)});
}, this),
drag = L.bind(function(e) {
this._waypoints[i].latLng = eventLatLng(e);
this.fire('waypointdrag', {index: i, latlng: eventLatLng(e)});
}, this),
dragEnd = L.bind(function(e) {
this._waypoints[i].latLng = eventLatLng(e);
this._waypoints[i].name = '';
if (this._geocoderElems) {
this._geocoderElems[i].update(true);
}
this.fire('waypointdragend', {index: i, latlng: eventLatLng(e)});
this._fireChanged();
}, this),
mouseMove,
mouseUp;
if (trackMouseMove) {
mouseMove = L.bind(function(e) {
this._markers[i].setLatLng(e.latlng);
drag(e);
}, this);
mouseUp = L.bind(function(e) {
this._map.dragging.enable();
this._map.off('mouseup', mouseUp);
this._map.off('mousemove', mouseMove);
dragEnd(e);
}, this);
this._map.dragging.disable();
this._map.on('mousemove', mouseMove);
this._map.on('mouseup', mouseUp);
dragStart({latlng: this._waypoints[i].latLng});
} else {
m.on('dragstart', dragStart);
m.on('drag', drag);
m.on('dragend', dragEnd);
}
},
dragNewWaypoint: function(e) {
var newWpIndex = e.afterIndex + 1;
if (this.options.routeWhileDragging) {
this.spliceWaypoints(newWpIndex, 0, e.latlng);
this._hookWaypointEvents(this._markers[newWpIndex], newWpIndex, true);
} else {
this._dragNewWaypoint(newWpIndex, e.latlng);
}
},
_dragNewWaypoint: function(newWpIndex, initialLatLng) {
var wp = new Waypoint(initialLatLng),
prevWp = this._waypoints[newWpIndex - 1],
nextWp = this._waypoints[newWpIndex],
marker = this.options.createMarker(newWpIndex, wp, this._waypoints.length + 1),
lines = [],
draggingEnabled = this._map.dragging.enabled(),
mouseMove = L.bind(function(e) {
var i,
latLngs;
if (marker) {
marker.setLatLng(e.latlng);
}
for (i = 0; i < lines.length; i++) {
latLngs = lines[i].getLatLngs();
latLngs.splice(1, 1, e.latlng);
lines[i].setLatLngs(latLngs);
}
L.DomEvent.stop(e);
}, this),
mouseUp = L.bind(function(e) {
var i;
if (marker) {
this._map.removeLayer(marker);
}
for (i = 0; i < lines.length; i++) {
this._map.removeLayer(lines[i]);
}
this._map.off('mousemove', mouseMove);
this._map.off('mouseup', mouseUp);
this.spliceWaypoints(newWpIndex, 0, e.latlng);
if (draggingEnabled) {
this._map.dragging.enable();
}
}, this),
i;
if (marker) {
marker.addTo(this._map);
}
for (i = 0; i < this.options.dragStyles.length; i++) {
lines.push(L.polyline([prevWp.latLng, initialLatLng, nextWp.latLng],
this.options.dragStyles[i]).addTo(this._map));
}
if (draggingEnabled) {
this._map.dragging.disable();
}
this._map.on('mousemove', mouseMove);
this._map.on('mouseup', mouseUp);
},
_focusGeocoder: function(i) {
if (this._geocoderElems[i]) {
this._geocoderElems[i].focus();
} else {
document.activeElement.blur();
}
}
});
})();

View File

@ -0,0 +1,16 @@
(function() {
'use strict';
var L = require('leaflet');
module.exports = L.Class.extend({
options: {
allowUTurn: false,
},
initialize: function(latLng, name, options) {
L.Util.setOptions(this, options);
this.latLng = L.latLng(latLng);
this.name = name;
}
});
})();

View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Jasmine Spec Runner v2.0.0</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>
<!-- include source files here... -->
<script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>
<script src="../dist/leaflet-routing-machine.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/formatter-spec.js"></script>
<script type="text/javascript" src="spec/osrmv1-spec.js"></script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,76 @@
describe('L.Routing.Formatter', function() {
describe('._round', function() {
it('rounds 0 < x < 5 to multiple of 0.5', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(1)).toBe(1);
expect(p._round(1.4)).toBe(1.5);
expect(p._round(1.75)).toBe(2);
});
it('rounds 5 < x < 10 to nearest integer', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(7)).toBe(7);
expect(p._round(9.4)).toBe(9);
expect(p._round(9.8)).toBe(10);
});
it('rounds 10 < x < 50 values to multiples of 5', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(11.5)).toBe(10);
expect(p._round(14)).toBe(15);
expect(p._round(42)).toBe(40);
expect(p._round(43)).toBe(45);
});
it('rounds 50 < x < 100 to multiples of 10', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(72)).toBe(70);
expect(p._round(76)).toBe(80);
expect(p._round(97.6)).toBe(100);
});
it('rounds 100 < x < 150 to multiples of 50', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(105)).toBe(100);
expect(p._round(125)).toBe(150);
});
it('rounds large values to multiples of 100', function() {
var p = new L.Routing.Formatter(undefined);
expect(p._round(686)).toBe(700);
expect(p._round(860)).toBe(900);
});
it('considers rounding sensitivity', function() {
var p = new L.Routing.Formatter({roundingSensitivity: 5});
expect(p._round(24)).toBe(24);
expect(p._round(52)).toBe(50);
});
});
describe('.formatDistance', function() {
it('rounds long distances reasonably', function() {
var p = new L.Routing.Formatter({
distanceTemplate: '{value}'
});
expect(parseInt(p.formatDistance(22000), 10)).toBe(20);
expect(parseInt(p.formatDistance(24000), 10)).toBe(25);
expect(parseInt(p.formatDistance(86000), 10)).toBe(90);
});
it('formats imperial units properly', function() {
var p = new L.Routing.Formatter({
distanceTemplate: '{value}',
units: 'imperial'
});
expect(parseInt(p.formatDistance(800), 10)).toBe(900);
expect(parseInt(p.formatDistance(22000), 10)).toBe(15);
expect(parseInt(p.formatDistance(24500), 10)).toBe(15);
expect(parseInt(p.formatDistance(86000), 10)).toBe(55);
});
});
describe('.formatTime', function() {
it('rounds whole minutes without seconds', function() {
var p = new L.Routing.Formatter();
expect(p.formatTime(240)).toBe('4 min');
})
it('rounds just under five minutes to five minutes without seconds', function() {
var p = new L.Routing.Formatter();
expect(p.formatTime(299.10000000005)).toBe('5 min');
})
});
});

View File

@ -0,0 +1,28 @@
describe('L.Routing.OSRMv1', function() {
describe('#route', function() {
var waypoints = [
new L.Routing.Waypoint([57.73, 11.94]),
new L.Routing.Waypoint([57.7, 11.9])
];
it('returns correct waypoints', function(done) {
var router = new L.Routing.OSRMv1();
router.route(waypoints, function(err, routes) {
if (err) {
return done(err);
}
if (!routes.length) {
return done('No routes :(');
}
waypoints.forEach(function(wp, i) {
var returnedWp = routes[0].waypoints[i];
expect(Math.abs(returnedWp.latLng.lat - wp.latLng.lat)).to.be.lessThan(0.1);
expect(Math.abs(returnedWp.latLng.lng - wp.latLng.lng)).to.be.lessThan(0.1);
});
done();
});
});
});
});

View File

@ -1,94 +1,14 @@
{% extends 'base.jinja' %}
{% block head_extra_first %}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css"
integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
crossorigin=""/>
<link rel="stylesheet" href="{{ static('libs/leaflet/leaflet.css') }}"/>
<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
crossorigin="">
</script>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<link rel="stylesheet" href="leaflet-routing-machine.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="leaflet-routing-machine.js"></script>
<script>
var endLat = '';
var endLon = '';
var startLat = '';
var startLon = '';
document.addEventListener('DOMContentLoaded', loadData);
document.addEventListener('DOMContentLoaded', resizeMap);
document.addEventListener('DOMContentLoaded', getPos);
document.getElementById("navigate").onclick = makeRoute();
window.onresize = resizeMap;
function loadData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
generateMap(JSON.parse(this.response))
}
};
xhttp.open("GET", "https://nominatim.openstreetmap.org/search/?format=json&city=Bamberg&street={{ room.address }}", true);
xhttp.send();
}
function generateMap(streets) {
if (streets.length > 0) {
endLon = streets[0]['lon'];
endLat = streets[0]['lat'];
var map = L.map('map').setView([endLat, endLon], 16);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker([endLat, endLon]).addTo(map)
.bindPopup('{{ room.short }} </br> {{ room.address }}')
.openPopup();
}
}
function resizeMap() {
var height = window.innerHeight
document.getElementById('map').style.height = height + 'px'
}
function getPos() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
console.log(position);
startLat = position.coords.latitude;
startLon = position.coords.longitude;
}, function (err) {
console.log(err.code)
console.log(err.message)
})
} else {
document.getElementById('map').innerHTML('Geolocation not available')
}
}
function makeRoute() {
L.Routing.control({
waypoints: [
L.latLng(startLat, startLon),
L.latLng(endLat, endLon)
]
}).addTo(map);
}
</script>
<script src="{{ static('libs/leaflet/leaflet.js') }}"></script>
<script id="nav_data" src="{{ static('js/donar/navigate.js') }}" data-address="{{ room.address }}"
data-short="{{ room.short }}"></script>
{% endblock %}
{% block headline %}
<h1 class="text-center"> Donar </h1>
{% endblock %}
{% block content %}
{# TODO: implement Leaflet #}
<div id="kart"></div>
<div id="map"></div>
<button class="button" type="button" id="navigate">Route</button>
{% endblock %}