@furkot/directions 1.5.2 → 2.0.1

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 CHANGED
@@ -1,13 +1,11 @@
1
- const strategy = require('run-waterfall-until');
2
1
  const travelMode = require('./model').travelMode;
3
- const util = require('./service/util');
2
+ const { defaults: defaults, withTimeout } = require('./service/util');
4
3
 
5
4
  module.exports = furkotDirections;
6
5
 
7
- function skip(options, query, result) {
8
- // some other service already calculated directions
9
- // or service is disabled
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, result) {
33
+ skip(options, query) {
36
34
  // or asking for walking or biking directions (OSRM doesn't do it well)
37
- return skip(options, query, result) || (query.mode !== travelMode.car && query.mode !== travelMode.motorcycle);
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.reduce(function (result, name) {
51
+ options.services = options.order.map(name => {
105
52
  const service = services[options[name] || name];
106
- let defaults;
107
- if (service && options[(name + '_enable')]) {
108
- defaults = {
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
- limiter: options[(name + '_limiter')],
111
- enable: options[(name + '_enable')],
112
- skip: service.skip
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
- return result;
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.defaults[opt.replace(result.name, result.optName)] = result.options[opt];
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 = [{ // array of legs each for consecutive series of points
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, // return alternatives to the default route
35
- stats: [] // set on output - list of providers that requests have been sent to to obtain directions
36
- }];
34
+ alternate: false // return alternatives to the default route
35
+ };
37
36
 
38
37
  // template for directions results object
39
- const directionsResult = [{ // array of directions legs, one for each consecutive series of points
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,
@@ -1,8 +1,8 @@
1
1
  const fetchagent = require('fetchagent');
2
- const pathType = require("../model").pathType;
3
- const series = require('run-series');
2
+ const { pathType } = require("../model");
3
+ const makeLimiter = require('limiter-component');
4
4
  const status = require('./status');
5
- const util = require('./util');
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 eachOfSeries(items, task, fn) {
15
- const tasks = items.map(function (item, i) {
16
- return task.bind(null, item, i);
17
- });
18
- return series(tasks, fn);
19
- }
20
-
21
- function request(url, req, fn) {
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 init(options) {
47
- let limiter;
48
- let holdRequests;
49
- let simplify;
50
- const outstanding = {};
42
+ async function queryDirections(query) {
43
+ if (!query) {
44
+ throw ERROR;
45
+ }
51
46
 
52
- function abort(queryId) {
53
- debug('abort', queryId);
54
- if (!outstanding[queryId]) {
47
+ query.path = query.path || pathType.none;
48
+ let req = options.prepareRequest(query);
49
+ if (!req) {
55
50
  return;
56
51
  }
57
- // cancel later request if scheduled
58
- if (outstanding[queryId].laterTimeoutId) {
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
- function spliceResults(idx, segments, segResult) {
71
- Array.prototype.splice.apply(queryArray, [idx + queryArray.delta, 1].concat(segments));
72
- Array.prototype.splice.apply(result, [idx + queryArray.delta, 1].concat(segResult));
73
- queryArray.delta += segments.length - 1;
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
- function queryDirections(query, idx, callback) {
77
- let req;
78
- let segments;
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 (req === true) {
127
- req = undefined;
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
- outstanding[queryId] = outstanding[queryId] || {
192
- stack: 0,
193
- hits: 0
194
- };
195
- outstanding[queryId].stack += 1;
196
- outstanding[queryId].callback = function (err) {
197
- fn(err, true, queryId, queryArray, result);
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
- options = util.defaults(options, {
216
- interval: 340,
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
  }
@@ -1,3 +1,6 @@
1
+ // https://openrouteservice.org/dev/#/api-docs/directions
2
+
3
+ const LatLon = require('geodesy/latlon-spherical');
1
4
  const { pathType, travelMode } = require("../../model");
2
5
  const status = require('../status');
3
6
  const util = require('../util');
@@ -17,6 +20,7 @@ const profileRestrictions = {
17
20
  }
18
21
  };
19
22
  const ferryType = 9;
23
+ const maxRoundabout = 12 * 60 * 60; // 12 hours maximum for route that is 10 times longer than direct distance
20
24
 
21
25
  function nextFerry(result, points = [-1, -1]) {
22
26
  let { waytypes } = result;
@@ -87,9 +91,35 @@ function extractDirections(result, { distance, duration, steps }, i) {
87
91
  return result;
88
92
  }
89
93
 
94
+ function toLatLon(p) {
95
+ return new LatLon(p[1], p[0]);
96
+ }
97
+
98
+ function directDistance(locations) {
99
+ return locations.reduce((result, next) => {
100
+ next = toLatLon(next);
101
+ result.dist += result.ll.distanceTo(next);
102
+ result.ll = next;
103
+ return result;
104
+ }, {
105
+ dist: 0,
106
+ ll: toLatLon(locations[0])
107
+ }).dist;
108
+ }
109
+
90
110
  function getStatus(st, response) {
91
111
  if (!st && response && response.routes && response.routes.length) {
92
- return status.success;
112
+ const { distance, duration } = response.routes.reduce((result, { summary}) => {
113
+ result.distance += summary.distance;
114
+ result.duration += summary.duration;
115
+ return result;
116
+ }, { distance: 0, duration: 0 });
117
+ const coordinates = response.metadata?.query?.coordinates;
118
+ const direct = coordinates ? directDistance(coordinates) : distance;
119
+ if (distance < 5 * direct || duration < maxRoundabout) {
120
+ return status.success;
121
+ }
122
+ delete response.routes;
93
123
  }
94
124
  return status.empty;
95
125
  }
@@ -13,7 +13,9 @@ module.exports = {
13
13
  metersInKm: 1000,
14
14
  metersInMile: 1609.34,
15
15
  split2object,
16
- splitPoints
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 splitPoints(query, maxPoints) {
82
- let i;
83
- let segments;
84
- if (!(query.points && query.points.length > 1)) {
85
- return;
86
- }
87
- if (query.points.length <= maxPoints) {
88
- return query;
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
- segments = [];
91
- for (i = 0; i < query.points.length - 1; i += maxPoints - 1) {
92
- segments.push(defaults({
93
- points: query.points.slice(i, i + maxPoints),
94
- stats: []
95
- }, query));
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
- if (last(segments).points.length === 1) {
98
- last(segments).points.unshift(segments[segments.length - 2].points.pop());
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
- return segments;
130
+ }
131
+
132
+ function timeout(millis = 0) {
133
+ return new Promise(resolve => setTimeout(resolve, millis));
101
134
  }
@@ -25,6 +25,7 @@ const costingOptions = {
25
25
  };
26
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
+ const maxRoundabout = 12 * 60 * 60; // 12 hours maximum for route that is 10 times longer than direct distance
28
29
 
29
30
  const maneuver = {
30
31
  ferryEnter: 28,
@@ -109,9 +110,17 @@ function toLatLon(p) {
109
110
  return new LatLon(p.lat, p.lon);
110
111
  }
111
112
 
112
- function minDistance(locations, units) {
113
+ function directDistance(locations, units) {
113
114
  units = units === 'miles' ? util.metersInMile : util.metersInKm;
114
- return 0.9 * toLatLon(locations[0]).distanceTo(toLatLon(util.last(locations))) / units;
115
+ return locations.reduce((result, next) => {
116
+ next = toLatLon(next);
117
+ result.dist += result.ll.distanceTo(next);
118
+ result.ll = next;
119
+ return result;
120
+ }, {
121
+ dist: 0,
122
+ ll: toLatLon(locations[0])
123
+ }).dist / units;
115
124
  }
116
125
 
117
126
  function getStatus(err, response) {
@@ -130,10 +139,14 @@ function getStatus(err, response) {
130
139
  }
131
140
  st = response.trip && response.trip.status;
132
141
  if (st === 0) {
133
- if (response.trip.legs && response.trip.legs.length &&
142
+ if (response.trip.legs && response.trip.legs.length) {
143
+ const { length, time } = response.trip.summary;
144
+ const distance = directDistance(response.trip.locations, response.trip.units);
134
145
  // make sure points are not too far from roads
135
- response.trip.summary.length > minDistance(response.trip.locations, response.trip.units)) {
136
- return status.success;
146
+ if (length > 0.9 * distance &&
147
+ (length < 10 * distance || time < maxRoundabout)) {
148
+ return status.success;
149
+ }
137
150
  }
138
151
  delete response.trip.legs;
139
152
  return status.empty;
@@ -169,7 +182,7 @@ function init(options) {
169
182
  if (query.avoidHighways) {
170
183
  req.costing_options[req.costing].use_highways = 0;
171
184
  } else {
172
- req.costing_options[req.costing].use_highways = 1;
185
+ req.costing_options[req.costing].use_highways = 1.1;
173
186
  }
174
187
  vehicleSize(query, req.costing_options[req.costing]);
175
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furkot/directions",
3
- "version": "1.5.2",
3
+ "version": "2.0.1",
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.0.0",
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"