You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.6 KiB
JavaScript
170 lines
4.6 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
var L = require('leaflet');
|
|
|
|
L.Routing = L.Routing || {};
|
|
|
|
L.Routing.OpenRouteService = L.Evented.extend({
|
|
options: {
|
|
serviceUrl: 'https://api.openrouteservice.org/v2/directions',
|
|
profile: 'cycling-regular',
|
|
orsOptions: {}, // see https://openrouteservice.org/dev/#/api-docs/v2/directions/{profile}/post
|
|
},
|
|
|
|
initialize: function(apiKey, options) {
|
|
this._apiKey = apiKey;
|
|
L.Util.setOptions(this, options);
|
|
},
|
|
|
|
route: async function(waypoints, callback, context, options) {
|
|
options = options || {}
|
|
const url = `${this.options.serviceUrl}/${this.options.profile}/json`
|
|
const instructions = !options.geometryOnly
|
|
const coordinates = waypoints.map(wp => [wp.latLng.lng, wp.latLng.lat])
|
|
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'authorization': this._apiKey,
|
|
'content-type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
instructions,
|
|
coordinates,
|
|
maneuvers: true,
|
|
geometry_simplify: options.simplifyGeometry,
|
|
...this.options.orsOptions,
|
|
}),
|
|
})
|
|
const ratelimit = {
|
|
limit: Number(res.headers.get('X-RateLimit-Limit')),
|
|
remaining: Number(res.headers.get('X-RateLimit-Remaining')),
|
|
reset: Number(res.headers.get('X-RateLimit-Reset')),
|
|
}
|
|
const parsed = await res.json()
|
|
const payload = res.ok
|
|
? [null, this.toIRoute(parsed, waypoints)]
|
|
: [{ status: res.statusText, message: parsed.error }, null]
|
|
|
|
callback.call(context || callback, ...payload)
|
|
return this;
|
|
},
|
|
|
|
toIRoute: function(orsResponse, inputWaypoints) {
|
|
const routes = []
|
|
for (const r of orsResponse.routes) {
|
|
const coordinates = this.decodePolyline(r.geometry)
|
|
const steps = [].concat(...r.segments.map(s => s.steps))
|
|
const instructions = steps.map(s => ({
|
|
distance: s.distance,
|
|
time: s.duration,
|
|
text: s.instruction,
|
|
road: s.name,
|
|
type: this.instructionType(s.type),
|
|
direction: s.maneuver ? this.direction(s.maneuver.bearing_after) : undefined,
|
|
}))
|
|
|
|
routes.push({
|
|
name: '',
|
|
coordinates,
|
|
instructions,
|
|
summary: {
|
|
totalDistance: r.summary.distance,
|
|
totalTime: r.summary.duration,
|
|
},
|
|
inputWaypoints,
|
|
actualWaypoints: r.way_points.map(i => ({ latLng: coordinates[i] })),
|
|
waypointIndices: r.way_points,
|
|
})
|
|
}
|
|
return routes
|
|
},
|
|
|
|
direction: bearing => {
|
|
if (bearing === undefined) return undefined
|
|
const labels = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
|
|
const fac = 360/labels.length
|
|
const i = Math.floor((bearing + fac/2) % 360 / fac)
|
|
return labels[i]
|
|
},
|
|
|
|
instructionType: i => {
|
|
switch (i) {
|
|
case 0: return 'Left'
|
|
case 1: return 'Right'
|
|
case 2: return 'SharpLeft'
|
|
case 3: return 'SharpRight'
|
|
case 4: return 'SlightLeft'
|
|
case 5: return 'SlightRight'
|
|
case 6: return 'Straight'
|
|
case 7: return 'Roundabout' // enter
|
|
case 8: return 'Roundabout' // exit
|
|
case 9: return 'TurnAround'
|
|
case 10: return 'DestinationReached'
|
|
case 11: return 'StartAt'
|
|
case 12: return 'Left'
|
|
case 13: return 'Right'
|
|
default: return undefined
|
|
}
|
|
},
|
|
|
|
decodePolyline: (encodedPolyline, includeElevation) => {
|
|
// array that holds the points as [lat,lng,alt?]
|
|
let points = []
|
|
let index = 0
|
|
const len = encodedPolyline.length
|
|
let lat = 0
|
|
let lng = 0
|
|
let ele = 0
|
|
while (index < len) {
|
|
let b
|
|
let shift = 0
|
|
let result = 0
|
|
do {
|
|
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63 // finds ascii
|
|
// and subtract it by 63
|
|
result |= (b & 0x1f) << shift
|
|
shift += 5
|
|
} while (b >= 0x20)
|
|
|
|
lat += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
|
|
shift = 0
|
|
result = 0
|
|
do {
|
|
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63
|
|
result |= (b & 0x1f) << shift
|
|
shift += 5
|
|
} while (b >= 0x20)
|
|
lng += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
|
|
|
|
if (includeElevation) {
|
|
shift = 0
|
|
result = 0
|
|
do {
|
|
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63
|
|
result |= (b & 0x1f) << shift
|
|
shift += 5
|
|
} while (b >= 0x20)
|
|
ele += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
|
|
}
|
|
try {
|
|
let location = [(lat / 1E5), (lng / 1E5)]
|
|
if (includeElevation) location.push((ele / 100))
|
|
points.push(L.latLng(location))
|
|
} catch (e) {
|
|
console.error(e)
|
|
}
|
|
}
|
|
return points
|
|
},
|
|
|
|
});
|
|
|
|
L.Routing.openRouteService = function(apiKey, options) {
|
|
return new L.Routing.OpenRouteService(apiKey, options);
|
|
};
|
|
|
|
module.exports = L.Routing.OpenRouteService;
|
|
})();
|