@furkot/directions 1.5.2 → 2.0.0
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/lib/directions.js +83 -83
- package/lib/model.js +6 -6
- package/lib/service/index.js +90 -201
- package/lib/service/util.js +53 -20
- package/package.json +3 -7
package/lib/directions.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
const strategy = require('run-waterfall-until');
|
|
2
1
|
const travelMode = require('./model').travelMode;
|
|
3
|
-
const
|
|
2
|
+
const { defaults: defaults, withTimeout } = require('./service/util');
|
|
4
3
|
|
|
5
4
|
module.exports = furkotDirections;
|
|
6
5
|
|
|
7
|
-
function skip(options, query
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
return result || !options.enable(query, result);
|
|
6
|
+
function skip(options, query) {
|
|
7
|
+
// if service is disabled
|
|
8
|
+
return !options.enable(query);
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
// query cascades through services until one produces a result
|
|
@@ -32,9 +30,9 @@ const services = {
|
|
|
32
30
|
},
|
|
33
31
|
osrm: {
|
|
34
32
|
service: require('./service/osrm'),
|
|
35
|
-
skip(options, query
|
|
33
|
+
skip(options, query) {
|
|
36
34
|
// or asking for walking or biking directions (OSRM doesn't do it well)
|
|
37
|
-
return skip(options, query
|
|
35
|
+
return skip(options, query) || (query.mode !== travelMode.car && query.mode !== travelMode.motorcycle);
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
};
|
|
@@ -42,96 +40,98 @@ const services = {
|
|
|
42
40
|
// default timeout to complete operation
|
|
43
41
|
const defaultTimeout = 20 * 1000;
|
|
44
42
|
|
|
45
|
-
let id = 0;
|
|
46
|
-
|
|
47
43
|
function furkotDirections(options) {
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
* Asynchronous directions service
|
|
51
|
-
* @param query directions query object
|
|
52
|
-
* @param fn function called with directions
|
|
53
|
-
*/
|
|
54
|
-
function directions(query, fn) {
|
|
55
|
-
if (!query) {
|
|
56
|
-
return fn();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
id += 1;
|
|
60
|
-
|
|
61
|
-
const result = new Array(query.length);
|
|
62
|
-
if (!query.length) {
|
|
63
|
-
return fn(query, result);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const queryId = id;
|
|
67
|
-
let timeoutId = setTimeout(function () {
|
|
68
|
-
timeoutId = undefined;
|
|
69
|
-
// cancel outstanding requests
|
|
70
|
-
options.services.forEach(function (service) {
|
|
71
|
-
service.abort(queryId);
|
|
72
|
-
});
|
|
73
|
-
}, options.timeout);
|
|
74
|
-
|
|
75
|
-
strategy(options.services, queryId, query, result, function (err, queryId, query, result) {
|
|
76
|
-
if (timeoutId) {
|
|
77
|
-
clearTimeout(timeoutId);
|
|
78
|
-
timeoutId = undefined;
|
|
79
|
-
}
|
|
80
|
-
if (err) {
|
|
81
|
-
return fn();
|
|
82
|
-
}
|
|
83
|
-
// if no results, mark first as empty
|
|
84
|
-
if (result.length > 0 && !result.some(function (r) {
|
|
85
|
-
return r;
|
|
86
|
-
})) {
|
|
87
|
-
result[0] = {
|
|
88
|
-
query: query[0],
|
|
89
|
-
routes: [{
|
|
90
|
-
distance: 0,
|
|
91
|
-
duration: 0
|
|
92
|
-
}]
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
fn(query, result);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
options = util.defaults(options, {
|
|
45
|
+
options = {
|
|
100
46
|
timeout: defaultTimeout,
|
|
101
|
-
order: ['osrm', 'mapquest', 'valhalla', 'graphhopper', 'openroute']
|
|
102
|
-
|
|
47
|
+
order: ['osrm', 'mapquest', 'valhalla', 'graphhopper', 'openroute'],
|
|
48
|
+
...options
|
|
49
|
+
};
|
|
103
50
|
if (!options.services) {
|
|
104
|
-
options.services = options.order.
|
|
51
|
+
options.services = options.order.map(name => {
|
|
105
52
|
const service = services[options[name] || name];
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
53
|
+
if (!service) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const enable = options[`${name}_enable`];
|
|
57
|
+
if (!enable) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// object representing actual parameters for a service
|
|
61
|
+
const serviceOptions = {
|
|
62
|
+
name,
|
|
63
|
+
limiter: options[`${name}_limiter`],
|
|
64
|
+
enable,
|
|
65
|
+
skip: service.skip
|
|
66
|
+
};
|
|
67
|
+
if (options[name]) {
|
|
68
|
+
Object.keys(options).reduce(mapOptions, {
|
|
69
|
+
options,
|
|
109
70
|
name,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
114
|
-
if (options[name]) {
|
|
115
|
-
Object.keys(options).reduce(mapOptions, {
|
|
116
|
-
options,
|
|
117
|
-
name,
|
|
118
|
-
optName: options[name],
|
|
119
|
-
defaults
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
result.push(service.service(util.defaults(defaults, options)));
|
|
71
|
+
optName: options[name],
|
|
72
|
+
serviceOptions
|
|
73
|
+
});
|
|
123
74
|
}
|
|
124
|
-
|
|
125
|
-
|
|
75
|
+
// we are adding options that has not been copied to serviceOptions yet
|
|
76
|
+
return service.service(defaults(serviceOptions, options));
|
|
77
|
+
}).filter(Boolean);
|
|
126
78
|
}
|
|
127
79
|
|
|
128
80
|
directions.options = options;
|
|
129
81
|
return directions;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Asynchronous directions service
|
|
85
|
+
* @param query directions query object
|
|
86
|
+
*/
|
|
87
|
+
function directions(query, { signal } = {}) {
|
|
88
|
+
if (query?.points?.length > 1) {
|
|
89
|
+
return requestDirections(query, options.timeout);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function requestDirections(query, timeout) {
|
|
93
|
+
const stats = [];
|
|
94
|
+
for (const service of options.services) {
|
|
95
|
+
if (service.skip(service, query)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
stats.push(service.name);
|
|
99
|
+
const startTime = Date.now();
|
|
100
|
+
const result = await withTimeout(service.operation(query), Math.floor(timeout / 2), signal);
|
|
101
|
+
if (signal?.aborted) {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
if (result?.query) {
|
|
105
|
+
result.stats = stats;
|
|
106
|
+
result.provider = service.name;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
timeout -= Date.now() - startTime;
|
|
110
|
+
if (timeout <= 0) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (query.points.length > 2) {
|
|
114
|
+
return requestDirections({
|
|
115
|
+
...query,
|
|
116
|
+
points: query.points.slice(0, 2)
|
|
117
|
+
}, timeout);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
query: {
|
|
122
|
+
...query,
|
|
123
|
+
points: query.points.slice(0, 2)
|
|
124
|
+
},
|
|
125
|
+
stats,
|
|
126
|
+
routes: [{}]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function mapOptions(result, opt) {
|
|
133
133
|
if (opt.startsWith(result.name)) {
|
|
134
|
-
result.
|
|
134
|
+
result.serviceOptions[opt.replace(result.name, result.optName)] = result.options[opt];
|
|
135
135
|
}
|
|
136
136
|
return result;
|
|
137
137
|
}
|
package/lib/model.js
CHANGED
|
@@ -17,7 +17,7 @@ const travelMode = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// template for directions query object
|
|
20
|
-
const directionsQuery =
|
|
20
|
+
const directionsQuery = {
|
|
21
21
|
mode: travelMode.car, // numeric value of travel mode
|
|
22
22
|
avoidHighways: false, // true to avoid highways
|
|
23
23
|
avoidTolls: false, // true to avoid toll roads
|
|
@@ -31,12 +31,11 @@ const directionsQuery = [{ // array of legs each for consecutive series of point
|
|
|
31
31
|
seasonal: false, // include roads that are seasonally closed
|
|
32
32
|
path: pathType.none, // the degree of route path simplification
|
|
33
33
|
span: 0, // distance in meters for more detailed path simplification
|
|
34
|
-
alternate: false
|
|
35
|
-
|
|
36
|
-
}];
|
|
34
|
+
alternate: false // return alternatives to the default route
|
|
35
|
+
};
|
|
37
36
|
|
|
38
37
|
// template for directions results object
|
|
39
|
-
const directionsResult =
|
|
38
|
+
const directionsResult = {
|
|
40
39
|
query: directionsQuery, // query parameters
|
|
41
40
|
places: [], // addresses or place names corresponding to points (if directions service performs reverse geocoding)
|
|
42
41
|
name: '', // human-readable name of directions (if available)
|
|
@@ -53,8 +52,9 @@ const directionsResult = [{ // array of directions legs, one for each consecutiv
|
|
|
53
52
|
path: [], // series of interim points; each point is [lon, lat]
|
|
54
53
|
instructions: '' // textual instructions for this segment
|
|
55
54
|
}],
|
|
55
|
+
stats: [], // list of providers that requests have been sent to to obtain directions
|
|
56
56
|
provider: '' // identifies service providing the directions
|
|
57
|
-
}
|
|
57
|
+
};
|
|
58
58
|
|
|
59
59
|
module.exports = {
|
|
60
60
|
directionsQuery,
|
package/lib/service/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fetchagent = require('fetchagent');
|
|
2
|
-
const pathType = require("../model")
|
|
3
|
-
const
|
|
2
|
+
const { pathType } = require("../model");
|
|
3
|
+
const makeLimiter = require('limiter-component');
|
|
4
4
|
const status = require('./status');
|
|
5
|
-
const
|
|
5
|
+
const makeSimplify = require('./simplify');
|
|
6
6
|
const debug = require('debug')('furkot:directions:service');
|
|
7
7
|
|
|
8
8
|
module.exports = init;
|
|
@@ -11,218 +11,107 @@ const limiters = {};
|
|
|
11
11
|
|
|
12
12
|
const ERROR = 'input error';
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const options = this;
|
|
23
|
-
let fa = fetchagent;
|
|
24
|
-
if (options.post) {
|
|
25
|
-
fa = fa.post(url).send(req);
|
|
26
|
-
} else {
|
|
27
|
-
fa = fa.get(url).query(req);
|
|
28
|
-
}
|
|
29
|
-
if (options.authorization) {
|
|
30
|
-
fa.set('authorization', options.authorization);
|
|
31
|
-
}
|
|
32
|
-
return fa
|
|
33
|
-
.set('accept', 'application/json')
|
|
34
|
-
.end(fn);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function initUrl(url) {
|
|
38
|
-
if (typeof url === 'function') {
|
|
39
|
-
return url;
|
|
40
|
-
}
|
|
41
|
-
return function () {
|
|
42
|
-
return url;
|
|
14
|
+
function init(options) {
|
|
15
|
+
options = {
|
|
16
|
+
interval: 340,
|
|
17
|
+
penaltyInterval: 2000,
|
|
18
|
+
limiter: limiters[options.name],
|
|
19
|
+
request,
|
|
20
|
+
operation,
|
|
21
|
+
...options
|
|
43
22
|
};
|
|
44
|
-
|
|
23
|
+
options.url = initUrl(options.url);
|
|
24
|
+
limiters[options.name] = options.limiter || makeLimiter(options.interval, options.penaltyInterval);
|
|
25
|
+
const limiter = limiters[options.name];
|
|
26
|
+
const simplify = makeSimplify(options);
|
|
27
|
+
return options;
|
|
28
|
+
|
|
29
|
+
async function operation(query) {
|
|
30
|
+
const { maxPoints } = options;
|
|
31
|
+
const { points } = query;
|
|
32
|
+
if (points.length > maxPoints) {
|
|
33
|
+
debug('Can only query %d points', maxPoints);
|
|
34
|
+
query = {
|
|
35
|
+
...query,
|
|
36
|
+
points: points.slice(0, maxPoints)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return queryDirections(query);
|
|
40
|
+
}
|
|
45
41
|
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const outstanding = {};
|
|
42
|
+
async function queryDirections(query) {
|
|
43
|
+
if (!query) {
|
|
44
|
+
throw ERROR;
|
|
45
|
+
}
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (!
|
|
47
|
+
query.path = query.path || pathType.none;
|
|
48
|
+
let req = options.prepareRequest(query);
|
|
49
|
+
if (!req) {
|
|
55
50
|
return;
|
|
56
51
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
clearTimeout(outstanding[queryId].laterTimeoutId);
|
|
60
|
-
}
|
|
61
|
-
// cancel request in progress
|
|
62
|
-
if (outstanding[queryId].reqInProgress) {
|
|
63
|
-
outstanding[queryId].reqInProgress.abort();
|
|
52
|
+
if (req === true) {
|
|
53
|
+
req = undefined;
|
|
64
54
|
}
|
|
65
|
-
outstanding[queryId].callback(ERROR);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function directions(queryId, queryArray, result, fn) {
|
|
69
55
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
await limiter.trigger();
|
|
57
|
+
const { status: err, response } = await options.request(options.url(query), req);
|
|
58
|
+
let st = options.status(err, response);
|
|
59
|
+
if (st === undefined) {
|
|
60
|
+
// shouldn't happen (bug or unexpected response format)
|
|
61
|
+
// treat it as no route
|
|
62
|
+
st = status.empty;
|
|
63
|
+
}
|
|
64
|
+
if (st === status.failure) {
|
|
65
|
+
// don't ever ask again
|
|
66
|
+
options.skip = () => true;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (st === status.error) {
|
|
70
|
+
// try again later
|
|
71
|
+
limiter.penalty();
|
|
72
|
+
return queryDirections(query);
|
|
73
|
+
}
|
|
74
|
+
if (st === status.empty) {
|
|
75
|
+
return;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
function requestLater() {
|
|
81
|
-
outstanding[queryId].laterTimeoutId = setTimeout(function () {
|
|
82
|
-
if (outstanding[queryId]) {
|
|
83
|
-
delete outstanding[queryId].laterTimeoutId;
|
|
84
|
-
}
|
|
85
|
-
queryDirections(query, idx, callback);
|
|
86
|
-
}, options.penaltyTimeout);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!outstanding[queryId]) {
|
|
90
|
-
// query has been aborted
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
outstanding[queryId].callback = callback;
|
|
94
|
-
|
|
95
|
-
if (options.skip(options, query, result[idx + queryArray.delta])) {
|
|
96
|
-
return callback();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (holdRequests) {
|
|
100
|
-
return callback();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
segments = util.splitPoints(query, queryArray.maxPoints || options.maxPoints);
|
|
104
|
-
if (!segments) {
|
|
105
|
-
return callback(ERROR);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (segments !== query) {
|
|
109
|
-
segments[0].stats = query.stats;
|
|
110
|
-
delete query.stats;
|
|
111
|
-
return directions(queryId, segments,
|
|
112
|
-
new Array(segments.length),
|
|
113
|
-
function (err, stop, id, query, result) {
|
|
114
|
-
if (query && result) {
|
|
115
|
-
spliceResults(idx, query, result);
|
|
116
|
-
}
|
|
117
|
-
callback(err);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
query.path = query.path || pathType.none;
|
|
122
|
-
req = options.prepareRequest(query);
|
|
123
|
-
if (!req) {
|
|
124
|
-
return callback();
|
|
78
|
+
const res = options.processResponse(response, query);
|
|
79
|
+
if (res) {
|
|
80
|
+
if (!res.pathReady && res.routes && res.segments) {
|
|
81
|
+
simplify(query.path, query.span, res.routes, res.segments);
|
|
125
82
|
}
|
|
126
|
-
if (
|
|
127
|
-
|
|
83
|
+
if (!query.turnbyturn) {
|
|
84
|
+
delete res.segments;
|
|
128
85
|
}
|
|
129
|
-
|
|
130
|
-
limiter.trigger(function () {
|
|
131
|
-
if (!outstanding[queryId]) {
|
|
132
|
-
// query has been aborted
|
|
133
|
-
limiter.skip(); // immediately process the next request in the queue
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
query.stats = query.stats || [];
|
|
137
|
-
query.stats.push(options.name);
|
|
138
|
-
outstanding[queryId].reqInProgress = options.request(options.url(query), req, function (err, response) {
|
|
139
|
-
let st;
|
|
140
|
-
let res;
|
|
141
|
-
if (!outstanding[queryId]) {
|
|
142
|
-
// query has been aborted
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
delete outstanding[queryId].reqInProgress;
|
|
146
|
-
st = options.status(err, response);
|
|
147
|
-
if (st === undefined) {
|
|
148
|
-
// shouldn't happen (bug or unexpected response format)
|
|
149
|
-
// treat it as no route
|
|
150
|
-
st = status.empty;
|
|
151
|
-
}
|
|
152
|
-
if (st === status.failure) {
|
|
153
|
-
// don't ever ask again
|
|
154
|
-
holdRequests = true;
|
|
155
|
-
return callback();
|
|
156
|
-
}
|
|
157
|
-
if (st === status.error) {
|
|
158
|
-
// try again later
|
|
159
|
-
limiter.penalty();
|
|
160
|
-
return requestLater();
|
|
161
|
-
}
|
|
162
|
-
if (st === status.empty && query.points.length > 2) {
|
|
163
|
-
query = [query];
|
|
164
|
-
query.maxPoints = 2;
|
|
165
|
-
|
|
166
|
-
return directions(queryId, query,
|
|
167
|
-
new Array(1),
|
|
168
|
-
function (err, stop, id, query, result) {
|
|
169
|
-
if (query && result) {
|
|
170
|
-
spliceResults(idx, query, result);
|
|
171
|
-
}
|
|
172
|
-
callback(err);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
res = options.processResponse(response, query);
|
|
177
|
-
if (res) {
|
|
178
|
-
if (!res.pathReady && res.routes && res.segments) {
|
|
179
|
-
simplify(query.path, query.span, res.routes, res.segments);
|
|
180
|
-
}
|
|
181
|
-
if (!query.turnbyturn) {
|
|
182
|
-
delete res.segments;
|
|
183
|
-
}
|
|
184
|
-
result[idx + queryArray.delta] = res;
|
|
185
|
-
}
|
|
186
|
-
callback();
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
86
|
}
|
|
87
|
+
return res;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
190
90
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
91
|
+
async function request(url, req) {
|
|
92
|
+
const options = this;
|
|
93
|
+
let fa = fetchagent;
|
|
94
|
+
if (options.post) {
|
|
95
|
+
fa = fa.post(url).send(req);
|
|
96
|
+
} else {
|
|
97
|
+
fa = fa.get(url).query(req);
|
|
98
|
+
}
|
|
99
|
+
if (options.authorization) {
|
|
100
|
+
fa.set('authorization', options.authorization);
|
|
101
|
+
}
|
|
102
|
+
const res = await fa.set('accept', 'application/json').end();
|
|
103
|
+
let status;
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
status = {
|
|
106
|
+
status: res.status
|
|
198
107
|
};
|
|
199
|
-
queryArray.delta = 0;
|
|
200
|
-
|
|
201
|
-
eachOfSeries(queryArray, queryDirections, function (err) {
|
|
202
|
-
if (outstanding[queryId]) {
|
|
203
|
-
outstanding[queryId].stack -= 1;
|
|
204
|
-
if (!outstanding[queryId].stack) {
|
|
205
|
-
delete outstanding[queryId];
|
|
206
|
-
}
|
|
207
|
-
if (err === ERROR) {
|
|
208
|
-
return fn(outstanding[queryId] ? err : undefined, true, queryId, queryArray, result);
|
|
209
|
-
}
|
|
210
|
-
fn(err, false, queryId, queryArray, result);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
108
|
}
|
|
109
|
+
return {
|
|
110
|
+
status,
|
|
111
|
+
response: await res.json()
|
|
112
|
+
};
|
|
113
|
+
}
|
|
214
114
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
penaltyInterval: 2000,
|
|
218
|
-
limiter: limiters[options.name],
|
|
219
|
-
request,
|
|
220
|
-
abort
|
|
221
|
-
});
|
|
222
|
-
options.url = initUrl(options.url);
|
|
223
|
-
limiters[options.name] = options.limiter || require('limiter-component')(options.interval, options.penaltyInterval);
|
|
224
|
-
limiter = limiters[options.name];
|
|
225
|
-
simplify = require('./simplify')(options);
|
|
226
|
-
directions.abort = options.abort;
|
|
227
|
-
return directions;
|
|
115
|
+
function initUrl(url) {
|
|
116
|
+
return typeof url === 'function' ? url : () => url;
|
|
228
117
|
}
|
package/lib/service/util.js
CHANGED
|
@@ -13,7 +13,9 @@ module.exports = {
|
|
|
13
13
|
metersInKm: 1000,
|
|
14
14
|
metersInMile: 1609.34,
|
|
15
15
|
split2object,
|
|
16
|
-
|
|
16
|
+
collateResults,
|
|
17
|
+
withTimeout,
|
|
18
|
+
timeout
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
function concat(result, path) {
|
|
@@ -43,9 +45,8 @@ function indexAt(path, distance) {
|
|
|
43
45
|
let index = 1;
|
|
44
46
|
let p1 = toLatLon(path[0]);
|
|
45
47
|
let d = 0;
|
|
46
|
-
let p2;
|
|
47
48
|
while (index < path.length) {
|
|
48
|
-
p2 = toLatLon(path[index]);
|
|
49
|
+
const p2 = toLatLon(path[index]);
|
|
49
50
|
d += p1.distanceTo(p2);
|
|
50
51
|
if (d > distance) {
|
|
51
52
|
break;
|
|
@@ -78,24 +79,56 @@ function split2object(str, conn, obj) {
|
|
|
78
79
|
}, obj || {});
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
function collateResults(results, query) {
|
|
83
|
+
return results.reduce((result, r) => {
|
|
84
|
+
concatArrayProp(result, r, 'segments');
|
|
85
|
+
concatArrayProp(result, r, 'places');
|
|
86
|
+
concatArrayProp(result, r, 'routes');
|
|
87
|
+
if (!result.name && r?.name) {
|
|
88
|
+
result.name = r.name;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}, {
|
|
92
|
+
query
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
function concatArrayProp(to, from, prop) {
|
|
96
|
+
if (!from[prop]) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!to[prop]) {
|
|
100
|
+
to[prop] = from[prop];
|
|
101
|
+
} else {
|
|
102
|
+
to[prop].push(...from[prop]);
|
|
103
|
+
}
|
|
89
104
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function withTimeout(promise, millis, signal) {
|
|
108
|
+
let id;
|
|
109
|
+
let reject;
|
|
110
|
+
|
|
111
|
+
signal?.addEventListener('abort', onabort);
|
|
112
|
+
return Promise
|
|
113
|
+
.race([promise, new Promise(timeoutPromise)])
|
|
114
|
+
.finally(() => {
|
|
115
|
+
signal?.removeEventListener('abort', onabort);
|
|
116
|
+
clearTimeout(id);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function onabort() {
|
|
120
|
+
reject(signal.reason);
|
|
96
121
|
}
|
|
97
|
-
|
|
98
|
-
|
|
122
|
+
|
|
123
|
+
function timeoutPromise(_, _reject) {
|
|
124
|
+
reject = _reject;
|
|
125
|
+
id = setTimeout(
|
|
126
|
+
() => reject(Error('timeout', { cause: Symbol.for('timeout') })),
|
|
127
|
+
millis
|
|
128
|
+
);
|
|
99
129
|
}
|
|
100
|
-
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function timeout(millis = 0) {
|
|
133
|
+
return new Promise(resolve => setTimeout(resolve, millis));
|
|
101
134
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furkot/directions",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Directions service for Furkot",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Damian Krzeminski",
|
|
@@ -17,17 +17,13 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@pirxpilot/google-polyline": "^3.0.0",
|
|
19
19
|
"debug": "~2 || ~3 || ~4",
|
|
20
|
-
"fetchagent": "~2",
|
|
20
|
+
"fetchagent": "~2.1.0",
|
|
21
21
|
"geodesy": "^1.1.1",
|
|
22
|
-
"limiter-component": "^1.
|
|
23
|
-
"run-series": "^1.1.4",
|
|
24
|
-
"run-waterfall-until": "~1",
|
|
22
|
+
"limiter-component": "^1.2.0",
|
|
25
23
|
"vis-why": "^1.2.2"
|
|
26
24
|
},
|
|
27
25
|
"devDependencies": {
|
|
28
26
|
"jshint": "~2",
|
|
29
|
-
"lodash.clonedeep": "^4.5.0",
|
|
30
|
-
"lodash.clonedeepwith": "^4.5.0",
|
|
31
27
|
"mocha": "~10",
|
|
32
28
|
"should": "~13",
|
|
33
29
|
"sinon": "~14"
|