@wemap/osm 4.0.15 → 5.0.2

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.
Files changed (32) hide show
  1. package/assets/itinerary-grenoble-otp-1.json +1536 -0
  2. package/assets/itinerary-grenoble-otp-2.json +1092 -0
  3. package/assets/itinerary-montpellier-outdoor-without-steps.json +110 -1
  4. package/assets/itinerary-montpellier-outdoor.json +513 -1
  5. package/index.js +21 -2
  6. package/package.json +6 -5
  7. package/src/routers/Itinerary.js +185 -0
  8. package/src/routers/Itinerary.type.spec.js +104 -0
  9. package/src/routers/Leg.js +101 -0
  10. package/src/routers/LevelChange.js +65 -0
  11. package/src/routers/RouterResponse.js +19 -0
  12. package/src/routers/RouterResponse.type.spec.js +24 -0
  13. package/src/routers/Step.js +113 -0
  14. package/src/routers/Utils.js +22 -0
  15. package/src/routers/custom/OsmNetworkUtils.js +194 -0
  16. package/src/{network → routers/custom}/OsmNetworkUtils.spec.js +13 -34
  17. package/src/routers/custom/OsmRouter.js +27 -0
  18. package/src/routers/custom/OsmRouter.spec.js +222 -0
  19. package/src/routers/custom/OsmRouterOptions.js +33 -0
  20. package/src/routers/custom/OsmRouterUtils.js +46 -0
  21. package/src/routers/custom/StepsGeneration.js +104 -0
  22. package/src/routers/info/ItineraryInfo.js +34 -0
  23. package/src/routers/info/ItineraryInfoManager.js +142 -0
  24. package/src/routers/info/ItineraryInfoManager.spec.js +54 -0
  25. package/src/routers/osrm/OsrmUtils.js +273 -0
  26. package/src/routers/osrm/OsrmUtils.spec.js +431 -0
  27. package/src/routers/otp/OtpUtils.js +172 -0
  28. package/src/routers/otp/OtpUtils.spec.js +97 -0
  29. package/src/network/OsmNetworkUtils.js +0 -168
  30. package/src/network/OsmRouter.spec.js +0 -174
  31. package/src/osrm/OsrmUtils.js +0 -284
  32. package/src/osrm/OsrmUtils.spec.js +0 -384
@@ -0,0 +1,431 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ import { Coordinates } from '@wemap/geo';
8
+ // import { verifyCoherence } from '@wemap/geo/tests/CommonTest.js';
9
+
10
+ // import OsmParser from '../../model/OsmParser.js';
11
+ // import OsmNetworkUtils from '../../network/OsmNetworkUtils.js';
12
+ import {
13
+ // itineraryToOsrmJson,
14
+ // jsonToCoordinates,
15
+ // createItineraryFromJson,
16
+ // getModifierFromAngle,
17
+ // noRouteFoundJson,
18
+ createRouterResponseFromJson
19
+ } from './OsrmUtils.js';
20
+
21
+ import { verifyRouterResponseData } from '../RouterResponse.type.spec.js';
22
+
23
+ const { expect } = chai;
24
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
25
+
26
+
27
+ // const load = fileName => {
28
+ // const filePath = path.resolve(__dirname, '../../../assets/' + fileName);
29
+ // const osmXmlString = fs.readFileSync(filePath, 'utf8');
30
+ // const model = OsmParser.parseOsmXmlString(osmXmlString);
31
+ // const network = OsmNetworkUtils.createNetworkFromOsmModel(model);
32
+ // const router = new GraphRouter(network);
33
+ // return {
34
+ // network,
35
+ // router
36
+ // };
37
+ // };
38
+
39
+ // let wemapIndoorItinerary, wemapIndoorJsonItinerary, wemapIndoorStart, wemapIndoorEnd;
40
+ // let wemapOutdoorItinerary, wemapOutdoorJsonItinerary, wemapOutdoorStart, wemapOutdoorEnd;
41
+
42
+ // describe('OsrmUtils - itineraryToOsrmJson', () => {
43
+
44
+ // it('With levels (Bureaux Wemap)', () => {
45
+
46
+ // const {
47
+ // network, router
48
+ // } = load('bureaux-wemap-montpellier-network.osm');
49
+
50
+ // wemapIndoorStart = new Coordinates(43.6092754, 3.8842306, null, new Level(2));
51
+ // wemapIndoorEnd = new Coordinates(43.6092602, 3.8842669, null, new Level(1));
52
+ // const itinerary = router.getShortestPath(wemapIndoorStart, wemapIndoorEnd);
53
+ // wemapIndoorItinerary = itinerary;
54
+
55
+ // const osrmJson = itineraryToOsrmJson(itinerary);
56
+ // wemapIndoorJsonItinerary = osrmJson;
57
+ // const itinerarySteps = itinerary.steps;
58
+
59
+ // const {
60
+ // geometry, legs
61
+ // } = osrmJson.routes[0];
62
+
63
+ // /**
64
+ // * Verify if JSON geometry corresponds to Itinerary points
65
+ // */
66
+ // const { coordinates } = geometry;
67
+ // expect(coordinates.length).equal(11);
68
+ // for (let i = 1; i < 11; i++) {
69
+ // expect(
70
+ // network.getNodeByName('p' + (i + 6)).coords.equalsTo(
71
+ // jsonToCoordinates(coordinates[i]))
72
+ // ).true;
73
+ // }
74
+
75
+ // /**
76
+ // * Verify location, type and modifier of each step
77
+ // */
78
+ // const { steps } = legs[0];
79
+ // for (let i = 0; i < steps.length; i++) {
80
+ // const {
81
+ // location, type
82
+ // } = steps[i].maneuver;
83
+ // expect(itinerarySteps[i].node.coords.equalsTo(jsonToCoordinates(location))).true;
84
+
85
+ // let expectedType;
86
+ // switch (i) {
87
+ // case 0:
88
+ // expectedType = 'depart';
89
+ // break;
90
+ // case steps.length - 1:
91
+ // expectedType = 'arrive';
92
+ // break;
93
+ // default:
94
+ // expectedType = 'turn';
95
+ // }
96
+ // expect(type).equals(expectedType);
97
+ // }
98
+
99
+ // // The first modifier is not mandatory by OSRM.
100
+ // expect(steps[0].maneuver.modifier).equals('left');
101
+ // expect(steps[0].distance).be.closeTo(2.33, 0.01);
102
+ // expect(steps[0].maneuver.bearing_after).be.closeTo(11.90, 0.01);
103
+
104
+ // expect(steps[1].maneuver.modifier).equals('right');
105
+ // expect(steps[1].distance).be.closeTo(1.76, 0.01);
106
+ // expect(steps[1].maneuver.bearing_after).be.closeTo(56.90, 0.01);
107
+
108
+ // expect(steps[2].maneuver.modifier).equals('left');
109
+ // expect(steps[2].distance).be.closeTo(1.15, 0.01);
110
+ // expect(steps[2].maneuver.bearing_after).be.closeTo(11.90, 0.01);
111
+
112
+ // expect(steps[3].maneuver.modifier).equals('left');
113
+ // expect(steps[3].distance).be.closeTo(5.95, 0.01);
114
+ // expect(steps[3].maneuver.bearing_after).be.closeTo(281.93, 0.01);
115
+
116
+ // expect(steps[4].maneuver.modifier).equals('right');
117
+ // expect(steps[4].distance).be.closeTo(10.71, 0.01);
118
+ // expect(steps[4].maneuver.bearing_after).be.closeTo(12.22, 0.01);
119
+ // expect(steps[4].levelChange).is.not.null;
120
+ // expect(steps[4].levelChange.direction).equals('down');
121
+ // expect(steps[4].levelChange.difference).equals(-1);
122
+ // expect(steps[4].levelChange.type).equals('stairs');
123
+
124
+ // expect(steps[5].maneuver.modifier).equals('left');
125
+ // expect(steps[5].distance).be.closeTo(4.23, 0.01);
126
+ // expect(steps[5].maneuver.bearing_after).be.closeTo(102.00, 0.01);
127
+
128
+ // expect(steps[6].maneuver.modifier).equals('right');
129
+ // expect(steps[6].distance).be.closeTo(5.03, 0.01);
130
+ // expect(steps[6].maneuver.bearing_after).be.closeTo(191.75, 0.01);
131
+
132
+ // expect(steps[7].maneuver.modifier).equals('left');
133
+ // expect(steps[7].distance).be.closeTo(0, 0.01);
134
+ // expect(steps[7].maneuver.bearing_after).be.closeTo(0, 0.01);
135
+
136
+ // });
137
+
138
+ // it('Without levels', () => {
139
+
140
+ // const {
141
+ // network, router
142
+ // } = load('network-simple.osm');
143
+
144
+ // const start = network.getNodeByName('p1');
145
+ // const end = network.getNodeByName('p3');
146
+
147
+ // const itinerary = router.getShortestPath(start, end);
148
+
149
+ // wemapOutdoorJsonItinerary = itineraryToOsrmJson(itinerary);
150
+
151
+ // wemapOutdoorStart = start.coords;
152
+ // wemapOutdoorEnd = end.coords;
153
+ // wemapOutdoorItinerary = itinerary;
154
+ // });
155
+
156
+ // it('Itinerary with modifiers', () => {
157
+
158
+ // const {
159
+ // network, router
160
+ // } = load('network-with-modifiers.osm');
161
+
162
+ // const start = network.getNodeByName('start');
163
+ // const end = network.getNodeByName('end');
164
+
165
+ // const itinerary = router.getShortestPath(start, end);
166
+ // expect(() => itineraryToOsrmJson(itinerary)).not.throw(Error);
167
+
168
+ // });
169
+
170
+ // });
171
+
172
+
173
+ // describe('OsrmUtils - createItineraryFromJson', () => {
174
+
175
+
176
+ // it('Without levels (Montpellier outdoor)', () => {
177
+
178
+ // const filePath = path.resolve(__dirname, '../../../assets/itinerary-montpellier-outdoor.json');
179
+ // const fileString = fs.readFileSync(filePath, 'utf8');
180
+
181
+ // const json = JSON.parse(fileString);
182
+
183
+ // const start = new Coordinates(43.6007871, 3.8757218000000004);
184
+ // const end = new Coordinates(43.598877, 3.873866);
185
+
186
+ // const itinerary = createItineraryFromJson(json, start, end);
187
+
188
+ // expect(json.routes[0].geometry.coordinates.length).equal(itinerary.nodes.length);
189
+ // expect(itinerary.nodes.length).equal(itinerary.edges.length + 1);
190
+ // expect(itinerary.nodes.length).equal(19);
191
+
192
+ // const { steps } = itinerary;
193
+ // expect(steps.length).equal(5);
194
+
195
+ // let step;
196
+ // step = steps[0];
197
+ // expect(Coordinates.equalsTo(step.location, new Coordinates(43.600777, 3.875607))).true;
198
+ // expect(getModifierFromAngle(step.angle)).equals('left');
199
+ // expect(step.nodes.length).equals(6);
200
+ // expect(step.edges.length).equals(5);
201
+ // expect(step.firstStep).true;
202
+ // expect(step.lastStep).false;
203
+ // expect(step.name).equals('');
204
+
205
+ // step = steps[1];
206
+ // expect(Coordinates.equalsTo(step.location, new Coordinates(43.599881, 3.876396))).true;
207
+ // expect(getModifierFromAngle(step.angle)).equals('right');
208
+ // expect(step.nodes.length).equals(12);
209
+ // expect(step.edges.length).equals(11);
210
+ // expect(step.firstStep).false;
211
+ // expect(step.lastStep).false;
212
+ // expect(step.name).equals('Boulevard Vieussens');
213
+
214
+ // step = steps[2];
215
+ // expect(Coordinates.equalsTo(step.location, new Coordinates(43.599577, 3.874655))).true;
216
+ // expect(getModifierFromAngle(step.angle)).equals('left');
217
+ // expect(step.nodes.length).equals(2);
218
+ // expect(step.edges.length).equals(1);
219
+ // expect(step.firstStep).false;
220
+ // expect(step.lastStep).false;
221
+ // expect(step.name).equals('Impasse Bizeray');
222
+
223
+ // step = steps[3];
224
+ // expect(Coordinates.equalsTo(step.location, new Coordinates(43.599063, 3.874623))).true;
225
+ // expect(getModifierFromAngle(step.angle)).equals('right');
226
+ // expect(step.nodes.length).equals(2);
227
+ // expect(step.edges.length).equals(1);
228
+ // expect(step.firstStep).false;
229
+ // expect(step.lastStep).false;
230
+ // expect(step.name).equals('Rue du Docteur Louis Perrier');
231
+
232
+ // step = steps[4];
233
+ // expect(Coordinates.equalsTo(step.location, new Coordinates(43.59906, 3.873865))).true;
234
+ // expect(getModifierFromAngle(step.angle)).equals('left');
235
+ // expect(step.nodes.length).equals(1);
236
+ // expect(step.edges.length).equals(0);
237
+ // expect(step.firstStep).false;
238
+ // expect(step.lastStep).true;
239
+ // expect(step.name).equals('Rue du Docteur Louis Perrier');
240
+
241
+ // expect(itinerary._nextStepsIndexes.length).equals(itinerary.nodes.length);
242
+ // expect(itinerary._nextStepsIndexes[0]).equals(0);
243
+ // for (let i = 1; i <= 5; i++) {
244
+ // expect(itinerary._nextStepsIndexes[i]).equals(1);
245
+ // }
246
+ // for (let i = 6; i <= 16; i++) {
247
+ // expect(itinerary._nextStepsIndexes[i]).equals(2);
248
+ // }
249
+ // expect(itinerary._nextStepsIndexes[17]).equals(3);
250
+ // expect(itinerary._nextStepsIndexes[18]).equals(4);
251
+ // });
252
+
253
+
254
+ // it('Without levels (from itineraryToOsrmJson)', () => {
255
+
256
+ // const itinerary = createItineraryFromJson(wemapOutdoorJsonItinerary,
257
+ // wemapOutdoorStart, wemapOutdoorEnd);
258
+
259
+ // expect(itinerary.nodes.length).equal(wemapOutdoorItinerary.nodes.length);
260
+ // for (let i = 0; i < itinerary.nodes.length; i++) {
261
+ // const itineraryNode = itinerary.nodes[i];
262
+ // const wemapItineraryNode = wemapOutdoorItinerary.nodes[i];
263
+ // expect(itineraryNode.coords.equalsTo(wemapItineraryNode.coords)).true;
264
+ // }
265
+
266
+ // expect(itinerary.edges.length).equal(wemapOutdoorItinerary.edges.length);
267
+ // for (let i = 0; i < itinerary.edges.length; i++) {
268
+ // const itineraryEdge = itinerary.edges[i];
269
+ // const wemapItineraryEdge = wemapOutdoorItinerary.edges[i];
270
+ // const firstNode = wemapItineraryEdge[wemapOutdoorItinerary._edgesDirectionReversed[i] ? 'node2' : 'node1'];
271
+ // const secondNode = wemapItineraryEdge[wemapOutdoorItinerary._edgesDirectionReversed[i] ? 'node1' : 'node2'];
272
+ // expect(itineraryEdge.node1.coords.equalsTo(firstNode.coords)).true;
273
+ // expect(itineraryEdge.node2.coords.equalsTo(secondNode.coords)).true;
274
+ // expect(Level.equalsTo(itineraryEdge.level, wemapItineraryEdge.level)).true;
275
+ // }
276
+
277
+ // });
278
+
279
+ // it('Without steps (Montpellier outdoor)', () => {
280
+
281
+ // const filePath = path.resolve(__dirname,
282
+ // '../../../assets/itinerary-montpellier-outdoor-without-steps.json');
283
+ // const fileString = fs.readFileSync(filePath, 'utf8');
284
+
285
+ // const json = JSON.parse(fileString);
286
+
287
+ // const start = new Coordinates(43.6007871, 3.8757218000000004);
288
+ // const end = new Coordinates(43.598877, 3.873866);
289
+
290
+ // const itinerary = createItineraryFromJson(json, start, end);
291
+
292
+ // expect(json.routes[0].geometry.coordinates.length).equal(itinerary.nodes.length);
293
+ // expect(itinerary.nodes.length).equal(itinerary.edges.length + 1);
294
+ // expect(itinerary.nodes.length).equal(19);
295
+
296
+ // const { steps } = itinerary;
297
+ // expect(steps.length).equal(7);
298
+ // });
299
+
300
+
301
+ // it('With levels (from itineraryToOsrmJson)', () => {
302
+
303
+ // const itinerary = createItineraryFromJson(wemapIndoorJsonItinerary,
304
+ // wemapIndoorStart, wemapIndoorEnd);
305
+
306
+ // expect(itinerary.nodes.length).equal(wemapIndoorItinerary.nodes.length);
307
+ // for (let i = 0; i < itinerary.nodes.length; i++) {
308
+ // const itineraryNode = itinerary.nodes[i];
309
+ // const wemapItineraryNode = wemapIndoorItinerary.nodes[i];
310
+ // expect(itineraryNode.coords.equalsTo(wemapItineraryNode.coords)).true;
311
+ // }
312
+
313
+ // expect(itinerary.edges.length).equal(wemapIndoorItinerary.edges.length);
314
+ // for (let i = 0; i < itinerary.edges.length; i++) {
315
+ // const itineraryEdge = itinerary.edges[i];
316
+ // const wemapItineraryEdge = wemapIndoorItinerary.edges[i];
317
+ // const firstNode = wemapItineraryEdge[wemapIndoorItinerary._edgesDirectionReversed[i] ? 'node2' : 'node1'];
318
+ // const secondNode = wemapItineraryEdge[wemapIndoorItinerary._edgesDirectionReversed[i] ? 'node1' : 'node2'];
319
+ // expect(itineraryEdge.node1.coords.equalsTo(firstNode.coords)).true;
320
+ // expect(itineraryEdge.node2.coords.equalsTo(secondNode.coords)).true;
321
+ // expect(Level.equalsTo(itineraryEdge.level, wemapItineraryEdge.level)).true;
322
+ // }
323
+
324
+ // });
325
+
326
+ // it('Errored (Montpellier outdoor)', () => {
327
+
328
+ // const filePath = path.resolve(__dirname,
329
+ // '../../../assets/itinerary-with-duplicate-nodes.json');
330
+ // const fileString = fs.readFileSync(filePath, 'utf8');
331
+
332
+ // const json = JSON.parse(fileString);
333
+
334
+ // const start = new Coordinates(44.810569099999995, 4.9503924999999995);
335
+ // const end = new Coordinates(44.302673, 4.556377);
336
+
337
+ // const itinerary = createItineraryFromJson(json, start, end);
338
+ // itinerary.edges.forEach(edge => {
339
+ // expect(edge).instanceOf(Edge);
340
+ // });
341
+ // itinerary.steps.forEach(step => {
342
+ // step.edges.forEach(edge => {
343
+ // expect(edge).instanceOf(Edge);
344
+ // });
345
+ // });
346
+ // });
347
+
348
+ // });
349
+
350
+ // describe('OsrmUtils - itineraryToOsrmJson - createItineraryFromJson - way/node', () => {
351
+
352
+ // it('With levels (Bureaux Wemap)', () => {
353
+
354
+ // const { router } = load('bureaux-wemap-montpellier-network.osm');
355
+
356
+ // const start = new Coordinates(43.6092754, 3.8842306, null, new Level(2));
357
+ // const end = new Coordinates(43.60949854, 3.88452048, null, new Level(0));
358
+ // const itinerary = router.getShortestPath(start, end);
359
+
360
+ // const osrmJson = itineraryToOsrmJson(itinerary);
361
+ // const jsonSteps = osrmJson.routes[0].legs[0].steps;
362
+
363
+ // expect(jsonSteps[0].name).equals('w2');
364
+ // expect(jsonSteps[4].name).equals('p19');
365
+ // expect(jsonSteps[6].name).equals('p21');
366
+
367
+ // const itineraryBis = createItineraryFromJson(osrmJson, start, end);
368
+ // verifyCoherence(itineraryBis);
369
+
370
+ // expect(itineraryBis.nodes.length).equals(itinerary.nodes.length);
371
+ // expect(itineraryBis.edges.length).equals(itinerary.edges.length);
372
+ // expect(itineraryBis.steps.length).equals(itinerary.steps.length);
373
+ // });
374
+ // });
375
+
376
+ // describe('OsrmUtils - Output JSON', () => {
377
+
378
+ // it('noRouteFoundJson', () => {
379
+ // expect(noRouteFoundJson('foo')).deep.equals({
380
+ // 'code': 'NoRoute',
381
+ // 'message': 'foo'
382
+ // });
383
+ // });
384
+
385
+ // });
386
+
387
+
388
+ describe('OsrmUtils - createRouterResponseFromJson', () => {
389
+
390
+ it('RouterResponse - 1', () => {
391
+
392
+ const filePath = path.resolve(__dirname, '../../../assets/itinerary-montpellier-outdoor.json');
393
+ const fileString = fs.readFileSync(filePath, 'utf8');
394
+ const json = JSON.parse(fileString);
395
+
396
+ const from = new Coordinates(43.6007871, 3.8757218000000004);
397
+ const to = new Coordinates(43.598877, 3.873866);
398
+ const routerResponse = createRouterResponseFromJson(json, from, to);
399
+ verifyRouterResponseData(routerResponse);
400
+
401
+ expect(routerResponse.routerName).equal('osrm');
402
+ expect(routerResponse.itineraries.length).equal(1);
403
+ expect(routerResponse.from.equalsTo(from)).true;
404
+ expect(routerResponse.to.equalsTo(to)).true;
405
+
406
+ const itinerary1 = routerResponse.itineraries[0];
407
+ expect(itinerary1.distance).equal(399.7);
408
+ expect(itinerary1.duration).equal(287.8);
409
+ expect(itinerary1.startTime).is.null;
410
+ expect(itinerary1.endTime).is.null;
411
+ expect(itinerary1.legs.length).equal(1);
412
+ expect(itinerary1.coords.length).equal(19);
413
+
414
+ const itinerary1leg1 = itinerary1.legs[0];
415
+ expect(itinerary1leg1.startTime).is.null;
416
+ expect(itinerary1leg1.endTime).is.null;
417
+ expect(itinerary1leg1.distance).equal(399.7);
418
+ expect(itinerary1leg1.duration).equal(287.8);
419
+ expect(itinerary1leg1.mode).equal('WALK');
420
+ expect(itinerary1leg1.transportInfo).is.null;
421
+ expect(itinerary1leg1.from.name).is.null;
422
+ expect(itinerary1leg1.from.coords.equalsTo(new Coordinates(43.600777, 3.875607))).true;
423
+ expect(itinerary1leg1.to.name).is.null;
424
+ expect(itinerary1leg1.to.coords.equalsTo(new Coordinates(43.59906, 3.873865))).true;
425
+ expect(itinerary1leg1.coords.length).equal(19);
426
+ expect(itinerary1leg1.coords).deep.equal(itinerary1.coords);
427
+
428
+ });
429
+
430
+ });
431
+
@@ -0,0 +1,172 @@
1
+ /* eslint-disable max-statements */
2
+ import Polyline from '@mapbox/polyline';
3
+
4
+ import { Coordinates } from '@wemap/geo';
5
+ import { diffAngle } from '@wemap/maths';
6
+
7
+ import Itinerary from '../Itinerary.js';
8
+ import Leg from '../Leg.js';
9
+ import RouterResponse from '../RouterResponse.js';
10
+ import Step from '../Step.js';
11
+ import { generateStepsNumbers } from '../Utils.js';
12
+
13
+ /**
14
+ * @param {object} json
15
+ * @returns {Coordinates}
16
+ */
17
+ export function jsonToCoordinates(json) {
18
+ return new Coordinates(json.lat, json.lon);
19
+ }
20
+
21
+ /**
22
+ * @param {object} jsonSteps
23
+ * @param {Coordinates[]} legCoords
24
+ * @param {Coordinates} itineraryStart
25
+ * @param {Coordinates} itineraryEnd
26
+ * @returns {Step[]}
27
+ */
28
+ function parseJsonSteps(jsonSteps, legCoords, itineraryStart) {
29
+
30
+ if (!jsonSteps) {
31
+ return [];
32
+ }
33
+
34
+ return jsonSteps.map(jsonStep => {
35
+
36
+ const step = new Step();
37
+ const stepCoords = jsonToCoordinates(jsonStep);
38
+
39
+ // OTP step does not have the same coordinates than a point in legCoords.
40
+ // That is why we look for the closest point.
41
+ const distances = legCoords.map(coords => coords.distanceTo(stepCoords));
42
+ const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
43
+ if (idStepCoordsInLeg < 0) {
44
+ throw new Error('OTP Parser: Cannot find closest step');
45
+ }
46
+ step.coords = legCoords[idStepCoordsInLeg];
47
+
48
+ step.previousBearing = idStepCoordsInLeg === 0
49
+ ? itineraryStart.bearingTo(step.coords)
50
+ : legCoords[idStepCoordsInLeg - 1].bearingTo(legCoords[idStepCoordsInLeg]);
51
+ step.nextBearing = idStepCoordsInLeg === legCoords.length - 1
52
+ ? 0
53
+ : legCoords[idStepCoordsInLeg].bearingTo(legCoords[idStepCoordsInLeg + 1]);
54
+
55
+ step.angle = diffAngle(step.previousBearing, step.nextBearing + Math.PI);
56
+
57
+ step.name = jsonStep.streetName;
58
+ step.levelChange = null;
59
+
60
+ step.distance = jsonStep.distance;
61
+
62
+ return step;
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Generate multi itineraries from OTP JSON
68
+ * @param {object} json JSON file provided by OTP.
69
+ * @returns {?RouterResponse}
70
+ */
71
+ export function createRouterResponseFromJson(json) {
72
+
73
+ const { plan: jsonPlan } = json;
74
+
75
+ if (!jsonPlan) {
76
+ return null;
77
+ }
78
+
79
+ const routerResponse = new RouterResponse();
80
+ routerResponse.routerName = 'otp';
81
+
82
+ routerResponse.from = jsonToCoordinates(jsonPlan.from);
83
+ routerResponse.to = jsonToCoordinates(jsonPlan.to);
84
+
85
+ for (const jsonItinerary of jsonPlan.itineraries) {
86
+
87
+ const itinerary = new Itinerary();
88
+
89
+ itinerary.duration = jsonItinerary.duration;
90
+ itinerary.startTime = jsonItinerary.startTime;
91
+ itinerary.endTime = jsonItinerary.endTime;
92
+ itinerary.from = routerResponse.from;
93
+ itinerary.to = routerResponse.to;
94
+
95
+ routerResponse.itineraries.push(itinerary);
96
+
97
+ for (const jsonLeg of jsonItinerary.legs) {
98
+
99
+ const leg = new Leg();
100
+
101
+ leg.mode = jsonLeg.mode;
102
+ leg.duration = jsonLeg.duration;
103
+ leg.startTime = jsonLeg.startTime;
104
+ leg.endTime = jsonLeg.endTime;
105
+ leg.from = {
106
+ name: jsonLeg.from.name,
107
+ coords: jsonToCoordinates(jsonLeg.from)
108
+ };
109
+ leg.to = {
110
+ name: jsonLeg.to.name,
111
+ coords: jsonToCoordinates(jsonLeg.to)
112
+ };
113
+ leg.coords = Polyline.decode(jsonLeg.legGeometry.points).map(([lat, lon]) => new Coordinates(lat, lon));
114
+
115
+ if (leg.mode === 'BUS' || leg.mode === 'TRAM') {
116
+ leg.transportInfo = {
117
+ name: jsonLeg.route,
118
+ routeColor: jsonLeg.routeColor,
119
+ routeTextColor: jsonLeg.routeTextColor,
120
+ directionName: jsonLeg.headsign
121
+ };
122
+ }
123
+
124
+ // jsonLeg.distance is not reliable when compared to the array of leg coords.
125
+ // leg.distance = jsonLeg.distance;
126
+ leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
127
+ if (idx === 0) {
128
+ return acc;
129
+ }
130
+ return acc + arr[idx - 1].distanceTo(coords);
131
+ }, 0);
132
+
133
+ let previousPoint;
134
+ if (itinerary.legs.length === 0) {
135
+ previousPoint = itinerary.from;
136
+ } else {
137
+ const lastLeg = itinerary.legs[itinerary.legs.length - 1];
138
+ previousPoint = lastLeg.coords[lastLeg.coords.length - 1];
139
+ }
140
+
141
+ leg.steps = parseJsonSteps(
142
+ jsonLeg.steps,
143
+ leg.coords,
144
+ previousPoint
145
+ );
146
+ // itinerary.coords.push(...leg.coords);
147
+ itinerary.legs.push(leg);
148
+
149
+ }
150
+
151
+ // Remove duplicates
152
+ // itinerary.coords = itinerary.coords.reduce((acc, val) => {
153
+ // if (acc.length === 0 || !acc[acc.length - 1].equalsTo(val)) {
154
+ // acc.push(val);
155
+ // }
156
+ // return acc;
157
+ // }, []);
158
+
159
+ itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
160
+ if (idx === 0) {
161
+ return acc;
162
+ }
163
+ return acc + arr[idx - 1].distanceTo(coords);
164
+ }, 0);
165
+
166
+ generateStepsNumbers(itinerary);
167
+ }
168
+
169
+ return routerResponse;
170
+ }
171
+
172
+