@furkot/directions 1.5.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/Readme.md +34 -0
- package/index.js +1 -0
- package/lib/directions.js +137 -0
- package/lib/model.js +64 -0
- package/lib/service/graphhopper/index.js +164 -0
- package/lib/service/index.js +228 -0
- package/lib/service/mapquest/index.js +199 -0
- package/lib/service/openroute/index.js +197 -0
- package/lib/service/osrm/index.js +144 -0
- package/lib/service/partition.js +79 -0
- package/lib/service/simplify.js +96 -0
- package/lib/service/status.js +6 -0
- package/lib/service/util.js +101 -0
- package/lib/service/valhalla/index.js +230 -0
- package/package.json +42 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const { pathType } = require("../../model");
|
|
2
|
+
const status = require('../status');
|
|
3
|
+
const util = require('../util');
|
|
4
|
+
|
|
5
|
+
module.exports = init;
|
|
6
|
+
|
|
7
|
+
const units = {
|
|
8
|
+
km: 'k',
|
|
9
|
+
m: 'm'
|
|
10
|
+
};
|
|
11
|
+
const routeType = {
|
|
12
|
+
'-1': 'shortest',
|
|
13
|
+
0: 'shortest',
|
|
14
|
+
1: 'bicycle',
|
|
15
|
+
2: 'pedestrian',
|
|
16
|
+
5: 'shortest'
|
|
17
|
+
};
|
|
18
|
+
const tolerance = {
|
|
19
|
+
'-1': 20,
|
|
20
|
+
0: 20,
|
|
21
|
+
1: 10,
|
|
22
|
+
2: 2
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function prepareWaypoint(p) {
|
|
26
|
+
return {
|
|
27
|
+
latLng: {
|
|
28
|
+
lng: p[0],
|
|
29
|
+
lat: p[1]
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function addPoint(result, p, i, points) {
|
|
35
|
+
if (i % 2 === 0) {
|
|
36
|
+
result.push([points[i + 1], p]);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function extractSegment(result, { distance, index, narrative, time }) {
|
|
42
|
+
const { directions: { segments }, maneuverIndexes: indexes, path, unitMultiplier } = result;
|
|
43
|
+
segments.push({
|
|
44
|
+
duration: time,
|
|
45
|
+
distance: Math.round((distance || 0) * unitMultiplier),
|
|
46
|
+
path: indexes && path && path.slice(indexes[index], indexes[index + 1]),
|
|
47
|
+
instructions: narrative
|
|
48
|
+
});
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function extractDirections(result, leg) {
|
|
53
|
+
const { directions: { routes, segments }, legIndexes: indexes, path } = result;
|
|
54
|
+
const route = {
|
|
55
|
+
duration: leg.time || 0,
|
|
56
|
+
distance: Math.round((leg.distance || 0) * result.unitMultiplier)
|
|
57
|
+
};
|
|
58
|
+
if (indexes && path && result.fullPath) {
|
|
59
|
+
route.path = path.slice(indexes[leg.index], indexes[leg.index + 1]);
|
|
60
|
+
}
|
|
61
|
+
if (leg.hasSeasonalClosure) {
|
|
62
|
+
route.seasonal = true;
|
|
63
|
+
}
|
|
64
|
+
if (segments) {
|
|
65
|
+
route.segmentIndex = segments.length;
|
|
66
|
+
}
|
|
67
|
+
routes.push(route);
|
|
68
|
+
if (segments && leg.maneuvers) {
|
|
69
|
+
leg.maneuvers.reduce(extractSegment, result);
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getStatus(err, response) {
|
|
75
|
+
if (!response) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const { info, route } = response;
|
|
79
|
+
const st = info && info.statuscode;
|
|
80
|
+
if (st === 403 || st === 500) {
|
|
81
|
+
// assume its because we exceeded the limit
|
|
82
|
+
return status.failure;
|
|
83
|
+
}
|
|
84
|
+
if (st === 402 || st > 600) {
|
|
85
|
+
// cannot find route
|
|
86
|
+
return status.empty;
|
|
87
|
+
}
|
|
88
|
+
if (st === 0) {
|
|
89
|
+
if (route) {
|
|
90
|
+
return status.success;
|
|
91
|
+
}
|
|
92
|
+
return status.empty;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function init(options) {
|
|
97
|
+
|
|
98
|
+
function getProvider(query) {
|
|
99
|
+
if (query.alternate) {
|
|
100
|
+
return options.name;
|
|
101
|
+
}
|
|
102
|
+
return 'open' + options.name;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getUrl(query) {
|
|
106
|
+
return options[getProvider(query) + '_url'] + '/directions/v2/' +
|
|
107
|
+
(query.alternate && query.points.length <= 2 ? 'alternateroutes' : 'route');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function prepareRequest(query) {
|
|
111
|
+
const req = {
|
|
112
|
+
unit: units[query.units] || units.m,
|
|
113
|
+
manmaps: false,
|
|
114
|
+
destinationManeuverDisplay: false,
|
|
115
|
+
doReverseGeocode: false,
|
|
116
|
+
routeType: routeType[query.mode] || routeType[0],
|
|
117
|
+
generalize: tolerance[query.mode]
|
|
118
|
+
};
|
|
119
|
+
if (query.avoidHighways) {
|
|
120
|
+
req.avoids = ['Limited Access'];
|
|
121
|
+
}
|
|
122
|
+
if (query.avoidTolls) {
|
|
123
|
+
req.avoids = req.avoids || [];
|
|
124
|
+
req.avoids.push('Toll Road');
|
|
125
|
+
}
|
|
126
|
+
if (query.begin) {
|
|
127
|
+
req.timeType = 2;
|
|
128
|
+
req.dateType = 0;
|
|
129
|
+
req.isoLocal = query.begin;
|
|
130
|
+
} else if (!query.seasonal) {
|
|
131
|
+
req.disallows = ['Approximate Seasonal Closure'];
|
|
132
|
+
}
|
|
133
|
+
if (!query.turnbyturn) {
|
|
134
|
+
req.narrativeType = 'none';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
json: JSON.stringify({
|
|
139
|
+
options: req,
|
|
140
|
+
locations: query.points.map(prepareWaypoint)
|
|
141
|
+
}),
|
|
142
|
+
inFormat: 'json',
|
|
143
|
+
outFormat: 'json',
|
|
144
|
+
key: options.mapquest_key
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function processResponse(response, query) {
|
|
149
|
+
|
|
150
|
+
if (response && response.info) {
|
|
151
|
+
const { statuscode: st } = response.info;
|
|
152
|
+
if (st === 402 || st > 600) {
|
|
153
|
+
// let it cascade to the next service
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const route = response && response.route;
|
|
159
|
+
if (!(route && route.legs && route.legs.length)) {
|
|
160
|
+
// shouldn't happen
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const directions = {
|
|
164
|
+
query,
|
|
165
|
+
provider: getProvider(query)
|
|
166
|
+
};
|
|
167
|
+
if (route.name) {
|
|
168
|
+
directions.name = route.name;
|
|
169
|
+
}
|
|
170
|
+
directions.routes = [];
|
|
171
|
+
if (query.turnbyturn || query.path === pathType.smooth || query.path === pathType.coarse) {
|
|
172
|
+
directions.segments = [];
|
|
173
|
+
}
|
|
174
|
+
const fullPath = query.path === pathType.full;
|
|
175
|
+
route.legs.reduce(extractDirections, {
|
|
176
|
+
directions,
|
|
177
|
+
unitMultiplier: query.units === 'km' ? util.metersInKm : util.metersInMile,
|
|
178
|
+
maneuverIndexes: route.shape && route.shape.maneuverIndexes,
|
|
179
|
+
legIndexes: route.shape && route.shape.legIndexes,
|
|
180
|
+
path: route.shape && route.shape.shapePoints.reduce(addPoint, []),
|
|
181
|
+
fullPath
|
|
182
|
+
});
|
|
183
|
+
if (fullPath) {
|
|
184
|
+
// path is already prepared - no need to do it again from segments
|
|
185
|
+
directions.pathReady = true;
|
|
186
|
+
}
|
|
187
|
+
return directions;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
options = util.defaults(options, {
|
|
191
|
+
// MapQuest allows 50 but we code it in URL and may run into URL length limit of approx 2000
|
|
192
|
+
maxPoints: 30,
|
|
193
|
+
url: getUrl,
|
|
194
|
+
status: getStatus,
|
|
195
|
+
prepareRequest,
|
|
196
|
+
processResponse
|
|
197
|
+
});
|
|
198
|
+
return require('..')(options);
|
|
199
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const { pathType, travelMode } = require("../../model");
|
|
2
|
+
const status = require('../status');
|
|
3
|
+
const util = require('../util');
|
|
4
|
+
|
|
5
|
+
module.exports = init;
|
|
6
|
+
|
|
7
|
+
const profile = {
|
|
8
|
+
'-1': 'driving-car',
|
|
9
|
+
0: 'driving-car',
|
|
10
|
+
1: 'cycling-regular',
|
|
11
|
+
2: 'foot-hiking',
|
|
12
|
+
5: 'driving-hgv'
|
|
13
|
+
};
|
|
14
|
+
const profileRestrictions = {
|
|
15
|
+
5: {
|
|
16
|
+
hazmat: true
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const ferryType = 9;
|
|
20
|
+
|
|
21
|
+
function nextFerry(result, points = [-1, -1]) {
|
|
22
|
+
let { waytypes } = result;
|
|
23
|
+
if (!waytypes) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (; result.way < waytypes.length; result.way += 1) {
|
|
27
|
+
const waytype = waytypes[result.way];
|
|
28
|
+
if (waytype[0] > points[1]) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (waytype[2] === ferryType && (
|
|
32
|
+
(waytype[0] <= points[0] && waytype[1] >= points[1]) ||
|
|
33
|
+
(waytype[0] > points[0] && waytype[1] < points[1]) ||
|
|
34
|
+
(waytype[0] >= points[0] && waytype[0] <= points[1]) ||
|
|
35
|
+
(waytype[1] >= points[0] && waytype[1] <= points[1])
|
|
36
|
+
)) {
|
|
37
|
+
return waytype;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function extractStep(result, { distance, duration, instruction, way_points }) {
|
|
43
|
+
const { directions: { segments, routes }, path } = result;
|
|
44
|
+
const seg = {
|
|
45
|
+
duration: Math.round(duration || 0),
|
|
46
|
+
distance: Math.round(distance || 0),
|
|
47
|
+
path: path && path.slice(way_points[0], way_points[1]),
|
|
48
|
+
instructions: instruction
|
|
49
|
+
};
|
|
50
|
+
segments.push(seg);
|
|
51
|
+
const ferrySeg = nextFerry(result, way_points);
|
|
52
|
+
if (ferrySeg) {
|
|
53
|
+
seg.mode = travelMode.ferry;
|
|
54
|
+
util.last(routes).ferry = true;
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractDirections(result, { distance, duration, steps }, i) {
|
|
60
|
+
const { directions: { routes, segments }, path, waypoints } = result;
|
|
61
|
+
const route = {
|
|
62
|
+
duration: Math.round(duration || 0),
|
|
63
|
+
distance: Math.round(distance || 0),
|
|
64
|
+
path: path && path.slice(waypoints[i], waypoints[i + 1] + 1)
|
|
65
|
+
};
|
|
66
|
+
if (!(route.duration || route.distance || (route.path && route.path.length > 1))) {
|
|
67
|
+
route.path = [];
|
|
68
|
+
}
|
|
69
|
+
if (segments) {
|
|
70
|
+
route.segmentIndex = segments.length;
|
|
71
|
+
}
|
|
72
|
+
routes.push(route);
|
|
73
|
+
if (segments && steps) {
|
|
74
|
+
result.way = 0;
|
|
75
|
+
nextFerry(result);
|
|
76
|
+
steps.reduce(extractStep, result);
|
|
77
|
+
if (segments.length === 1) {
|
|
78
|
+
const seg = segments[0];
|
|
79
|
+
if (!(seg.duration || seg.distance || (seg.path && seg.path.length > 1))) {
|
|
80
|
+
seg.path = [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (segments.length && route.path.length) {
|
|
84
|
+
util.last(segments).path.push(util.last(route.path));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getStatus(st, response) {
|
|
91
|
+
if (!st && response && response.routes && response.routes.length) {
|
|
92
|
+
return status.success;
|
|
93
|
+
}
|
|
94
|
+
return status.empty;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function vehicleSize(query, restrictions) {
|
|
98
|
+
const { mode, vehicle } = query;
|
|
99
|
+
if (!(vehicle && mode === 5)) {
|
|
100
|
+
return restrictions;
|
|
101
|
+
}
|
|
102
|
+
restrictions = Object.assign({}, restrictions, vehicle);
|
|
103
|
+
if (restrictions.axle_load !== undefined) {
|
|
104
|
+
restrictions.axleload = restrictions.axle_load;
|
|
105
|
+
delete restrictions.axle_load;
|
|
106
|
+
}
|
|
107
|
+
return restrictions;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function init(options) {
|
|
111
|
+
|
|
112
|
+
function prepareUrl(query) {
|
|
113
|
+
return [
|
|
114
|
+
options.openroute_url,
|
|
115
|
+
profile[query.mode] || profile[0],
|
|
116
|
+
'json'
|
|
117
|
+
].join('/');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function prepareRequest(query) {
|
|
121
|
+
const req = {
|
|
122
|
+
coordinates: query.points
|
|
123
|
+
};
|
|
124
|
+
if (!query.turnbyturn && query.path !== pathType.smooth && query.path !== pathType.coarse) {
|
|
125
|
+
req.instructions = false;
|
|
126
|
+
} else {
|
|
127
|
+
req.extra_info = ['waytype'];
|
|
128
|
+
}
|
|
129
|
+
const restrictions = vehicleSize(query, profileRestrictions[query.mode]);
|
|
130
|
+
if (restrictions) {
|
|
131
|
+
req.options = {
|
|
132
|
+
profile_params: {
|
|
133
|
+
restrictions
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if ((profile[query.mode] || profile[0]) === 'driving-car') {
|
|
138
|
+
if (query.avoidHighways) {
|
|
139
|
+
req.options = req.options || {
|
|
140
|
+
avoid_features: ['highways']
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (query.avoidTolls) {
|
|
144
|
+
req.options = req.options || {};
|
|
145
|
+
req.options.avoid_features = req.options.avoid_features || [];
|
|
146
|
+
req.options.avoid_features.push('tollways');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return req;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function processResponse(response, query) {
|
|
154
|
+
|
|
155
|
+
if (!(response && response.routes && response.routes.length)) {
|
|
156
|
+
// let it cascade to the next service
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const directions = {
|
|
161
|
+
query,
|
|
162
|
+
provider: options.name
|
|
163
|
+
};
|
|
164
|
+
const paths = response.routes[0].segments;
|
|
165
|
+
const geometry = response.routes[0].geometry;
|
|
166
|
+
if (paths) {
|
|
167
|
+
directions.routes = [];
|
|
168
|
+
if (query.turnbyturn || query.path === pathType.smooth || query.path === pathType.coarse) {
|
|
169
|
+
directions.segments = [];
|
|
170
|
+
}
|
|
171
|
+
const fullPath = query.path === pathType.full;
|
|
172
|
+
const { extras, way_points: waypoints } = response.routes[0];
|
|
173
|
+
paths.reduce(extractDirections, {
|
|
174
|
+
directions,
|
|
175
|
+
path: util.decode(geometry),
|
|
176
|
+
waypoints,
|
|
177
|
+
waytypes: extras && extras.waytypes && extras.waytypes.values
|
|
178
|
+
});
|
|
179
|
+
if (fullPath) {
|
|
180
|
+
// path is already prepared - no need to do it again from segments
|
|
181
|
+
directions.pathReady = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return directions;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
options = util.defaults(options, {
|
|
188
|
+
maxPoints: 10, // unknown maximum
|
|
189
|
+
post: true,
|
|
190
|
+
authorization: options.openroute_key,
|
|
191
|
+
url: prepareUrl,
|
|
192
|
+
status: getStatus,
|
|
193
|
+
prepareRequest,
|
|
194
|
+
processResponse
|
|
195
|
+
});
|
|
196
|
+
return require('..')(options);
|
|
197
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const { pathType } = require("../../model");
|
|
2
|
+
const status = require('../status');
|
|
3
|
+
const util = require('../util');
|
|
4
|
+
|
|
5
|
+
const code2status = {
|
|
6
|
+
Ok: status.success,
|
|
7
|
+
NoRoute: status.empty,
|
|
8
|
+
NoSegment: status.empty
|
|
9
|
+
};
|
|
10
|
+
const RADIUS = 1000; // search radius for nearby roads
|
|
11
|
+
|
|
12
|
+
module.exports = init;
|
|
13
|
+
|
|
14
|
+
function convertPlace(wpt) {
|
|
15
|
+
return wpt.name;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function convertStep(step) {
|
|
19
|
+
return {
|
|
20
|
+
duration: step.duration,
|
|
21
|
+
distance: step.distance,
|
|
22
|
+
path: util.decode(step.geometry),
|
|
23
|
+
instructions: step.maneuver.type + ' ' + step.maneuver.modifier
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function convertPath(path, seg) {
|
|
28
|
+
return path.concat(seg.path.slice(1));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function convertLeg({ distance, duration, steps }) {
|
|
32
|
+
const r = {
|
|
33
|
+
duration,
|
|
34
|
+
distance,
|
|
35
|
+
segments: steps.map(convertStep)
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
r.path = r.segments.reduce(convertPath, []);
|
|
39
|
+
|
|
40
|
+
return r;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function convertRoute({ legs }) {
|
|
44
|
+
return legs.map(convertLeg);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// response codes: http://project-osrm.org/docs/v5.5.2/api/#responses
|
|
48
|
+
function getStatus(err, response) {
|
|
49
|
+
const code = response && response.code;
|
|
50
|
+
if (!response) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
return code2status[code] || status.error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function point2radius() {
|
|
57
|
+
return RADIUS;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function prepareRequest(query) {
|
|
61
|
+
return {
|
|
62
|
+
alternatives: Boolean(query.alternate),
|
|
63
|
+
steps: true, // always ask for steps since legs do not have overview
|
|
64
|
+
overview: false, // we'll get this from steps
|
|
65
|
+
radiuses: query.points.map(point2radius).join(';')
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const profile = {
|
|
70
|
+
'-1': 'driving', // same as car
|
|
71
|
+
0: 'car',
|
|
72
|
+
1: 'bicycle',
|
|
73
|
+
2: 'foot',
|
|
74
|
+
5: 'driving'
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function prepareUrl(baseUrl, query) {
|
|
78
|
+
|
|
79
|
+
function coords2string(c) {
|
|
80
|
+
return c[0].toFixed(5) + ',' + c[1].toFixed(5);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
const path = query.points
|
|
85
|
+
.map(coords2string)
|
|
86
|
+
.join(';');
|
|
87
|
+
|
|
88
|
+
return [
|
|
89
|
+
baseUrl,
|
|
90
|
+
'route/v1',
|
|
91
|
+
profile[query.mode],
|
|
92
|
+
path
|
|
93
|
+
].join('/');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function init(options) {
|
|
97
|
+
|
|
98
|
+
function processResponse(response, query) {
|
|
99
|
+
const directions = {
|
|
100
|
+
query,
|
|
101
|
+
provider: options.name
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (response) {
|
|
105
|
+
if (response.waypoints) {
|
|
106
|
+
directions.places = response.waypoints.map(convertPlace);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (response.routes) {
|
|
110
|
+
directions.routes = convertRoute(response.routes[0]);
|
|
111
|
+
|
|
112
|
+
if (query.turnbyturn || query.path === pathType.smooth || query.path === pathType.coarse) {
|
|
113
|
+
directions.segments = [];
|
|
114
|
+
// copy segments from route to its own table
|
|
115
|
+
directions.routes.forEach(function (route) {
|
|
116
|
+
route.segmentIndex = directions.segments.length;
|
|
117
|
+
directions.segments = directions.segments.concat(route.segments);
|
|
118
|
+
delete route.segments;
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
// delete segments
|
|
122
|
+
directions.routes.forEach(function (route) {
|
|
123
|
+
delete route.segments;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (query.path === pathType.full) {
|
|
128
|
+
// path is already prepared - no need to do it again from segments
|
|
129
|
+
directions.pathReady = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return directions;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
options = util.defaults(options, {
|
|
137
|
+
maxPoints: 20, // max 20 points for automobile and 50 for bicycle and pedestrian
|
|
138
|
+
url: prepareUrl.bind(null, options.osrm_url || 'https://router.project-osrm.org'),
|
|
139
|
+
status: getStatus,
|
|
140
|
+
prepareRequest,
|
|
141
|
+
processResponse
|
|
142
|
+
});
|
|
143
|
+
return require('..')(options);
|
|
144
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const util = require('./util');
|
|
2
|
+
|
|
3
|
+
module.exports = partition;
|
|
4
|
+
|
|
5
|
+
function distanceSquare(p1, p2) {
|
|
6
|
+
return Math.pow((p1[0] - p2[0]), 2) + Math.pow((p1[1] - p2[1]), 2);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function pointOnLine(point, p1, p2) {
|
|
10
|
+
const d = distanceSquare(p1, p2);
|
|
11
|
+
let t;
|
|
12
|
+
let pol;
|
|
13
|
+
if (!d) {
|
|
14
|
+
pol = p1;
|
|
15
|
+
} else {
|
|
16
|
+
t = ((point[0] - p1[0]) * (p2[0] - p1[0]) + (point[1] - p1[1]) * (p2[1] - p1[1])) / d;
|
|
17
|
+
if (t < 0) {
|
|
18
|
+
pol = p1;
|
|
19
|
+
} else if (t > 1) {
|
|
20
|
+
pol = p2;
|
|
21
|
+
} else {
|
|
22
|
+
pol = [p1[0] + t * (p2[0] - p1[0]), p1[1] + t * (p2[1] - p1[1])];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return pol;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function minDistance(res, p, i, path) {
|
|
29
|
+
let dist = distanceSquare(res.point, p);
|
|
30
|
+
if (dist < res.dist || (dist < res.minDistance && res.pol)) {
|
|
31
|
+
res.dist = dist;
|
|
32
|
+
res.idx = i;
|
|
33
|
+
res.p = p;
|
|
34
|
+
res.pol = undefined;
|
|
35
|
+
}
|
|
36
|
+
if (i > 0) {
|
|
37
|
+
p = pointOnLine(res.point, path[i - 1], p);
|
|
38
|
+
dist = distanceSquare(res.point, p);
|
|
39
|
+
if (dist < res.dist && (res.dist >= res.minDistance || res.pol)) {
|
|
40
|
+
res.dist = dist;
|
|
41
|
+
res.idx = i - 1;
|
|
42
|
+
res.p = p;
|
|
43
|
+
res.pol = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findPointOnPath(point, path) {
|
|
50
|
+
let result;
|
|
51
|
+
if (point && path) {
|
|
52
|
+
result = path.reduce(minDistance, {
|
|
53
|
+
dist: Number.MAX_VALUE,
|
|
54
|
+
point,
|
|
55
|
+
minDistance: 0.5
|
|
56
|
+
});
|
|
57
|
+
if (result.idx !== undefined) {
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function cut(prev, next) {
|
|
64
|
+
const segments = prev.path.segments;
|
|
65
|
+
const point = findPointOnPath(segments[next.segmentIndex].path[0], prev.path);
|
|
66
|
+
if (point) {
|
|
67
|
+
next.path = prev.path.slice(point.idx + 1);
|
|
68
|
+
prev.path = prev.path.slice(0, point.idx + 1);
|
|
69
|
+
next.path.segments = segments;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// divide path into routes
|
|
74
|
+
function partition(path, routes, segments) {
|
|
75
|
+
path.segments = segments;
|
|
76
|
+
routes[0].path = path;
|
|
77
|
+
routes.reduce(cut);
|
|
78
|
+
delete util.last(routes).path.segments;
|
|
79
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const pathType = require("../model").pathType;
|
|
2
|
+
const util = require('./util');
|
|
3
|
+
|
|
4
|
+
module.exports = init;
|
|
5
|
+
|
|
6
|
+
function init(options) {
|
|
7
|
+
options = options || {};
|
|
8
|
+
|
|
9
|
+
const algorithm = options.algorithm || require("vis-why");
|
|
10
|
+
const endPoints = options.endPoints || 25; // how many points keep at ends
|
|
11
|
+
const maxPoints = options.pathPoints || 100; // maximum number of points kept per path
|
|
12
|
+
const ed = maxPoints / endPoints;
|
|
13
|
+
|
|
14
|
+
function simplifyRoute(result, route) {
|
|
15
|
+
let i;
|
|
16
|
+
let seg;
|
|
17
|
+
let first = [];
|
|
18
|
+
let last = [];
|
|
19
|
+
const path = [];
|
|
20
|
+
const type = result.type;
|
|
21
|
+
const segments = result.segments;
|
|
22
|
+
let endDistance = result.endDistance;
|
|
23
|
+
|
|
24
|
+
if (endDistance > route.distance / ed) {
|
|
25
|
+
endDistance = 0;
|
|
26
|
+
}
|
|
27
|
+
last.distance = 0;
|
|
28
|
+
if (endDistance) {
|
|
29
|
+
for (i = result.segmentIndex - 1; i >= route.segmentIndex; i -= 1) {
|
|
30
|
+
seg = segments[i];
|
|
31
|
+
last.distance += seg.distance;
|
|
32
|
+
if (last.distance >= endDistance) {
|
|
33
|
+
last.split = util.indexAt(seg.path, last.distance - endDistance);
|
|
34
|
+
Array.prototype.unshift.apply(last, seg.path.slice(last.split));
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
result.segmentIndex -= 1;
|
|
38
|
+
Array.prototype.unshift.apply(last, seg.path);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
first.distance = 0;
|
|
42
|
+
i = route.segmentIndex;
|
|
43
|
+
if (endDistance) {
|
|
44
|
+
for (; i < result.segmentIndex; i += 1) {
|
|
45
|
+
seg = segments[i];
|
|
46
|
+
first.distance += seg.distance;
|
|
47
|
+
if (first.distance >= endDistance) {
|
|
48
|
+
first.split = util.indexAt(seg.path, endDistance - first.distance + seg.distance);
|
|
49
|
+
util.concat(first, seg.path.slice(0, first.split));
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
util.concat(first, seg.path);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (first.split) {
|
|
56
|
+
util.concat(path, segments[i].path.slice(first.split,
|
|
57
|
+
(i === result.segmentIndex - 1 && last.split) ? last.split : undefined));
|
|
58
|
+
i += 1;
|
|
59
|
+
}
|
|
60
|
+
for (; i < (last.split ? result.segmentIndex - 1 : result.segmentIndex); i += 1) {
|
|
61
|
+
util.concat(path, segments[i].path);
|
|
62
|
+
}
|
|
63
|
+
if (last.split && i < result.segmentIndex) {
|
|
64
|
+
util.concat(path, segments[i].path.slice(0, last.split));
|
|
65
|
+
}
|
|
66
|
+
result.segmentIndex = route.segmentIndex;
|
|
67
|
+
if (type === pathType.full || (first.length + path.length + last.length) <= maxPoints) {
|
|
68
|
+
if (endDistance) {
|
|
69
|
+
route.path = util.concat(util.concat(first, path), last);
|
|
70
|
+
} else {
|
|
71
|
+
route.path = path;
|
|
72
|
+
}
|
|
73
|
+
} else if (endDistance) {
|
|
74
|
+
first = algorithm(first, endPoints);
|
|
75
|
+
last = algorithm(last, endPoints);
|
|
76
|
+
route.path = util.concat(util.concat(first, algorithm(path, maxPoints - first.length - last.length)), last);
|
|
77
|
+
} else {
|
|
78
|
+
route.path = algorithm(path, maxPoints);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function simplify(type, endDistance, routes, segments) {
|
|
84
|
+
if (type === pathType.none) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
routes.reduceRight(simplifyRoute, {
|
|
88
|
+
type,
|
|
89
|
+
segments,
|
|
90
|
+
segmentIndex: segments.length,
|
|
91
|
+
endDistance: endDistance || 2000
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return simplify;
|
|
96
|
+
}
|