@furkot/directions 2.1.2 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +1 -1
- package/lib/directions.js +36 -32
- package/lib/model.js +20 -16
- package/lib/profile/rv/index.js +11 -12
- package/lib/service/graphhopper/index.js +34 -22
- package/lib/service/index.js +4 -5
- package/lib/service/mapquest/index.js +25 -15
- package/lib/service/openroute/index.js +47 -38
- package/lib/service/osrm/index.js +7 -17
- package/lib/service/partition.js +1 -1
- package/lib/service/simplify.js +7 -5
- package/lib/service/util.js +20 -22
- package/lib/service/valhalla/index.js +35 -36
- package/package.json +8 -8
package/Readme.md
CHANGED
|
@@ -28,7 +28,7 @@ MIT © [Damian Krzeminski](https://pirxpilot.me)
|
|
|
28
28
|
[npm-url]: https://npmjs.org/package/@furkot/directions
|
|
29
29
|
|
|
30
30
|
[build-url]: https://github.com/furkot/directions/actions/workflows/check.yaml
|
|
31
|
-
[build-image]: https://img.shields.io/github/workflow/status/furkot/directions/check
|
|
31
|
+
[build-image]: https://img.shields.io/github/actions/workflow/status/furkot/directions/check.yaml?branch=main
|
|
32
32
|
|
|
33
33
|
[deps-image]: https://img.shields.io/librariesio/release/npm/@furkot/directions
|
|
34
34
|
[deps-url]: https://libraries.io/npm/@furkot%2Fdirections
|
package/lib/directions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const travelMode = require('./model').travelMode;
|
|
2
|
-
const { defaults
|
|
2
|
+
const { defaults, withTimeout } = require('./service/util');
|
|
3
3
|
const prepareQuery = require('./profile');
|
|
4
4
|
|
|
5
5
|
module.exports = furkotDirections;
|
|
@@ -42,40 +42,41 @@ const services = {
|
|
|
42
42
|
const defaultTimeout = 20 * 1000;
|
|
43
43
|
|
|
44
44
|
function furkotDirections(options) {
|
|
45
|
-
|
|
46
45
|
options = {
|
|
47
46
|
timeout: defaultTimeout,
|
|
48
47
|
order: ['osrm', 'mapquest', 'valhalla', 'graphhopper', 'openroute'],
|
|
49
48
|
...options
|
|
50
49
|
};
|
|
51
50
|
if (!options.services) {
|
|
52
|
-
options.services = options.order
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
limiter: options[`${name}_limiter`],
|
|
65
|
-
enable,
|
|
66
|
-
skip: service.skip
|
|
67
|
-
};
|
|
68
|
-
if (options[name]) {
|
|
69
|
-
Object.keys(options).reduce(mapOptions, {
|
|
70
|
-
options,
|
|
51
|
+
options.services = options.order
|
|
52
|
+
.map(name => {
|
|
53
|
+
const service = services[options[name] || name];
|
|
54
|
+
if (!service) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const enable = options[`${name}_enable`];
|
|
58
|
+
if (!enable) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// object representing actual parameters for a service
|
|
62
|
+
const serviceOptions = {
|
|
71
63
|
name,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
limiter: options[`${name}_limiter`],
|
|
65
|
+
enable,
|
|
66
|
+
skip: service.skip
|
|
67
|
+
};
|
|
68
|
+
if (options[name]) {
|
|
69
|
+
Object.keys(options).reduce(mapOptions, {
|
|
70
|
+
options,
|
|
71
|
+
name,
|
|
72
|
+
optName: options[name],
|
|
73
|
+
serviceOptions
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// we are adding options that has not been copied to serviceOptions yet
|
|
77
|
+
return service.service(defaults(serviceOptions, options));
|
|
78
|
+
})
|
|
79
|
+
.filter(Boolean);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
directions.options = options;
|
|
@@ -112,10 +113,13 @@ function furkotDirections(options) {
|
|
|
112
113
|
break;
|
|
113
114
|
}
|
|
114
115
|
if (query.points.length > 2) {
|
|
115
|
-
return requestDirections(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
return requestDirections(
|
|
117
|
+
{
|
|
118
|
+
...query,
|
|
119
|
+
points: query.points.slice(0, 2)
|
|
120
|
+
},
|
|
121
|
+
timeout
|
|
122
|
+
);
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
return {
|
package/lib/model.js
CHANGED
|
@@ -22,9 +22,7 @@ const directionsQuery = {
|
|
|
22
22
|
avoidHighways: false, // true to avoid highways
|
|
23
23
|
avoidTolls: false, // true to avoid toll roads
|
|
24
24
|
units: 'm', // m - miles, km - kilometers
|
|
25
|
-
points: [
|
|
26
|
-
[0, 0]
|
|
27
|
-
], // array of consecutive series of points; each point is [lon, lat]
|
|
25
|
+
points: [[0, 0]], // array of consecutive series of points; each point is [lon, lat]
|
|
28
26
|
isInChina: false, // points are in China (some services need this information)
|
|
29
27
|
begin: '', // date/time for the begin of route as 'YYYY-MM-DDThh:mm'
|
|
30
28
|
turnbyturn: false, // provide detailed turn-by-turn instructions (segments in directionsResult)
|
|
@@ -39,19 +37,25 @@ const directionsResult = {
|
|
|
39
37
|
query: directionsQuery, // query parameters
|
|
40
38
|
places: [], // addresses or place names corresponding to points (if directions service performs reverse geocoding)
|
|
41
39
|
name: '', // human-readable name of directions (if available)
|
|
42
|
-
routes: [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
routes: [
|
|
41
|
+
{
|
|
42
|
+
// routes; one for each point with a successor in the query.points
|
|
43
|
+
duration: 0, // route duration in seconds
|
|
44
|
+
distance: 0, // route distance in meters
|
|
45
|
+
path: [], // simplified series of interim points; each point is [lon, lat]
|
|
46
|
+
seasonal: false, // indicates a road that is seasonally closed
|
|
47
|
+
segmentIndex: 0 // index of the turn-by-turn directions segment
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
segments: [
|
|
51
|
+
{
|
|
52
|
+
// turn-by-turn directions
|
|
53
|
+
duration: 0, // segment duration in seconds
|
|
54
|
+
distance: 0, // segment distance in meters
|
|
55
|
+
path: [], // series of interim points; each point is [lon, lat]
|
|
56
|
+
instructions: '' // textual instructions for this segment
|
|
57
|
+
}
|
|
58
|
+
],
|
|
55
59
|
stats: [], // list of providers that requests have been sent to to obtain directions
|
|
56
60
|
provider: '' // identifies service providing the directions
|
|
57
61
|
};
|
package/lib/profile/rv/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { passengerCar, defaultRV } = require(
|
|
1
|
+
const { passengerCar, defaultRV } = require('./dimensions');
|
|
2
2
|
const passengerCarEntries = Object.entries(passengerCar);
|
|
3
3
|
|
|
4
4
|
module.exports = prepareQuery;
|
|
@@ -20,9 +20,7 @@ function isPassengerCar(vehicle) {
|
|
|
20
20
|
* @returns {boolean} Whether to treat RV as non commercial truck.
|
|
21
21
|
*/
|
|
22
22
|
function isNonCommercialTruck(vehicle) {
|
|
23
|
-
return !vehicle.hazmat &&
|
|
24
|
-
vehicle.length < defaultRV.length &&
|
|
25
|
-
vehicle.weight < defaultRV.weight;
|
|
23
|
+
return !vehicle.hazmat && vehicle.length < defaultRV.length && vehicle.weight < defaultRV.weight;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
@@ -61,12 +59,13 @@ function prepareVehicle(vehicle) {
|
|
|
61
59
|
* @returns {Object} The prepared query.
|
|
62
60
|
*/
|
|
63
61
|
function prepareQuery(query) {
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
const { mode: initialMode, vehicle: initialVehicle } = query;
|
|
63
|
+
return Object.assign(
|
|
64
|
+
{
|
|
65
|
+
initialMode,
|
|
66
|
+
initialVehicle
|
|
67
|
+
},
|
|
68
|
+
query,
|
|
69
|
+
prepareVehicle(query.vehicle)
|
|
70
|
+
);
|
|
72
71
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// https://docs.graphhopper.com/#tag/Routing-API
|
|
2
2
|
// https://github.com/boldtrn/kurviger-api-documentation
|
|
3
3
|
|
|
4
|
-
const { pathType, travelMode } = require(
|
|
4
|
+
const { pathType, travelMode } = require('../../model');
|
|
5
5
|
const status = require('../status');
|
|
6
6
|
const tagRoute = require('../tag-route');
|
|
7
7
|
const util = require('../util');
|
|
@@ -39,7 +39,10 @@ const tollTypes = {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
function extractSegment(result, { distance, interval, text, time }) {
|
|
42
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
directions: { segments },
|
|
44
|
+
path
|
|
45
|
+
} = result;
|
|
43
46
|
const [from, to] = interval;
|
|
44
47
|
segments.push({
|
|
45
48
|
duration: Math.round((time || 0) / 1000),
|
|
@@ -63,7 +66,10 @@ function setTolls(seg) {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function extractDirections(result, { details, distance, instructions, points, time }) {
|
|
66
|
-
const {
|
|
69
|
+
const {
|
|
70
|
+
directions: { routes, segments },
|
|
71
|
+
fullPath
|
|
72
|
+
} = result;
|
|
67
73
|
result.path = util.decode(points);
|
|
68
74
|
const route = {
|
|
69
75
|
duration: Math.round((time || 0) / 1000),
|
|
@@ -110,7 +116,7 @@ function extractDirections(result, { details, distance, instructions, points, ti
|
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
function getStatus(st, response) {
|
|
113
|
-
st = st
|
|
119
|
+
st = st?.status;
|
|
114
120
|
if (!(st || response)) {
|
|
115
121
|
return;
|
|
116
122
|
}
|
|
@@ -157,18 +163,22 @@ function vehicleSize(query, options) {
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
function init(options) {
|
|
160
|
-
|
|
161
166
|
function prepareUrl(url) {
|
|
162
167
|
return `${url}?key=${options.graphhopper_key}`;
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
function prepareRequest(query) {
|
|
166
171
|
const { avoidFerry, avoidHighways, avoidTolls, avoidUnpaved, curvy, mode, path, points, turnbyturn } = query;
|
|
167
|
-
if (
|
|
168
|
-
|
|
172
|
+
if (
|
|
173
|
+
options.parameters.max_curvy_distance &&
|
|
174
|
+
curvy &&
|
|
175
|
+
mode === -1 &&
|
|
176
|
+
points.length === 2 &&
|
|
177
|
+
util.distance(points[0], points[1]) > options.parameters.max_curvy_distance
|
|
178
|
+
) {
|
|
169
179
|
return;
|
|
170
180
|
}
|
|
171
|
-
|
|
181
|
+
const req = {
|
|
172
182
|
vehicle: vehicleType(query),
|
|
173
183
|
points,
|
|
174
184
|
details: ['road_environment', 'toll']
|
|
@@ -194,21 +204,24 @@ function init(options) {
|
|
|
194
204
|
if (avoidUnpaved) {
|
|
195
205
|
req.avoid_unpaved_roads = true;
|
|
196
206
|
}
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
207
|
+
} else {
|
|
199
208
|
req.details.push('surface');
|
|
200
209
|
req['ch.disable'] = true;
|
|
201
210
|
req.custom_model = {
|
|
202
|
-
priority: [
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
priority: [
|
|
212
|
+
{
|
|
213
|
+
if: 'road_class == MOTORWAY',
|
|
214
|
+
multiply_by: avoidHighways ? '0.1' : '1.0'
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
if: 'toll == ALL',
|
|
218
|
+
multiply_by: avoidTolls ? '0.1' : '1.0'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
if: 'road_environment == FERRY',
|
|
222
|
+
multiply_by: avoidFerry ? '0.1' : '1.0'
|
|
223
|
+
}
|
|
224
|
+
]
|
|
212
225
|
};
|
|
213
226
|
if (avoidUnpaved) {
|
|
214
227
|
req.custom_model.priority.push({
|
|
@@ -223,7 +236,6 @@ function init(options) {
|
|
|
223
236
|
}
|
|
224
237
|
|
|
225
238
|
function processResponse(response, query) {
|
|
226
|
-
|
|
227
239
|
if (response && response.status >= 400) {
|
|
228
240
|
// let it cascade to the next service
|
|
229
241
|
return;
|
|
@@ -233,7 +245,7 @@ function init(options) {
|
|
|
233
245
|
query,
|
|
234
246
|
provider: options.name
|
|
235
247
|
};
|
|
236
|
-
const paths = response
|
|
248
|
+
const paths = response?.paths;
|
|
237
249
|
if (paths) {
|
|
238
250
|
directions.routes = [];
|
|
239
251
|
if (query.turnbyturn || query.path === pathType.smooth || query.path === pathType.coarse) {
|
package/lib/service/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const fetchagent = require('fetchagent');
|
|
2
|
-
const { pathType } = require(
|
|
2
|
+
const { pathType } = require('../model');
|
|
3
3
|
const makeLimiter = require('limiter-component');
|
|
4
4
|
const status = require('./status');
|
|
5
5
|
const makeSimplify = require('./simplify');
|
|
@@ -89,15 +89,14 @@ function init(options) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
async function request(url, req) {
|
|
92
|
-
const options = this;
|
|
93
92
|
let fa = fetchagent;
|
|
94
|
-
if (
|
|
93
|
+
if (this.post) {
|
|
95
94
|
fa = fa.post(url).send(req);
|
|
96
95
|
} else {
|
|
97
96
|
fa = fa.get(url).query(req);
|
|
98
97
|
}
|
|
99
|
-
if (
|
|
100
|
-
fa.set('authorization',
|
|
98
|
+
if (this.authorization) {
|
|
99
|
+
fa.set('authorization', this.authorization);
|
|
101
100
|
}
|
|
102
101
|
const res = await fa.set('accept', 'application/json').end();
|
|
103
102
|
let status;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { pathType } = require(
|
|
1
|
+
const { pathType } = require('../../model');
|
|
2
2
|
const status = require('../status');
|
|
3
3
|
const util = require('../util');
|
|
4
4
|
|
|
@@ -39,7 +39,12 @@ function addPoint(result, p, i, points) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function extractSegment(result, { distance, index, narrative, time }) {
|
|
42
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
directions: { segments },
|
|
44
|
+
maneuverIndexes: indexes,
|
|
45
|
+
path,
|
|
46
|
+
unitMultiplier
|
|
47
|
+
} = result;
|
|
43
48
|
segments.push({
|
|
44
49
|
duration: time,
|
|
45
50
|
distance: Math.round((distance || 0) * unitMultiplier),
|
|
@@ -50,7 +55,11 @@ function extractSegment(result, { distance, index, narrative, time }) {
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
function extractDirections(result, leg) {
|
|
53
|
-
const {
|
|
58
|
+
const {
|
|
59
|
+
directions: { routes, segments },
|
|
60
|
+
legIndexes: indexes,
|
|
61
|
+
path
|
|
62
|
+
} = result;
|
|
54
63
|
const route = {
|
|
55
64
|
duration: leg.time || 0,
|
|
56
65
|
distance: Math.round((leg.distance || 0) * result.unitMultiplier)
|
|
@@ -71,12 +80,12 @@ function extractDirections(result, leg) {
|
|
|
71
80
|
return result;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
function getStatus(
|
|
83
|
+
function getStatus(_err, response) {
|
|
75
84
|
if (!response) {
|
|
76
85
|
return;
|
|
77
86
|
}
|
|
78
87
|
const { info, route } = response;
|
|
79
|
-
const st = info
|
|
88
|
+
const st = info?.statuscode;
|
|
80
89
|
if (st === 403 || st === 500) {
|
|
81
90
|
// assume its because we exceeded the limit
|
|
82
91
|
return status.failure;
|
|
@@ -94,7 +103,6 @@ function getStatus(err, response) {
|
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
function init(options) {
|
|
97
|
-
|
|
98
106
|
function getProvider(query) {
|
|
99
107
|
if (query.alternate) {
|
|
100
108
|
return options.name;
|
|
@@ -103,8 +111,11 @@ function init(options) {
|
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
function getUrl(query) {
|
|
106
|
-
return
|
|
107
|
-
(query
|
|
114
|
+
return (
|
|
115
|
+
options[getProvider(query) + '_url'] +
|
|
116
|
+
'/directions/v2/' +
|
|
117
|
+
(query.alternate && query.points.length <= 2 ? 'alternateroutes' : 'route')
|
|
118
|
+
);
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
function prepareRequest(query) {
|
|
@@ -146,8 +157,7 @@ function init(options) {
|
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
function processResponse(response, query) {
|
|
149
|
-
|
|
150
|
-
if (response && response.info) {
|
|
160
|
+
if (response?.info) {
|
|
151
161
|
const { statuscode: st } = response.info;
|
|
152
162
|
if (st === 402 || st > 600) {
|
|
153
163
|
// let it cascade to the next service
|
|
@@ -155,8 +165,8 @@ function init(options) {
|
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
|
|
158
|
-
const route = response
|
|
159
|
-
if (!
|
|
168
|
+
const route = response?.route;
|
|
169
|
+
if (!route?.legs?.length) {
|
|
160
170
|
// shouldn't happen
|
|
161
171
|
return;
|
|
162
172
|
}
|
|
@@ -175,9 +185,9 @@ function init(options) {
|
|
|
175
185
|
route.legs.reduce(extractDirections, {
|
|
176
186
|
directions,
|
|
177
187
|
unitMultiplier: query.units === 'km' ? util.metersInKm : util.metersInMile,
|
|
178
|
-
maneuverIndexes: route.shape
|
|
179
|
-
legIndexes: route.shape
|
|
180
|
-
path: route.shape
|
|
188
|
+
maneuverIndexes: route.shape?.maneuverIndexes,
|
|
189
|
+
legIndexes: route.shape?.legIndexes,
|
|
190
|
+
path: route.shape?.shapePoints.reduce(addPoint, []),
|
|
181
191
|
fullPath
|
|
182
192
|
});
|
|
183
193
|
if (fullPath) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// https://openrouteservice.org/dev/#/api-docs/directions
|
|
2
2
|
|
|
3
3
|
const LatLon = require('geodesy/latlon-spherical');
|
|
4
|
-
const { pathType, travelMode } = require(
|
|
4
|
+
const { pathType, travelMode } = require('../../model');
|
|
5
5
|
const status = require('../status');
|
|
6
6
|
const tagRoute = require('../tag-route');
|
|
7
7
|
const util = require('../util');
|
|
@@ -26,27 +26,29 @@ const avoidFeatures = {
|
|
|
26
26
|
avoidFerry: 'ferries'
|
|
27
27
|
};
|
|
28
28
|
const ferryTypes = {
|
|
29
|
-
|
|
29
|
+
9: true
|
|
30
30
|
};
|
|
31
31
|
const roughTypes = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
2: true, // Unpaved
|
|
33
|
+
8: true, // Compacted Gravel
|
|
34
|
+
9: true, // Fine Gravel
|
|
35
|
+
10: true, // Gravel
|
|
36
|
+
11: true, // Dirt
|
|
37
|
+
12: true, // Ground
|
|
38
|
+
15: true, // Sand
|
|
39
|
+
17: true, // Grass
|
|
40
|
+
18: true // Grass Paver
|
|
41
41
|
};
|
|
42
42
|
const tollTypes = {
|
|
43
|
-
|
|
43
|
+
1: true
|
|
44
44
|
};
|
|
45
45
|
const maxRoundabout = 12 * 60 * 60; // 12 hours maximum for route that is 10 times longer than direct distance
|
|
46
46
|
|
|
47
|
-
|
|
48
47
|
function extractStep(result, { distance, duration, instruction, way_points }) {
|
|
49
|
-
const {
|
|
48
|
+
const {
|
|
49
|
+
directions: { segments },
|
|
50
|
+
path
|
|
51
|
+
} = result;
|
|
50
52
|
const seg = {
|
|
51
53
|
duration: Math.round(duration || 0),
|
|
52
54
|
distance: Math.round(distance || 0),
|
|
@@ -70,13 +72,20 @@ function setTolls(seg) {
|
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
function extractDirections(result, { distance, duration, steps }, i) {
|
|
73
|
-
const {
|
|
75
|
+
const {
|
|
76
|
+
directions: { routes, segments },
|
|
77
|
+
path,
|
|
78
|
+
surface,
|
|
79
|
+
waypoints,
|
|
80
|
+
waytypes,
|
|
81
|
+
tollways
|
|
82
|
+
} = result;
|
|
74
83
|
const route = {
|
|
75
84
|
duration: Math.round(duration || 0),
|
|
76
85
|
distance: Math.round(distance || 0),
|
|
77
86
|
path: path?.slice(waypoints[i], waypoints[i + 1] + 1)
|
|
78
87
|
};
|
|
79
|
-
if (!(route.duration || route.distance ||
|
|
88
|
+
if (!(route.duration || route.distance || route.path?.length > 1)) {
|
|
80
89
|
route.path = [];
|
|
81
90
|
}
|
|
82
91
|
if (segments) {
|
|
@@ -87,7 +96,7 @@ function extractDirections(result, { distance, duration, steps }, i) {
|
|
|
87
96
|
steps.reduce(extractStep, result);
|
|
88
97
|
if (segments.length === 1) {
|
|
89
98
|
const seg = segments[0];
|
|
90
|
-
if (!(seg.duration || seg.distance ||
|
|
99
|
+
if (!(seg.duration || seg.distance || seg.path?.length > 1)) {
|
|
91
100
|
seg.path = [];
|
|
92
101
|
}
|
|
93
102
|
}
|
|
@@ -127,24 +136,30 @@ function toLatLon(p) {
|
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
function directDistance(locations) {
|
|
130
|
-
return locations.reduce(
|
|
131
|
-
next
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
return locations.reduce(
|
|
140
|
+
(result, next) => {
|
|
141
|
+
next = toLatLon(next);
|
|
142
|
+
result.dist += result.ll.distanceTo(next);
|
|
143
|
+
result.ll = next;
|
|
144
|
+
return result;
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
dist: 0,
|
|
148
|
+
ll: toLatLon(locations[0])
|
|
149
|
+
}
|
|
150
|
+
).dist;
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
function getStatus(st, response) {
|
|
142
154
|
if (!st && response?.routes?.length) {
|
|
143
|
-
const { distance, duration } = response.routes.reduce(
|
|
144
|
-
result
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
const { distance, duration } = response.routes.reduce(
|
|
156
|
+
(result, { summary }) => {
|
|
157
|
+
result.distance += summary.distance;
|
|
158
|
+
result.duration += summary.duration;
|
|
159
|
+
return result;
|
|
160
|
+
},
|
|
161
|
+
{ distance: 0, duration: 0 }
|
|
162
|
+
);
|
|
148
163
|
const coordinates = response.metadata?.query?.coordinates;
|
|
149
164
|
const direct = coordinates ? directDistance(coordinates) : distance;
|
|
150
165
|
if (distance < 5 * direct || duration < maxRoundabout) {
|
|
@@ -169,13 +184,8 @@ function vehicleSize(query, restrictions) {
|
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
function init(options) {
|
|
172
|
-
|
|
173
187
|
function prepareUrl(query) {
|
|
174
|
-
return [
|
|
175
|
-
options.openroute_url,
|
|
176
|
-
profile[query.mode] || profile[0],
|
|
177
|
-
'json'
|
|
178
|
-
].join('/');
|
|
188
|
+
return [options.openroute_url, profile[query.mode] || profile[0], 'json'].join('/');
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
function avoidFeature(result, flag) {
|
|
@@ -219,7 +229,6 @@ function init(options) {
|
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
function processResponse(response, query) {
|
|
222
|
-
|
|
223
232
|
if (!response?.routes?.length) {
|
|
224
233
|
// let it cascade to the next service
|
|
225
234
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { pathType } = require(
|
|
1
|
+
const { pathType } = require('../../model');
|
|
2
2
|
const status = require('../status');
|
|
3
3
|
const util = require('../util');
|
|
4
4
|
|
|
@@ -45,8 +45,8 @@ function convertRoute({ legs }) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// response codes: http://project-osrm.org/docs/v5.5.2/api/#responses
|
|
48
|
-
function getStatus(
|
|
49
|
-
const code = response
|
|
48
|
+
function getStatus(_err, response) {
|
|
49
|
+
const code = response?.code;
|
|
50
50
|
if (!response) {
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
@@ -75,26 +75,16 @@ const profile = {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
function prepareUrl(baseUrl, query) {
|
|
78
|
-
|
|
79
78
|
function coords2string(c) {
|
|
80
79
|
return c[0].toFixed(5) + ',' + c[1].toFixed(5);
|
|
81
80
|
}
|
|
82
81
|
|
|
82
|
+
const path = query.points.map(coords2string).join(';');
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
.map(coords2string)
|
|
86
|
-
.join(';');
|
|
87
|
-
|
|
88
|
-
return [
|
|
89
|
-
baseUrl,
|
|
90
|
-
'route/v1',
|
|
91
|
-
profile[query.mode],
|
|
92
|
-
path
|
|
93
|
-
].join('/');
|
|
84
|
+
return [baseUrl, 'route/v1', profile[query.mode], path].join('/');
|
|
94
85
|
}
|
|
95
86
|
|
|
96
87
|
function init(options) {
|
|
97
|
-
|
|
98
88
|
function processResponse(response, query) {
|
|
99
89
|
const directions = {
|
|
100
90
|
query,
|
|
@@ -112,14 +102,14 @@ function init(options) {
|
|
|
112
102
|
if (query.turnbyturn || query.path === pathType.smooth || query.path === pathType.coarse) {
|
|
113
103
|
directions.segments = [];
|
|
114
104
|
// copy segments from route to its own table
|
|
115
|
-
directions.routes.forEach(
|
|
105
|
+
directions.routes.forEach(route => {
|
|
116
106
|
route.segmentIndex = directions.segments.length;
|
|
117
107
|
directions.segments = directions.segments.concat(route.segments);
|
|
118
108
|
delete route.segments;
|
|
119
109
|
});
|
|
120
110
|
} else {
|
|
121
111
|
// delete segments
|
|
122
|
-
directions.routes.forEach(
|
|
112
|
+
directions.routes.forEach(route => {
|
|
123
113
|
delete route.segments;
|
|
124
114
|
});
|
|
125
115
|
}
|
package/lib/service/partition.js
CHANGED
|
@@ -3,7 +3,7 @@ const util = require('./util');
|
|
|
3
3
|
module.exports = partition;
|
|
4
4
|
|
|
5
5
|
function distanceSquare(p1, p2) {
|
|
6
|
-
return
|
|
6
|
+
return (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function pointOnLine(point, p1, p2) {
|
package/lib/service/simplify.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const pathType = require(
|
|
1
|
+
const pathType = require('../model').pathType;
|
|
2
2
|
const util = require('./util');
|
|
3
3
|
|
|
4
4
|
module.exports = init;
|
|
@@ -6,7 +6,7 @@ module.exports = init;
|
|
|
6
6
|
function init(options) {
|
|
7
7
|
options = options || {};
|
|
8
8
|
|
|
9
|
-
const algorithm = options.algorithm || require(
|
|
9
|
+
const algorithm = options.algorithm || require('vis-why');
|
|
10
10
|
const endPoints = options.endPoints || 25; // how many points keep at ends
|
|
11
11
|
const maxPoints = options.pathPoints || 100; // maximum number of points kept per path
|
|
12
12
|
const ed = maxPoints / endPoints;
|
|
@@ -53,8 +53,10 @@ function init(options) {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
if (first.split) {
|
|
56
|
-
util.concat(
|
|
57
|
-
|
|
56
|
+
util.concat(
|
|
57
|
+
path,
|
|
58
|
+
segments[i].path.slice(first.split, i === result.segmentIndex - 1 && last.split ? last.split : undefined)
|
|
59
|
+
);
|
|
58
60
|
i += 1;
|
|
59
61
|
}
|
|
60
62
|
for (; i < (last.split ? result.segmentIndex - 1 : result.segmentIndex); i += 1) {
|
|
@@ -64,7 +66,7 @@ function init(options) {
|
|
|
64
66
|
util.concat(path, segments[i].path.slice(0, last.split));
|
|
65
67
|
}
|
|
66
68
|
result.segmentIndex = route.segmentIndex;
|
|
67
|
-
if (type === pathType.full ||
|
|
69
|
+
if (type === pathType.full || first.length + path.length + last.length <= maxPoints) {
|
|
68
70
|
if (endDistance) {
|
|
69
71
|
route.path = util.concat(util.concat(first, path), last);
|
|
70
72
|
} else {
|
package/lib/service/util.js
CHANGED
|
@@ -64,7 +64,7 @@ function isFuture(time) {
|
|
|
64
64
|
|
|
65
65
|
// like normal join but with optional filter fn
|
|
66
66
|
function join(arr, conn, fn) {
|
|
67
|
-
fn = fn ||
|
|
67
|
+
fn = fn || (it => it);
|
|
68
68
|
return arr.filter(fn).join(conn);
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -73,24 +73,27 @@ function last(arr) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function split2object(str, conn, obj) {
|
|
76
|
-
return str.split(conn || '-').reduce(
|
|
76
|
+
return str.split(conn || '-').reduce((result, word) => {
|
|
77
77
|
result[word] = word;
|
|
78
78
|
return result;
|
|
79
79
|
}, obj || {});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function collateResults(results, query) {
|
|
83
|
-
return results.reduce(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
result.name
|
|
83
|
+
return results.reduce(
|
|
84
|
+
(result, r) => {
|
|
85
|
+
concatArrayProp(result, r, 'segments');
|
|
86
|
+
concatArrayProp(result, r, 'places');
|
|
87
|
+
concatArrayProp(result, r, 'routes');
|
|
88
|
+
if (!result.name && r?.name) {
|
|
89
|
+
result.name = r.name;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
query
|
|
89
95
|
}
|
|
90
|
-
|
|
91
|
-
}, {
|
|
92
|
-
query
|
|
93
|
-
});
|
|
96
|
+
);
|
|
94
97
|
|
|
95
98
|
function concatArrayProp(to, from, prop) {
|
|
96
99
|
if (!from[prop]) {
|
|
@@ -110,12 +113,10 @@ function withTimeout(promise, millis, signal) {
|
|
|
110
113
|
let reject;
|
|
111
114
|
|
|
112
115
|
signal?.addEventListener('abort', onabort);
|
|
113
|
-
return Promise
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
clearTimeout(id);
|
|
118
|
-
});
|
|
116
|
+
return Promise.race([promise, new Promise(timeoutPromise)]).finally(() => {
|
|
117
|
+
signal?.removeEventListener('abort', onabort);
|
|
118
|
+
clearTimeout(id);
|
|
119
|
+
});
|
|
119
120
|
|
|
120
121
|
function onabort() {
|
|
121
122
|
reject(signal.reason);
|
|
@@ -124,10 +125,7 @@ function withTimeout(promise, millis, signal) {
|
|
|
124
125
|
function timeoutPromise(_resolve, _reject) {
|
|
125
126
|
resolve = _resolve;
|
|
126
127
|
reject = _reject;
|
|
127
|
-
id = setTimeout(
|
|
128
|
-
() => resolve(),
|
|
129
|
-
millis
|
|
130
|
-
);
|
|
128
|
+
id = setTimeout(() => resolve(), millis);
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// https://github.com/valhalla/valhalla-docs
|
|
2
2
|
|
|
3
3
|
const LatLon = require('geodesy/latlon-spherical');
|
|
4
|
-
const { pathType, travelMode } = require(
|
|
4
|
+
const { pathType, travelMode } = require('../../model');
|
|
5
5
|
const status = require('../status');
|
|
6
6
|
const util = require('../util');
|
|
7
7
|
|
|
@@ -23,7 +23,7 @@ const costingOptions = {
|
|
|
23
23
|
hazmat: true
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
|
-
const minSpeed = 15 * 1000 / 3600; // bump speed when it is absurdly low
|
|
26
|
+
const minSpeed = (15 * 1000) / 3600; // bump speed when it is absurdly low
|
|
27
27
|
const turnDistance = 100; // don't adjust speed of turns
|
|
28
28
|
const maxRoundabout = 12 * 60 * 60; // 12 hours maximum for route that is 10 times longer than direct distance
|
|
29
29
|
|
|
@@ -41,18 +41,15 @@ function prepareWaypoint(p) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function extractSegment(
|
|
45
|
-
|
|
46
|
-
begin_shape_index,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
travel_mode
|
|
54
|
-
}) {
|
|
55
|
-
const { directions: { segments, routes }, unitMultiplier, path } = result;
|
|
44
|
+
function extractSegment(
|
|
45
|
+
result,
|
|
46
|
+
{ type, begin_shape_index, end_shape_index, instruction, length, time, rough, toll, travel_mode }
|
|
47
|
+
) {
|
|
48
|
+
const {
|
|
49
|
+
directions: { segments, routes },
|
|
50
|
+
unitMultiplier,
|
|
51
|
+
path
|
|
52
|
+
} = result;
|
|
56
53
|
const distance = Math.round((length || 0) * unitMultiplier);
|
|
57
54
|
let duration = time;
|
|
58
55
|
if (rough) {
|
|
@@ -69,7 +66,7 @@ function extractSegment(result, {
|
|
|
69
66
|
const seg = {
|
|
70
67
|
duration,
|
|
71
68
|
distance,
|
|
72
|
-
path: path
|
|
69
|
+
path: path?.slice(begin_shape_index, end_shape_index),
|
|
73
70
|
instructions: instruction
|
|
74
71
|
};
|
|
75
72
|
if (rough) {
|
|
@@ -98,8 +95,8 @@ function extractDirections(result, leg) {
|
|
|
98
95
|
const { directions } = result;
|
|
99
96
|
result.path = util.decode(leg.shape, { factor: 1e6 });
|
|
100
97
|
const route = {
|
|
101
|
-
duration:
|
|
102
|
-
distance: Math.round((
|
|
98
|
+
duration: leg.summary?.time || 0,
|
|
99
|
+
distance: Math.round((leg.summary?.length || 0) * result.unitMultiplier)
|
|
103
100
|
};
|
|
104
101
|
if (result.fullPath) {
|
|
105
102
|
route.path = result.path;
|
|
@@ -125,19 +122,24 @@ function toLatLon(p) {
|
|
|
125
122
|
|
|
126
123
|
function directDistance(locations, units) {
|
|
127
124
|
units = units === 'miles' ? util.metersInMile : util.metersInKm;
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
125
|
+
return (
|
|
126
|
+
locations.reduce(
|
|
127
|
+
(result, next) => {
|
|
128
|
+
next = toLatLon(next);
|
|
129
|
+
result.dist += result.ll.distanceTo(next);
|
|
130
|
+
result.ll = next;
|
|
131
|
+
return result;
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
dist: 0,
|
|
135
|
+
ll: toLatLon(locations[0])
|
|
136
|
+
}
|
|
137
|
+
).dist / units
|
|
138
|
+
);
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
function getStatus(
|
|
140
|
-
let st = response
|
|
141
|
+
function getStatus(_err, response) {
|
|
142
|
+
let st = response?.status_code;
|
|
141
143
|
if (!response) {
|
|
142
144
|
return;
|
|
143
145
|
}
|
|
@@ -150,14 +152,13 @@ function getStatus(err, response) {
|
|
|
150
152
|
// no route
|
|
151
153
|
return status.empty;
|
|
152
154
|
}
|
|
153
|
-
st = response.trip
|
|
155
|
+
st = response.trip?.status;
|
|
154
156
|
if (st === 0) {
|
|
155
|
-
if (response.trip.legs
|
|
157
|
+
if (response.trip.legs?.length) {
|
|
156
158
|
const { length, time } = response.trip.summary;
|
|
157
159
|
const distance = directDistance(response.trip.locations, response.trip.units);
|
|
158
160
|
// make sure points are not too far from roads
|
|
159
|
-
if (length > 0.9 * distance &&
|
|
160
|
-
(length < 10 * distance || time < maxRoundabout)) {
|
|
161
|
+
if (length > 0.9 * distance && (length < 10 * distance || time < maxRoundabout)) {
|
|
161
162
|
return status.success;
|
|
162
163
|
}
|
|
163
164
|
}
|
|
@@ -180,7 +181,6 @@ function vehicleSize(query, options) {
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
function init(options) {
|
|
183
|
-
|
|
184
184
|
function prepareRequest(query) {
|
|
185
185
|
let req = {
|
|
186
186
|
locations: query.points.map(prepareWaypoint),
|
|
@@ -229,8 +229,8 @@ function init(options) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
function processResponse(response, query) {
|
|
232
|
-
const trip = response
|
|
233
|
-
if (trip
|
|
232
|
+
const trip = response?.trip;
|
|
233
|
+
if (trip?.legs?.length) {
|
|
234
234
|
const directions = {
|
|
235
235
|
query,
|
|
236
236
|
provider: options.name,
|
|
@@ -267,4 +267,3 @@ function init(options) {
|
|
|
267
267
|
});
|
|
268
268
|
return require('..')(options);
|
|
269
269
|
}
|
|
270
|
-
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furkot/directions",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "Directions service for Furkot",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Damian Krzeminski",
|
|
7
7
|
"email": "pirxpilot@furkot.com",
|
|
8
8
|
"url": "https://pirxpilot.me"
|
|
9
9
|
},
|
|
10
|
-
"repository":
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/furkot/directions.git"
|
|
13
|
+
},
|
|
11
14
|
"license": "MIT",
|
|
12
15
|
"keywords": [
|
|
13
16
|
"furkot-directions",
|
|
@@ -20,13 +23,10 @@
|
|
|
20
23
|
"fetchagent": "~2.1.0",
|
|
21
24
|
"geodesy": "^1.1.1",
|
|
22
25
|
"limiter-component": "^1.2.0",
|
|
23
|
-
"vis-why": "
|
|
26
|
+
"vis-why": "~2"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
26
|
-
"
|
|
27
|
-
"mocha": "~10",
|
|
28
|
-
"should": "~13",
|
|
29
|
-
"sinon": "~14"
|
|
29
|
+
"@biomejs/biome": "^1.9.4"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"test": "make check"
|
|
@@ -35,4 +35,4 @@
|
|
|
35
35
|
"index.js",
|
|
36
36
|
"lib"
|
|
37
37
|
]
|
|
38
|
-
}
|
|
38
|
+
}
|