@wemap/routers 6.2.2 → 7.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.
Files changed (57) hide show
  1. package/assets/biocbon-bergere-rdc-network.osm +163 -0
  2. package/assets/gare-de-lest-network-pp-bounds.osm +1615 -0
  3. package/dist/wemap-routers.es.js +1811 -695
  4. package/dist/wemap-routers.es.js.map +1 -1
  5. package/index.js +13 -5
  6. package/package.json +9 -6
  7. package/src/Constants.js +4 -2
  8. package/src/ItineraryInfoManager.spec.js +2 -2
  9. package/src/Utils.js +0 -77
  10. package/src/model/Itinerary.js +41 -5
  11. package/src/model/Itinerary.spec.js +91 -0
  12. package/src/model/Itinerary.type.spec.js +3 -78
  13. package/src/model/Leg.js +89 -19
  14. package/src/model/Leg.spec.js +110 -0
  15. package/src/model/Leg.type.spec.js +48 -0
  16. package/src/model/LevelChange.js +14 -24
  17. package/src/model/LevelChange.spec.js +78 -0
  18. package/src/model/LevelChange.type.spec.js +26 -0
  19. package/src/model/RouterResponse.js +70 -1
  20. package/src/model/RouterResponse.spec.js +85 -0
  21. package/src/model/RouterResponse.type.spec.js +7 -4
  22. package/src/model/Step.js +45 -6
  23. package/src/model/Step.spec.js +100 -0
  24. package/src/model/Step.type.spec.js +52 -0
  25. package/src/remote/RemoteRouter.js +31 -0
  26. package/src/remote/RemoteRouterManager.js +84 -0
  27. package/src/remote/RemoteRouterOptions.js +25 -0
  28. package/src/remote/RemoteRouterServerUnreachable.js +10 -0
  29. package/src/remote/RemoteRouterUtils.js +78 -0
  30. package/src/remote/RoutingModeCorrespondanceNotFound.js +18 -0
  31. package/src/remote/cityway/CitywayRemoteRouter.js +386 -0
  32. package/src/{cityway/CitywayUtils.spec.js → remote/cityway/CitywayRemoteRouter.spec.js} +19 -18
  33. package/src/remote/deutsche-bahn/DeutscheBahnRemoteRouter.js +143 -0
  34. package/src/{deutsche-bahn/DeutscheBahnRouterUtils.spec.js → remote/deutsche-bahn/DeutscheBahnRemoteRouter.spec.js} +7 -6
  35. package/src/remote/idfm/IdfmRemoteRouter.js +432 -0
  36. package/src/{idfm/IdfmUtils.spec.js → remote/idfm/IdfmRemoteRouter.spec.js} +7 -6
  37. package/src/remote/idfm/IdfmRemoteRouterTokenError.js +6 -0
  38. package/src/remote/osrm/OsrmRemoteRouter.js +331 -0
  39. package/src/{osrm/OsrmUtils.spec.js → remote/osrm/OsrmRemoteRouter.spec.js} +9 -15
  40. package/src/remote/otp/OtpRemoteRouter.js +222 -0
  41. package/src/{otp/OtpUtils.spec.js → remote/otp/OtpRemoteRouter.spec.js} +10 -9
  42. package/src/remote/wemap-meta/WemapMetaRemoteRouter.js +57 -0
  43. package/src/remote/wemap-meta/WemapMetaRemoteRouter.spec.js +22 -0
  44. package/src/remote/wemap-meta/WemapMetaRemoteRouterOptions.js +36 -0
  45. package/src/remote/wemap-meta/WemapMetaRemoteRouterPayload.js +44 -0
  46. package/src/wemap/WemapRouter.js +6 -0
  47. package/src/wemap/WemapRouterUtils.js +10 -4
  48. package/src/wemap/WemapStepsGeneration.js +36 -9
  49. package/src/wemap-meta/IOMap.js +191 -0
  50. package/src/wemap-meta/WemapMetaRouter.js +314 -0
  51. package/src/wemap-meta/WemapMetaRouter.spec.js +119 -0
  52. package/src/wemap-meta/WemapMetaRouterOptions.js +20 -0
  53. package/src/cityway/CitywayUtils.js +0 -252
  54. package/src/deutsche-bahn/DeutscheBahnRouterUtils.js +0 -91
  55. package/src/idfm/IdfmUtils.js +0 -247
  56. package/src/osrm/OsrmUtils.js +0 -269
  57. package/src/otp/OtpUtils.js +0 -150
@@ -1,8 +1,8 @@
1
- import { GraphUtils, Level, Coordinates, Network, GraphRouterOptions, GraphRouter, GraphEdge, GraphNode, GraphItinerary, MapMatching } from '@wemap/geo';
1
+ import { Coordinates, Network, GraphRouterOptions, GraphRouter, GraphUtils, Level, GraphEdge, GraphNode, GraphItinerary, Utils, MapMatching } from '@wemap/geo';
2
2
  import { OsmWay, OsmNode } from '@wemap/osm';
3
- import { diffAngle, deg2rad, positiveMod, rad2deg } from '@wemap/maths';
4
- import Polyline from '@mapbox/polyline';
3
+ import { deg2rad, diffAngle, positiveMod, rad2deg } from '@wemap/maths';
5
4
  import Logger from '@wemap/logger';
5
+ import Polyline from '@mapbox/polyline';
6
6
 
7
7
  class LevelChange {
8
8
 
@@ -16,28 +16,22 @@ class LevelChange {
16
16
  type = null;
17
17
 
18
18
  /**
19
- * @param {GraphNode<OsmElement>} firstNode
20
- * @param {GraphNode<OsmElement>} secondNode
21
- * @returns {LevelChange}
19
+ * @param {LevelChange} obj1
20
+ * @param {LevelChange} obj2
21
+ * @returns {Boolean}
22
22
  */
23
- static fromTwoNodes(firstNode, secondNode) {
24
-
25
- const levelChange = new LevelChange();
26
-
27
- const edge = GraphUtils.getEdgeByNodes(firstNode.edges, firstNode, secondNode);
28
-
29
- if (edge.builtFrom.isElevator) {
30
- levelChange.type = 'elevator';
31
- } else if (edge.builtFrom.isConveying) {
32
- levelChange.type = 'conveyor';
33
- } else if (edge.builtFrom.areStairs) {
34
- levelChange.type = 'stairs';
35
- }
36
-
37
- levelChange.difference = Level.diff(firstNode.coords.level, secondNode.coords.level);
38
- levelChange.direction = levelChange.difference > 0 ? 'up' : 'down';
23
+ static equalsTo(obj1, obj2) {
24
+ return obj1.difference === obj2.difference
25
+ && obj1.direction === obj2.direction
26
+ && obj1.type === obj2.type;
27
+ }
39
28
 
40
- return levelChange;
29
+ /**
30
+ * @param {LevelChange} obj
31
+ * @returns {Boolean}
32
+ */
33
+ equalsTo(obj) {
34
+ return LevelChange.equalsTo(this, obj);
41
35
  }
42
36
 
43
37
  /**
@@ -108,6 +102,45 @@ class Step {
108
102
  /** @type {!number} */
109
103
  _idCoordsInLeg = null;
110
104
 
105
+ /**
106
+ * @param {Step} obj1
107
+ * @param {Step} obj2
108
+ * @returns {Boolean}
109
+ */
110
+ static equalsTo(obj1, obj2) {
111
+ return obj1.firstStep === obj2.firstStep
112
+ && obj1.lastStep === obj2.lastStep
113
+ && obj1.number === obj2.number
114
+ && obj1.coords.equalsTo(obj2.coords)
115
+ && obj1.angle === obj2.angle
116
+ && obj1.previousBearing === obj2.previousBearing
117
+ && obj1.nextBearing === obj2.nextBearing
118
+ && obj1.distance === obj2.distance
119
+ && obj1.duration === obj2.duration
120
+ && obj1.name === obj2.name
121
+ && (
122
+ obj1.levelChange === obj2.levelChange
123
+ || obj1.levelChange !== null && obj1.levelChange.equalsTo(obj2.levelChange)
124
+ )
125
+ && (
126
+ obj1.extras === obj2.extras
127
+ || (
128
+ obj1.extras !== null
129
+ && obj1.extras.subwayEntrance === obj2.extras.subwayEntrance
130
+ && obj1.extras.subwayEntranceRef === obj2.extras.subwayEntranceRef
131
+ )
132
+ )
133
+ && obj1._idCoordsInLeg === obj2._idCoordsInLeg;
134
+ }
135
+
136
+ /**
137
+ * @param {Step} obj
138
+ * @returns {Boolean}
139
+ */
140
+ equalsTo(obj) {
141
+ return Step.equalsTo(this, obj);
142
+ }
143
+
111
144
  /**
112
145
  * @returns {object}
113
146
  */
@@ -155,31 +188,62 @@ class Step {
155
188
  step.nextBearing = json.nextBearing;
156
189
  step.distance = json.distance;
157
190
  step._idCoordsInLeg = json._idCoordsInLeg;
158
- if (json.firstStep) {
191
+ if (typeof json.firstStep === 'boolean') {
159
192
  step.firstStep = json.firstStep;
160
193
  }
161
- if (json.lastStep) {
194
+ if (typeof json.lastStep === 'boolean') {
162
195
  step.lastStep = json.lastStep;
163
196
  }
164
- if (json.duration) {
197
+ if (typeof json.duration === 'number') {
165
198
  step.duration = json.duration;
166
199
  }
167
- if (json.name) {
200
+ if (typeof json.name === 'string') {
168
201
  step.name = json.name;
169
202
  }
170
- if (json.levelChange) {
203
+ if (typeof json.levelChange === 'object') {
171
204
  step.levelChange = LevelChange.fromJson(json.levelChange);
172
205
  }
173
- if (json.extras) {
206
+ if (typeof json.extras === 'object') {
174
207
  step.extras = json.extras;
175
208
  }
176
209
  return step;
177
210
  }
178
211
  }
179
212
 
213
+ const Constants = {};
214
+
215
+ Constants.ROUTING_MODE = {
216
+ AIRPLANE: 'AIRPLANE',
217
+ BOAT: 'BOAT',
218
+ BIKE: 'BIKE',
219
+ BUS: 'BUS',
220
+ CAR: 'CAR',
221
+ FERRY: 'FERRY',
222
+ FUNICULAR: 'FUNICULAR',
223
+ METRO: 'METRO',
224
+ MOTO: 'MOTO',
225
+ TRAIN: 'TRAIN',
226
+ TAXI: 'TAXI',
227
+ TRAM: 'TRAM',
228
+ WALK: 'WALK',
229
+ MULTI: 'MULTI',
230
+ UNKNOWN: 'UNKNOWN'
231
+ };
232
+
233
+ Constants.PUBLIC_TRANSPORT = [
234
+ Constants.ROUTING_MODE.AIRPLANE,
235
+ Constants.ROUTING_MODE.BOAT,
236
+ Constants.ROUTING_MODE.BUS,
237
+ Constants.ROUTING_MODE.FERRY,
238
+ Constants.ROUTING_MODE.FUNICULAR,
239
+ Constants.ROUTING_MODE.METRO,
240
+ Constants.ROUTING_MODE.TRAIN,
241
+ Constants.ROUTING_MODE.TRAM
242
+ ];
243
+
180
244
  class Leg {
181
245
 
182
- /** @type {!string} can be WALK, BIKE, BUS, TRAM, CAR, FUNICULAR */
246
+ /** @type {!string} can be values in Constants.ROUTING_MODE */
183
247
  mode;
184
248
 
185
249
  /** @type {!number} */
@@ -209,6 +273,10 @@ class Leg {
209
273
  /** @type {?(Step[])} */
210
274
  steps = null;
211
275
 
276
+ isPublicTransport() {
277
+ return Constants.PUBLIC_TRANSPORT.includes(this.mode);
278
+ }
279
+
212
280
  /**
213
281
  * @returns {Network}
214
282
  */
@@ -216,6 +284,72 @@ class Leg {
216
284
  return Network.fromCoordinates([this.coords]);
217
285
  }
218
286
 
287
+
288
+ /**
289
+ * @param {Leg} obj1
290
+ * @param {Leg} obj2
291
+ * @returns {Boolean}
292
+ */
293
+ // eslint-disable-next-line complexity
294
+ static equalsTo(obj1, obj2) {
295
+ const intermediate = obj1.mode === obj2.mode
296
+ && obj1.distance === obj2.distance
297
+ && obj1.duration === obj2.duration
298
+ && obj1.startTime === obj2.startTime
299
+ && obj1.endTime === obj2.endTime
300
+ && obj1.from.name === obj2.from.name
301
+ && obj1.from.coords.equalsTo(obj2.from.coords)
302
+ && obj1.to.name === obj2.to.name
303
+ && obj1.to.coords.equalsTo(obj2.to.coords)
304
+ && obj1.coords.length === obj2.coords.length
305
+ && (
306
+ obj1.steps === obj2.steps
307
+ || obj1.steps.length === obj2.steps.length
308
+ );
309
+
310
+ if (!intermediate) {
311
+ return false;
312
+ }
313
+
314
+ let i;
315
+ for (i = 0; i < obj1.coords.length; i++) {
316
+ if (!obj1.coords[i].equalsTo(obj2.coords[i])) {
317
+ return false;
318
+ }
319
+ }
320
+ if (obj1.steps) {
321
+ for (i = 0; i < obj1.steps.length; i++) {
322
+ if (!obj1.steps[i].equalsTo(obj2.steps[i])) {
323
+ return false;
324
+ }
325
+ }
326
+ }
327
+
328
+ if (obj1.transportInfo !== obj2.transportInfo) {
329
+ if (obj1.transportInfo === null) {
330
+ return false;
331
+ }
332
+ if (
333
+ obj1.transportInfo.name !== obj2.transportInfo.name
334
+ || obj1.transportInfo.routeColor !== obj2.transportInfo.routeColor
335
+ || obj1.transportInfo.routeTextColor !== obj2.transportInfo.routeTextColor
336
+ || obj1.transportInfo.directionName !== obj2.transportInfo.directionName
337
+ ) {
338
+ return false;
339
+ }
340
+ }
341
+
342
+ return true;
343
+ }
344
+
345
+ /**
346
+ * @param {Leg} obj
347
+ * @returns {Boolean}
348
+ */
349
+ equalsTo(obj) {
350
+ return Leg.equalsTo(this, obj);
351
+ }
352
+
219
353
  /**
220
354
  * @returns {object}
221
355
  */
@@ -228,18 +362,18 @@ class Leg {
228
362
  duration: this.duration,
229
363
  coords: this.coords.map(coords => coords.toCompressedJson())
230
364
  };
231
- if (this.from.name) {
232
- output.from.name = this.from.name;
233
- }
234
- if (this.to.name) {
235
- output.to.name = this.to.name;
236
- }
237
365
  if (this.startTime !== null) {
238
366
  output.startTime = this.startTime;
239
367
  }
240
368
  if (this.endTime !== null) {
241
369
  output.endTime = this.endTime;
242
370
  }
371
+ if (this.from.name !== null) {
372
+ output.from.name = this.from.name;
373
+ }
374
+ if (this.to.name !== null) {
375
+ output.to.name = this.to.name;
376
+ }
243
377
  if (this.transportInfo !== null) {
244
378
  output.transportInfo = this.transportInfo;
245
379
  }
@@ -257,27 +391,31 @@ class Leg {
257
391
  static fromJson(json) {
258
392
  const leg = new Leg();
259
393
  leg.mode = json.mode;
260
- leg.from = { coords: Coordinates.fromCompressedJson(json.from.coords) };
261
- leg.to = { coords: Coordinates.fromCompressedJson(json.to.coords) };
262
394
  leg.distance = json.distance;
263
395
  leg.duration = json.duration;
264
- leg.coords = json.coords.map(Coordinates.fromCompressedJson);
265
- if (json.from.name) {
266
- leg.from.name = json.from.name;
267
- }
268
- if (json.to.name) {
269
- leg.to.name = json.to.name;
270
- }
271
- if (json.startTime) {
396
+
397
+ if (typeof json.startTime === 'number') {
272
398
  leg.startTime = json.startTime;
273
399
  }
274
- if (json.endTime) {
400
+ if (typeof json.endTime === 'number') {
275
401
  leg.endTime = json.endTime;
276
402
  }
277
- if (json.transportInfo) {
403
+
404
+ leg.from = {
405
+ coords: Coordinates.fromCompressedJson(json.from.coords),
406
+ name: typeof json.from.name === 'string' ? json.from.name : null
407
+ };
408
+ leg.to = {
409
+ coords: Coordinates.fromCompressedJson(json.to.coords),
410
+ name: typeof json.to.name === 'string' ? json.to.name : null
411
+ };
412
+
413
+ leg.coords = json.coords.map(Coordinates.fromCompressedJson);
414
+
415
+ if (typeof json.transportInfo === 'object') {
278
416
  leg.transportInfo = json.transportInfo;
279
417
  }
280
- if (json.steps) {
418
+ if (typeof json.steps === 'object') {
281
419
  leg.steps = json.steps.map(Step.fromJson);
282
420
  }
283
421
  return leg;
@@ -294,58 +432,6 @@ function getDurationFromLength(length, speed = 5) {
294
432
  return length / (speed * 1000 / 3600);
295
433
  }
296
434
 
297
-
298
- /**
299
- * @param {Itinerary} itinerary
300
- */
301
- function generateStepsMetadata(itinerary) {
302
-
303
- let counter = 1;
304
-
305
- itinerary.legs.forEach((leg, legId) => {
306
- leg.steps.forEach((step, stepId) => {
307
-
308
- if (counter === 1) {
309
- step.firstStep = true;
310
- }
311
- if (legId === itinerary.legs.length - 1
312
- && stepId === leg.steps.length - 1) {
313
- step.lastStep = true;
314
- }
315
-
316
- step.number = counter++;
317
-
318
-
319
- /*
320
- * Generate previousBearing, nextBearing and angle
321
- */
322
-
323
- let coordsBeforeStep;
324
- if (step._idCoordsInLeg > 0) {
325
- coordsBeforeStep = leg.coords[step._idCoordsInLeg - 1];
326
- } else if (legId === 0) {
327
- coordsBeforeStep = itinerary.from;
328
- } else {
329
- coordsBeforeStep = itinerary.legs[legId - 1].to.coords;
330
- }
331
-
332
- let coordsAfterStep;
333
- if (step._idCoordsInLeg !== leg.coords.length - 1) {
334
- coordsAfterStep = leg.coords[step._idCoordsInLeg + 1];
335
- } else if (legId === itinerary.legs.length - 1) {
336
- coordsAfterStep = itinerary.to;
337
- } else {
338
- coordsAfterStep = itinerary.legs[legId + 1].from.coords;
339
- }
340
-
341
- step.previousBearing = coordsBeforeStep.bearingTo(step.coords);
342
- step.nextBearing = step.coords.bearingTo(coordsAfterStep);
343
- step.angle = diffAngle(step.previousBearing, step.nextBearing + Math.PI);
344
-
345
- });
346
- });
347
- }
348
-
349
435
  /* eslint-disable max-statements */
350
436
 
351
437
  /**
@@ -417,14 +503,14 @@ class Itinerary {
417
503
 
418
504
  get mode() {
419
505
  if (!this._mode) {
420
- let isPublicTransport;
421
- let isBicycle;
422
- let isDriving;
506
+ let isPublicTransport = false;
507
+ let isBicycle = false;
508
+ let isDriving = false;
423
509
 
424
510
  this.legs.forEach((leg) => {
425
- isPublicTransport = isPublicTransport || ['BUS', 'FUNICULAR', 'TRAM', 'TRAIN'].includes(leg.mode);
426
- isBicycle = isBicycle || ['BIKE', 'BICYCLE'].includes(leg.mode);
427
- isDriving = isDriving || leg.mode === 'CAR';
511
+ isPublicTransport = isPublicTransport || Constants.PUBLIC_TRANSPORT.includes(leg.mode);
512
+ isBicycle = isBicycle || leg.mode === Constants.ROUTING_MODE.BIKE;
513
+ isDriving = isDriving || leg.mode === Constants.ROUTING_MODE.CAR;
428
514
  });
429
515
 
430
516
  if (isPublicTransport) {
@@ -532,6 +618,42 @@ class Itinerary {
532
618
  return itinerary;
533
619
  }
534
620
 
621
+
622
+ /**
623
+ * @param {Itinerary} obj1
624
+ * @param {Itinerary} obj2
625
+ * @returns {Boolean}
626
+ */
627
+ static equalsTo(obj1, obj2) {
628
+ const intermediate = obj1.from.equalsTo(obj2.from)
629
+ && obj1.to.equalsTo(obj2.to)
630
+ && obj1.distance === obj2.distance
631
+ && obj1.duration === obj2.duration
632
+ && obj1.startTime === obj2.startTime
633
+ && obj1.endTime === obj2.endTime
634
+ && obj1.legs.length === obj2.legs.length;
635
+
636
+ if (!intermediate) {
637
+ return false;
638
+ }
639
+
640
+ for (let i = 0; i < obj1.legs.length; i++) {
641
+ if (!obj1.legs[i].equalsTo(obj2.legs[i])) {
642
+ return false;
643
+ }
644
+ }
645
+
646
+ return true;
647
+ }
648
+
649
+ /**
650
+ * @param {Itinerary} obj
651
+ * @returns {Boolean}
652
+ */
653
+ equalsTo(obj) {
654
+ return Itinerary.equalsTo(this, obj);
655
+ }
656
+
535
657
  /**
536
658
  * @returns {object}
537
659
  */
@@ -564,10 +686,10 @@ class Itinerary {
564
686
  itinerary.distance = json.distance;
565
687
  itinerary.duration = json.duration;
566
688
  itinerary.legs = json.legs.map(Leg.fromJson);
567
- if (json.startTime) {
689
+ if (typeof json.startTime === 'number') {
568
690
  itinerary.startTime = json.startTime;
569
691
  }
570
- if (json.endTime) {
692
+ if (typeof json.endTime === 'number') {
571
693
  itinerary.endTime = json.endTime;
572
694
  }
573
695
  return itinerary;
@@ -576,7 +698,7 @@ class Itinerary {
576
698
 
577
699
  class RouterResponse {
578
700
 
579
- /** @type {!string} */
701
+ /** @type {!string|Array<string>} */
580
702
  routerName;
581
703
 
582
704
  /** @type {!Coordinates} */
@@ -587,6 +709,64 @@ class RouterResponse {
587
709
 
588
710
  /** @type {!(Itinerary[])} */
589
711
  itineraries = [];
712
+
713
+ /**
714
+ * @param {RouterResponse} obj1
715
+ * @param {RouterResponse} obj2
716
+ * @returns {Boolean}
717
+ */
718
+ static equalsTo(obj1, obj2) {
719
+ const intermediate = obj1.routerName === obj2.routerName
720
+ && obj1.from.equalsTo(obj2.from)
721
+ && obj1.to.equalsTo(obj2.to)
722
+ && obj1.itineraries.length === obj2.itineraries.length;
723
+
724
+ if (!intermediate) {
725
+ return false;
726
+ }
727
+
728
+ for (let i = 0; i < obj1.itineraries.length; i++) {
729
+ if (!obj1.itineraries[i].equalsTo(obj2.itineraries[i])) {
730
+ return false;
731
+ }
732
+ }
733
+
734
+ return true;
735
+ }
736
+
737
+ /**
738
+ * @param {RouterResponse} obj
739
+ * @returns {Boolean}
740
+ */
741
+ equalsTo(obj) {
742
+ return RouterResponse.equalsTo(this, obj);
743
+ }
744
+
745
+
746
+ /**
747
+ * @returns {object}
748
+ */
749
+ toJson() {
750
+ return {
751
+ routerName: this.routerName,
752
+ from: this.from.toCompressedJson(),
753
+ to: this.to.toCompressedJson(),
754
+ itineraries: this.itineraries.map(itinerary => itinerary.toJson())
755
+ };
756
+ }
757
+
758
+ /**
759
+ * @param {object} json
760
+ * @returns {RouterResponse}
761
+ */
762
+ static fromJson(json) {
763
+ const routerResponse = new RouterResponse();
764
+ routerResponse.routerName = json.routerName;
765
+ routerResponse.from = Coordinates.fromCompressedJson(json.from);
766
+ routerResponse.to = Coordinates.fromCompressedJson(json.to);
767
+ routerResponse.itineraries = json.itineraries.map(Itinerary.fromJson);
768
+ return routerResponse;
769
+ }
590
770
  }
591
771
 
592
772
  class ItineraryInfo {
@@ -654,6 +834,12 @@ class WemapRouter extends GraphRouter {
654
834
  super(network);
655
835
  }
656
836
 
837
+
838
+ /** @type {string} */
839
+ static get rname() {
840
+ return 'wemap';
841
+ }
842
+
657
843
  /**
658
844
  * @param {Coordinates} start
659
845
  * @param {Coordinates} end
@@ -730,7 +916,7 @@ class WemapStepsGeneration {
730
916
  }
731
917
 
732
918
  if (splitByLevel) {
733
- currentStep.levelChange = LevelChange.fromTwoNodes(node, nextNode);
919
+ currentStep.levelChange = WemapStepsGeneration.levelChangefromTwoNodes(node, nextNode);
734
920
  }
735
921
 
736
922
  steps.push(currentStep);
@@ -746,21 +932,48 @@ class WemapStepsGeneration {
746
932
  }
747
933
 
748
934
  const lastNode = nodes[nodes.length - 1];
749
- const lastStep = new Step();
750
- lastStep.coords = lastNode.coords;
751
- lastStep.number = steps.length + 1;
752
- lastStep.previousBearing = previousBearing;
753
- lastStep.distance = lastNode.coords.distanceTo(end);
935
+
936
+ // Create a last step if end is not on the network
754
937
  if (!Coordinates.equalsTo(lastNode.coords, end)) {
938
+ const lastStep = new Step();
939
+ lastStep.coords = lastNode.coords;
940
+ lastStep.number = steps.length + 1;
941
+ lastStep.previousBearing = previousBearing;
942
+ lastStep.distance = lastNode.coords.distanceTo(end);
755
943
  lastStep.nextBearing = lastNode.coords.bearingTo(end);
756
944
  lastStep.angle = diffAngle(lastStep.previousBearing, lastStep.nextBearing + Math.PI);
945
+ steps.push(lastStep);
757
946
  }
758
- lastStep.lastStep = true;
759
- steps.push(lastStep);
947
+
948
+ steps[steps.length - 1].lastStep = true;
760
949
 
761
950
  return steps;
762
951
  }
763
952
 
953
+ /**
954
+ * @param {GraphNode<OsmElement>} firstNode
955
+ * @param {GraphNode<OsmElement>} secondNode
956
+ * @returns {LevelChange}
957
+ */
958
+ static levelChangefromTwoNodes(firstNode, secondNode) {
959
+
960
+ const levelChange = new LevelChange();
961
+
962
+ const edge = GraphUtils.getEdgeByNodes(firstNode.edges, firstNode, secondNode);
963
+
964
+ if (edge.builtFrom.isElevator) {
965
+ levelChange.type = 'elevator';
966
+ } else if (edge.builtFrom.isConveying) {
967
+ levelChange.type = 'conveyor';
968
+ } else if (edge.builtFrom.areStairs) {
969
+ levelChange.type = 'stairs';
970
+ }
971
+
972
+ levelChange.difference = Level.diff(firstNode.coords.level, secondNode.coords.level);
973
+ levelChange.direction = levelChange.difference > 0 ? 'up' : 'down';
974
+
975
+ return levelChange;
976
+ }
764
977
  }
765
978
 
766
979
  /**
@@ -768,17 +981,21 @@ class WemapStepsGeneration {
768
981
  * @param {string} mode
769
982
  * @returns {Leg}
770
983
  */
771
- function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
984
+ function createLegFromGraphItinerary(graphItinerary,
985
+ mode = Constants.ROUTING_MODE.WALK) {
772
986
 
773
987
  const leg = new Leg();
774
988
 
775
- leg.from = { coords: graphItinerary.start };
776
- leg.to = { coords: graphItinerary.end };
989
+ leg.from = { coords: graphItinerary.start, name: null };
990
+ leg.to = { coords: graphItinerary.end, name: null };
777
991
  leg.coords = graphItinerary.nodes.map(node => node.coords);
778
992
  leg.distance = graphItinerary.edges.reduce((acc, edge) => acc + edge.length, 0);
779
993
  leg.duration = graphItinerary.edgesWeights.reduce((acc, weight) => acc + weight, 0);
780
994
  leg.mode = mode;
781
995
  leg.steps = WemapStepsGeneration.fromGraphItinerary(graphItinerary);
996
+ leg.steps.forEach(step => {
997
+ step._idCoordsInLeg = leg.coords.findIndex(coords => coords === step.coords);
998
+ });
782
999
 
783
1000
  return leg;
784
1001
  }
@@ -788,7 +1005,8 @@ function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
788
1005
  * @param {string} mode
789
1006
  * @returns {Itinerary}
790
1007
  */
791
- function createItineraryFromGraphItinerary(graphItinerary, mode = 'WALK') {
1008
+ function createItineraryFromGraphItinerary(graphItinerary,
1009
+ mode = Constants.ROUTING_MODE.WALK) {
792
1010
 
793
1011
  const leg = createLegFromGraphItinerary(graphItinerary, mode);
794
1012
 
@@ -1010,745 +1228,1643 @@ var WemapNetworkUtils = /*#__PURE__*/Object.freeze({
1010
1228
  createNetworkFromOsmModel: createNetworkFromOsmModel
1011
1229
  });
1012
1230
 
1013
- /* eslint-disable max-statements */
1014
-
1015
1231
  /**
1016
- * @param {Coordinates} coordinates
1017
- * @returns {object}
1232
+ * @param {Itinerary} itinerary
1018
1233
  */
1019
- function coordinatesToJson(coordinates) {
1020
- const output = [coordinates.lng, coordinates.lat];
1021
- if (coordinates.level) {
1022
- output.push(coordinates.level.toString());
1023
- }
1024
- return output;
1025
- }
1234
+ function generateStepsMetadata(itinerary) {
1026
1235
 
1027
- /**
1028
- * @param {object} json
1029
- * @returns {Coordinates}
1030
- */
1031
- function jsonToCoordinates$2(json) {
1032
- const output = new Coordinates(json[1], json[0]);
1033
- if (json.length > 2) {
1034
- output.level = Level.fromString(json[2]);
1035
- }
1036
- return output;
1037
- }
1236
+ let counter = 1;
1038
1237
 
1039
- function nodesToJsonCoords(nodes) {
1040
- return nodes.map(node => coordinatesToJson(node.coords));
1041
- }
1238
+ itinerary.legs.forEach((leg, legId) => {
1239
+ leg.steps.forEach((step, stepId) => {
1042
1240
 
1241
+ if (counter === 1) {
1242
+ step.firstStep = true;
1243
+ }
1244
+ if (legId === itinerary.legs.length - 1
1245
+ && stepId === leg.steps.length - 1) {
1246
+ step.lastStep = true;
1247
+ }
1043
1248
 
1044
- function getModifierFromAngle(_angle) {
1249
+ step.number = counter++;
1045
1250
 
1046
- const angle = positiveMod(rad2deg(_angle), 360);
1047
1251
 
1048
- if (angle > 0 && angle < 60) {
1049
- return 'sharp right';
1050
- }
1051
- if (angle >= 60 && angle < 140) {
1052
- return 'right';
1053
- }
1054
- if (angle >= 140 && angle < 160) {
1055
- return 'slight right';
1056
- }
1057
- if (angle >= 160 && angle <= 200) {
1058
- return 'straight';
1059
- }
1060
- if (angle > 200 && angle <= 220) {
1061
- return 'slight left';
1062
- }
1063
- if (angle > 220 && angle <= 300) {
1064
- return 'left';
1065
- }
1066
- if (angle > 300 && angle < 360) {
1067
- return 'sharp left';
1068
- }
1069
- return 'u turn';
1070
- }
1252
+ /*
1253
+ * Generate previousBearing, nextBearing and angle
1254
+ */
1071
1255
 
1256
+ let coordsBeforeStep;
1257
+ if (step._idCoordsInLeg > 0) {
1258
+ coordsBeforeStep = leg.coords[step._idCoordsInLeg - 1];
1259
+ } else if (legId === 0) {
1260
+ coordsBeforeStep = itinerary.from;
1261
+ } else {
1262
+ coordsBeforeStep = itinerary.legs[legId - 1].to.coords;
1263
+ }
1072
1264
 
1073
- function noRouteFoundJson(message) {
1074
- return {
1075
- 'code': 'NoRoute',
1076
- message
1077
- };
1078
- }
1265
+ let coordsAfterStep;
1266
+ if (step._idCoordsInLeg !== leg.coords.length - 1) {
1267
+ coordsAfterStep = leg.coords[step._idCoordsInLeg + 1];
1268
+ } else if (legId === itinerary.legs.length - 1) {
1269
+ coordsAfterStep = itinerary.to;
1270
+ } else {
1271
+ coordsAfterStep = itinerary.legs[legId + 1].from.coords;
1272
+ }
1079
1273
 
1080
- /**
1081
- * @param {Itinerary} itinerary
1082
- * @returns {object}
1274
+ step.previousBearing = coordsBeforeStep.bearingTo(step.coords);
1275
+ step.nextBearing = step.coords.bearingTo(coordsAfterStep);
1276
+ step.angle = diffAngle(step.previousBearing, step.nextBearing + Math.PI);
1277
+
1278
+ });
1279
+ });
1280
+ }
1281
+
1282
+
1283
+ /**
1284
+ * Create and return a date with a given timezone
1285
+ * @param {number} year
1286
+ * @param {number} month
1287
+ * @param {number} day
1288
+ * @param {number} hours
1289
+ * @param {number} minutes
1290
+ * @param {number} seconds
1291
+ * @param {string} timeZone - timezone name (e.g. 'Europe/Paris')
1083
1292
  */
1084
- function itineraryToOsrmJson(itinerary) {
1293
+ function dateWithTimeZone(year, month, day, hour, minute, second, timeZone = 'Europe/Paris') {
1294
+ const date = new Date(Date.UTC(year, month, day, hour, minute, second));
1295
+
1296
+ const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
1297
+ const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
1298
+ const offset = utcDate.getTime() - tzDate.getTime();
1299
+
1300
+ date.setTime(date.getTime() + offset);
1301
+
1302
+ return date;
1303
+ }
1304
+
1305
+ const stringify = (data) => {
1306
+ return Object.keys(data).map(key => {
1307
+ return `${key}=${data[key]}`;
1308
+ });
1309
+ };
1310
+
1311
+
1312
+ const request = async (url, options = {}) => {
1313
+ const responseType = options.responseType || 'json';
1314
+
1315
+ const fetchOptions = {
1316
+ method: options.method || 'GET',
1317
+ headers: {}
1318
+ // headers: new Headers(options.headers || {})
1319
+ };
1320
+
1321
+ if (options.hasOwnProperty('json')) {
1322
+ fetchOptions.headers.append('Content-Type', 'application/json');
1323
+ fetchOptions.body = JSON.stringify(options.json);
1324
+ } else if (options.hasOwnProperty('data')) {
1325
+ fetchOptions.headers.append('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
1326
+ fetchOptions.body = options.data instanceof URLSearchParams ? options.data : stringify(options.data);
1327
+ }
1328
+
1329
+ return fetch(url, fetchOptions).then(response => {
1330
+ if (responseType === 'arraybuffer') {
1331
+ Logger.ok('Request succeeded for: ' + url);
1332
+ return response.arrayBuffer();
1333
+ } else if (responseType === 'json') {
1334
+ return response.json().then((json) => {
1335
+ Logger.ok('Request succeeded for: ' + url);
1336
+ return json;
1337
+ }).catch(() => {
1338
+ Logger.error('Request failed for: ' + url);
1339
+ throw Error('Request failed for: ' + url);
1340
+ });
1341
+ }
1342
+
1343
+ return null;
1344
+ });
1345
+ };
1346
+
1347
+ class RemoteRouterOptions {
1348
+
1349
+ /** @type {boolean} */
1350
+ useStairs = true;
1351
+
1352
+ }
1353
+
1354
+ class RemoteRouter {
1355
+
1356
+ /**
1357
+ * Get the router name
1358
+ * @type {string} the router name
1359
+ */
1360
+ get rname() {
1361
+ return 'Unknown';
1362
+ }
1363
+
1364
+ /**
1365
+ * @abstract
1366
+ * @param {string} endpointUrl
1367
+ * @param {string} mode see Constants.ITINERARY_MODE
1368
+ * @param {Array<Coordinates>} waypoints
1369
+ * @param {RemoteRouterOptions} options
1370
+ * @returns {!RouterResponse}
1371
+ */
1372
+ // eslint-disable-next-line no-unused-vars
1373
+ async getItineraries(endpointUrl, mode, waypoints, options = new RemoteRouterOptions()) {
1374
+ throw new Error('RemoteRouter "' + this.rname + '" does not @override getItineraries()');
1375
+ }
1376
+
1377
+ }
1378
+
1379
+ class OsrmRoutingModeCorrespondanceNotFound extends Error {
1380
+
1381
+ /**
1382
+ * @param {!string} routingMode
1383
+ */
1384
+ constructor(routingMode) {
1385
+ super(`OSRM routing mode correspondance not found: ${routingMode}`);
1386
+ }
1387
+ }
1388
+
1389
+ /* eslint-disable max-statements */
1390
+
1391
+ /**
1392
+ * Input mode correspondance
1393
+ */
1394
+ const inputModeCorrespondance$2 = new Map();
1395
+ inputModeCorrespondance$2.set(Constants.ROUTING_MODE.CAR, 'driving');
1396
+ inputModeCorrespondance$2.set(Constants.ROUTING_MODE.WALK, 'walking');
1397
+ inputModeCorrespondance$2.set(Constants.ROUTING_MODE.BIKE, 'bike');
1398
+ inputModeCorrespondance$2.set(Constants.ROUTING_MODE.BUS, 'bus');
1399
+ inputModeCorrespondance$2.set(Constants.ROUTING_MODE.MULTI, 'walking');
1400
+
1401
+
1402
+ /**
1403
+ * Singleton.
1404
+ */
1405
+ class OsrmRemoteRouter extends RemoteRouter {
1406
+
1407
+ /**
1408
+ * @override
1409
+ */
1410
+ get rname() {
1411
+ return 'osrm';
1412
+ }
1413
+
1414
+ /**
1415
+ * @override
1416
+ */
1417
+ async getItineraries(endpointUrl, mode, waypoints) {
1418
+ const url = this.getURL(endpointUrl, mode, waypoints);
1419
+ const response = await request(url);
1420
+ return this.createRouterResponseFromJson(response, waypoints[0], waypoints[1]);
1421
+ }
1422
+
1423
+ /**
1424
+ * @param {string} endpointUrl
1425
+ * @param {string} mode
1426
+ * @param {Array<Coordinates>} waypoints
1427
+ */
1428
+ getURL(endpointUrl, mode, waypoints) {
1429
+
1430
+ const osrmMode = inputModeCorrespondance$2.get(mode);
1431
+ if (!osrmMode) {
1432
+ throw new OsrmRoutingModeCorrespondanceNotFound(mode);
1433
+ }
1434
+
1435
+ let url = endpointUrl + '/route/v1/' + osrmMode + '/';
1436
+ url += waypoints.map(waypoint => [waypoint.longitude + ',' + waypoint.latitude]).join(';');
1437
+ url += '?geometries=geojson&overview=full&steps=true';
1438
+
1439
+ return url;
1440
+ }
1441
+
1442
+ /**
1443
+ * @param {Coordinates} coordinates
1444
+ * @returns {object}
1445
+ */
1446
+ coordinatesToJson(coordinates) {
1447
+ const output = [coordinates.lng, coordinates.lat];
1448
+ if (coordinates.level) {
1449
+ output.push(coordinates.level.toString());
1450
+ }
1451
+ return output;
1452
+ }
1453
+
1454
+ /**
1455
+ * @param {object} json
1456
+ * @returns {Coordinates}
1457
+ */
1458
+ jsonToCoordinates(json) {
1459
+ const output = new Coordinates(json[1], json[0]);
1460
+ if (json.length > 2) {
1461
+ output.level = Level.fromString(json[2]);
1462
+ }
1463
+ return output;
1464
+ }
1465
+
1466
+ nodesToJsonCoords(nodes) {
1467
+ return nodes.map(node => this.coordinatesToJson(node.coords));
1468
+ }
1469
+
1470
+
1471
+ getModifierFromAngle(_angle) {
1472
+
1473
+ const angle = positiveMod(rad2deg(_angle), 360);
1474
+
1475
+ if (angle > 0 && angle < 60) {
1476
+ return 'sharp right';
1477
+ }
1478
+ if (angle >= 60 && angle < 140) {
1479
+ return 'right';
1480
+ }
1481
+ if (angle >= 140 && angle < 160) {
1482
+ return 'slight right';
1483
+ }
1484
+ if (angle >= 160 && angle <= 200) {
1485
+ return 'straight';
1486
+ }
1487
+ if (angle > 200 && angle <= 220) {
1488
+ return 'slight left';
1489
+ }
1490
+ if (angle > 220 && angle <= 300) {
1491
+ return 'left';
1492
+ }
1493
+ if (angle > 300 && angle < 360) {
1494
+ return 'sharp left';
1495
+ }
1496
+ return 'u turn';
1497
+ }
1498
+
1499
+
1500
+ noRouteFoundJson(message) {
1501
+ return {
1502
+ 'code': 'NoRoute',
1503
+ message
1504
+ };
1505
+ }
1506
+
1507
+ /**
1508
+ * @param {Itinerary} itinerary
1509
+ * @returns {object}
1510
+ */
1511
+ itineraryToOsrmJson(itinerary) {
1512
+
1513
+ const lastLegId = itinerary.legs.length - 1;
1514
+
1515
+ const jsonLegs = itinerary.legs.map(({ distance, duration, coords, steps }, idLeg) => {
1516
+
1517
+ const lastStepId = steps.length - 1;
1518
+
1519
+ return {
1520
+ distance,
1521
+ duration,
1522
+ steps: steps.map((step, idStep, arr) => {
1523
+
1524
+ let type = idStep === 0 && idLeg === 0 ? 'depart' : 'turn';
1525
+ type = idStep === lastStepId && idLeg === lastLegId ? 'arrive' : type;
1526
+
1527
+ const stepCoordsIdx = coords.findIndex(p => p.equalsTo(step.coords));
1528
+ const nextStepCoordsIdx = idStep === lastStepId
1529
+ ? stepCoordsIdx
1530
+ : coords.findIndex(p => p.equalsTo(arr[idStep + 1].coords));
1531
+
1532
+ const jsonStep = {
1533
+ geometry: {
1534
+ type: 'LineString',
1535
+ coordinates: coords.slice(stepCoordsIdx, nextStepCoordsIdx + 1).map(this.coordinatesToJson)
1536
+ },
1537
+ distance: step.distance,
1538
+ duration: step.duration,
1539
+ name: step.name,
1540
+ maneuver: {
1541
+ bearing_before: rad2deg(step.previousBearing),
1542
+ bearing_after: rad2deg(step.nextBearing),
1543
+ location: this.coordinatesToJson(step.coords),
1544
+ modifier: this.getModifierFromAngle(step.angle),
1545
+ type
1546
+ }
1547
+ };
1548
+ if (step.levelChange !== null) {
1549
+ jsonStep.levelChange = step.levelChange.toJson();
1550
+ }
1551
+ if (typeof step.extras === 'object' && Object.keys(step.extras).length !== 0) {
1552
+ jsonStep.extras = step.extras;
1553
+ }
1554
+
1555
+ return jsonStep;
1556
+ })
1557
+ };
1558
+ });
1559
+
1560
+ return {
1561
+ 'code': 'Ok',
1562
+ 'routes': [
1563
+ {
1564
+ 'geometry': {
1565
+ 'type': 'LineString',
1566
+ 'coordinates': itinerary.coords.map(this.coordinatesToJson)
1567
+ },
1568
+ 'legs': jsonLegs,
1569
+ 'distance': itinerary.distance,
1570
+ 'duration': itinerary.duration,
1571
+ 'weight_name': 'routability',
1572
+ 'weight': 0
1573
+ }
1574
+ ],
1575
+ 'waypoints': []
1576
+ };
1577
+ }
1578
+
1579
+ /**
1580
+ * @param {object} jsonSteps
1581
+ * @param {Coordinates[]} legCoords
1582
+ * @returns {Step[]}
1583
+ */
1584
+ parseJsonSteps(jsonSteps, legCoords) {
1585
+
1586
+ if (!jsonSteps) {
1587
+ return [];
1588
+ }
1589
+
1590
+ return jsonSteps.map(jsonStep => {
1591
+
1592
+ const step = new Step();
1593
+ step.coords = this.jsonToCoordinates(jsonStep.maneuver.location);
1594
+
1595
+ // Sometimes, OSRM step does not have the same coordinates than a point in legCoords.
1596
+ // ex: first step of https://routing.getwemap.com/route/v1/walking/2.33222164147,48.87084765712;2.3320734,48.8730212?geometries=geojson&overview=full&steps=true
1597
+ // That is why we look for the closest point.
1598
+ const distances = legCoords.map(coords => coords.distanceTo(step.coords));
1599
+ const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
1600
+ if (idStepCoordsInLeg < 0) {
1601
+ throw new Error('Osrm Parser: Cannot find step coords in leg coordinates');
1602
+ }
1603
+ step._idCoordsInLeg = idStepCoordsInLeg;
1604
+
1605
+ step.name = jsonStep.name;
1606
+ step.levelChange = jsonStep.levelChange ? LevelChange.fromJson(jsonStep.levelChange) : null;
1607
+
1608
+ step.distance = jsonStep.distance;
1609
+ step.duration = jsonStep.duration;
1610
+
1611
+ if (jsonStep.extras && jsonStep.extras.subwayEntrance) {
1612
+ step.extras.subwayEntrance = true;
1613
+ if (jsonStep.extras.subwayEntranceRef) {
1614
+ step.extras.subwayEntranceRef = jsonStep.extras.subwayEntranceRef;
1615
+ }
1616
+ }
1617
+
1618
+ return step;
1619
+ });
1620
+ }
1621
+
1622
+ /**
1623
+ * Generate multi itineraries from OSRM JSON
1624
+ * @param {object} json JSON file provided by OSRM.
1625
+ * @param {Coordinates} from itinerary start
1626
+ * @param {Coordinates} to itinerary end
1627
+ * @param {?string} routingMode [walking|driving|bicycle]
1628
+ * @returns {?RouterResponse}
1629
+ */
1630
+ createRouterResponseFromJson(json, from, to, routingMode = 'walking') {
1631
+ const { routes: jsonRoutes } = json;
1632
+
1633
+ if (!jsonRoutes) {
1634
+ return null;
1635
+ }
1636
+
1637
+ const routingModeCorrespondance = new Map();
1638
+ routingModeCorrespondance.set('walking', 'WALK');
1639
+ routingModeCorrespondance.set('driving', 'CAR');
1640
+ routingModeCorrespondance.set('bicycle', 'BIKE');
1641
+ const mode = routingModeCorrespondance.get(routingMode) || null;
1642
+
1643
+ const routerResponse = new RouterResponse();
1644
+ routerResponse.routerName = this.rname;
1645
+
1646
+ routerResponse.from = from;
1647
+ routerResponse.to = to;
1648
+
1649
+ for (const jsonItinerary of jsonRoutes) {
1650
+
1651
+ const itinerary = new Itinerary();
1652
+
1653
+ // itinerary.coords = jsonItinerary.geometry.coordinates.map(jsonToCoordinates);
1654
+ itinerary.distance = jsonItinerary.distance;
1655
+ itinerary.duration = jsonItinerary.duration;
1656
+ itinerary.from = from;
1657
+ itinerary.to = to;
1658
+
1659
+ routerResponse.itineraries.push(itinerary);
1660
+
1661
+ for (const jsonLeg of jsonItinerary.legs) {
1662
+
1663
+ const leg = new Leg();
1664
+
1665
+ leg.mode = mode;
1666
+ leg.distance = jsonLeg.distance;
1667
+ leg.duration = jsonLeg.duration;
1668
+
1669
+ leg.coords = jsonLeg.steps
1670
+ .map(step => step.geometry.coordinates.map(this.jsonToCoordinates))
1671
+ .flat()
1672
+ // Remove duplicates
1673
+ .filter((coords, idx, arr) => idx === 0 || !arr[idx - 1].equalsTo(coords));
1674
+
1675
+ leg.from = {
1676
+ name: null,
1677
+ coords: leg.coords[0]
1678
+ };
1679
+ leg.to = {
1680
+ name: null,
1681
+ coords: leg.coords[leg.coords.length - 1]
1682
+ };
1683
+
1684
+ leg.steps = this.parseJsonSteps(jsonLeg.steps, leg.coords);
1685
+
1686
+ itinerary.legs.push(leg);
1687
+ }
1688
+
1689
+ // All legs have to be parsed before computing steps metadata
1690
+ generateStepsMetadata(itinerary);
1691
+
1692
+ }
1693
+
1694
+ return routerResponse;
1695
+ }
1696
+ }
1697
+
1698
+ var OsrmRemoteRouter$1 = new OsrmRemoteRouter();
1699
+
1700
+ class OtpRoutingModeCorrespondanceNotFound extends Error {
1701
+
1702
+ /**
1703
+ * @param {!string} routingMode
1704
+ */
1705
+ constructor(routingMode) {
1706
+ super(`OTP routing mode correspondance not found: ${routingMode}`);
1707
+ }
1708
+ }
1709
+
1710
+ /* eslint-disable max-statements */
1711
+
1712
+ /**
1713
+ * Input mode correspondance
1714
+ */
1715
+ const inputModeCorrespondance$1 = new Map();
1716
+ inputModeCorrespondance$1.set(Constants.ROUTING_MODE.CAR, 'CAR');
1717
+ inputModeCorrespondance$1.set(Constants.ROUTING_MODE.WALK, 'WALK');
1718
+ inputModeCorrespondance$1.set(Constants.ROUTING_MODE.BIKE, 'BICYCLE');
1719
+ inputModeCorrespondance$1.set(Constants.ROUTING_MODE.BUS, 'WALK,TRANSIT');
1720
+ inputModeCorrespondance$1.set(Constants.ROUTING_MODE.MULTI, 'WALK,TRANSIT');
1721
+
1722
+ /**
1723
+ * Singleton.
1724
+ */
1725
+ class OtpRemoteRouter extends RemoteRouter {
1726
+
1727
+ /**
1728
+ * @override
1729
+ */
1730
+ get rname() {
1731
+ return 'otp';
1732
+ }
1733
+
1734
+ /**
1735
+ * @override
1736
+ */
1737
+ async getItineraries(endpointUrl, mode, waypoints) {
1738
+ const url = this.getURL(endpointUrl, mode, waypoints);
1739
+ const response = await request(url);
1740
+ return this.createRouterResponseFromJson(response);
1741
+ }
1742
+
1743
+ /**
1744
+ * @param {string} endpointUrl
1745
+ * @param {string} mode
1746
+ * @param {Array<Coordinates>} waypoints
1747
+ */
1748
+ getURL(endpointUrl, mode, waypoints) {
1749
+
1750
+ const otpMode = inputModeCorrespondance$1.get(mode);
1751
+ if (!otpMode) {
1752
+ throw new OtpRoutingModeCorrespondanceNotFound(mode);
1753
+ }
1754
+
1755
+ if (waypoints.length > 2) {
1756
+ Logger.warn(`${this.rname} router uses only the first 2 waypoints (asked ${waypoints.length})`);
1757
+ }
1758
+
1759
+ const fromPlace = `fromPlace=${waypoints[0].latitude},${waypoints[0].longitude}`;
1760
+ const toPlace = `toPlace=${waypoints[1].latitude},${waypoints[1].longitude}`;
1761
+ const queryMode = `mode=${otpMode}`;
1762
+
1763
+ const url = new URL(endpointUrl);
1764
+ let { search } = url;
1765
+ search = (search ? `${search}&` : '?') + `${fromPlace}&${toPlace}&${queryMode}`;
1766
+
1767
+ return `${url.origin}${url.pathname}${search}`;
1768
+ }
1769
+
1770
+ /**
1771
+ * @param {object} json
1772
+ * @returns {Coordinates}
1773
+ */
1774
+ jsonToCoordinates(json) {
1775
+ return new Coordinates(json.lat, json.lon);
1776
+ }
1777
+
1778
+ /**
1779
+ * @param {object} jsonSteps
1780
+ * @param {Coordinates[]} legCoords
1781
+ * @returns {Step[]}
1782
+ */
1783
+ parseJsonSteps(jsonSteps, legCoords) {
1784
+
1785
+ if (!jsonSteps) {
1786
+ return [];
1787
+ }
1788
+
1789
+ return jsonSteps.map(jsonStep => {
1790
+
1791
+ const step = new Step();
1792
+ const stepCoords = this.jsonToCoordinates(jsonStep);
1793
+
1794
+ // OTP step does not have the same coordinates than a point in legCoords.
1795
+ // That is why we look for the closest point.
1796
+ const distances = legCoords.map(coords => coords.distanceTo(stepCoords));
1797
+ const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
1798
+ if (idStepCoordsInLeg < 0) {
1799
+ throw new Error('OTP Parser: Cannot find closest step');
1800
+ }
1801
+ step.coords = legCoords[idStepCoordsInLeg];
1802
+ step._idCoordsInLeg = idStepCoordsInLeg;
1803
+
1804
+ step.name = jsonStep.streetName;
1805
+ step.levelChange = null;
1806
+
1807
+ step.distance = jsonStep.distance;
1808
+
1809
+ return step;
1810
+ });
1811
+ }
1812
+
1813
+ /**
1814
+ * Generate multi itineraries from OTP JSON
1815
+ * @param {object} json JSON file provided by OTP.
1816
+ * @returns {?RouterResponse}
1817
+ */
1818
+ createRouterResponseFromJson(json) {
1819
+
1820
+ const { plan: jsonPlan } = json;
1821
+
1822
+ if (!jsonPlan) {
1823
+ return null;
1824
+ }
1825
+
1826
+ const routerResponse = new RouterResponse();
1827
+ routerResponse.routerName = this.rname;
1828
+
1829
+ routerResponse.from = this.jsonToCoordinates(jsonPlan.from);
1830
+ routerResponse.to = this.jsonToCoordinates(jsonPlan.to);
1831
+
1832
+ for (const jsonItinerary of jsonPlan.itineraries) {
1833
+
1834
+ const itinerary = new Itinerary();
1835
+
1836
+ itinerary.duration = jsonItinerary.duration;
1837
+ itinerary.startTime = jsonItinerary.startTime;
1838
+ itinerary.endTime = jsonItinerary.endTime;
1839
+ itinerary.from = routerResponse.from;
1840
+ itinerary.to = routerResponse.to;
1841
+
1842
+ routerResponse.itineraries.push(itinerary);
1843
+
1844
+ for (const jsonLeg of jsonItinerary.legs) {
1845
+
1846
+ const leg = new Leg();
1847
+
1848
+ leg.mode = jsonLeg.mode;
1849
+ leg.duration = jsonLeg.duration;
1850
+ leg.startTime = jsonLeg.startTime;
1851
+ leg.endTime = jsonLeg.endTime;
1852
+ leg.from = {
1853
+ name: jsonLeg.from.name,
1854
+ coords: this.jsonToCoordinates(jsonLeg.from)
1855
+ };
1856
+ leg.to = {
1857
+ name: jsonLeg.to.name,
1858
+ coords: this.jsonToCoordinates(jsonLeg.to)
1859
+ };
1860
+ leg.coords = Polyline.decode(jsonLeg.legGeometry.points).map(([lat, lon]) => new Coordinates(lat, lon));
1861
+
1862
+ leg.steps = this.parseJsonSteps(jsonLeg.steps, leg.coords);
1863
+
1864
+ if (leg.mode === 'BUS' || leg.mode === 'TRAM') {
1865
+ leg.transportInfo = {
1866
+ name: jsonLeg.route,
1867
+ routeColor: jsonLeg.routeColor,
1868
+ routeTextColor: jsonLeg.routeTextColor,
1869
+ directionName: jsonLeg.headsign
1870
+ };
1871
+
1872
+ const legStep = new Step();
1873
+ legStep.coords = leg.coords[0];
1874
+ legStep._idCoordsInLeg = 0;
1875
+ legStep.name = jsonLeg.headsign;
1876
+ legStep.levelChange = null;
1877
+ legStep.distance = jsonLeg.distance;
1878
+ leg.steps = [legStep];
1879
+ }
1880
+
1881
+ // jsonLeg.distance is not reliable when compared to the array of leg coords.
1882
+ // leg.distance = jsonLeg.distance;
1883
+ leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
1884
+ if (idx === 0) {
1885
+ return acc;
1886
+ }
1887
+ return acc + arr[idx - 1].distanceTo(coords);
1888
+ }, 0);
1889
+
1890
+ itinerary.legs.push(leg);
1891
+
1892
+ }
1893
+
1894
+ itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
1895
+ if (idx === 0) {
1896
+ return acc;
1897
+ }
1898
+ return acc + arr[idx - 1].distanceTo(coords);
1899
+ }, 0);
1900
+
1901
+ // All legs have to be parsed before computing steps metadata
1902
+ generateStepsMetadata(itinerary);
1903
+ }
1904
+
1905
+ return routerResponse;
1906
+ }
1907
+ }
1908
+
1909
+ var OtpRemoteRouter$1 = new OtpRemoteRouter();
1910
+
1911
+ class CitywayRoutingModeCorrespondanceNotFound extends Error {
1912
+
1913
+ /**
1914
+ * @param {!string} routingMode
1915
+ */
1916
+ constructor(routingMode) {
1917
+ super(`Cityway routing mode correspondance not found: ${routingMode}`);
1918
+ }
1919
+ }
1920
+
1921
+ /* eslint-disable max-depth */
1922
+
1923
+ /**
1924
+ * Input mode correspondance
1925
+ */
1926
+ const inputModeCorrespondance = new Map();
1927
+ inputModeCorrespondance.set(Constants.ROUTING_MODE.CAR, 'Car');
1928
+ inputModeCorrespondance.set(Constants.ROUTING_MODE.WALK, 'Walk');
1929
+ inputModeCorrespondance.set(Constants.ROUTING_MODE.BIKE, 'Bike');
1930
+ inputModeCorrespondance.set(Constants.ROUTING_MODE.BUS, 'PT');
1931
+ inputModeCorrespondance.set(Constants.ROUTING_MODE.MULTI, 'PT');
1932
+
1933
+
1934
+ /**
1935
+ * List of all routing modes supported by the API
1936
+ */
1937
+ const routingModeCorrespondance$1 = new Map();
1938
+ routingModeCorrespondance$1.set('WALK', Constants.ROUTING_MODE.WALK);
1939
+ routingModeCorrespondance$1.set('BICYCLE', Constants.ROUTING_MODE.BIKE);
1940
+ routingModeCorrespondance$1.set('TRAMWAY', Constants.ROUTING_MODE.TRAM);
1941
+ routingModeCorrespondance$1.set('METRO', Constants.ROUTING_MODE.METRO);
1942
+ routingModeCorrespondance$1.set('FUNICULAR', Constants.ROUTING_MODE.FUNICULAR);
1943
+ routingModeCorrespondance$1.set('BUS', Constants.ROUTING_MODE.BUS);
1944
+ routingModeCorrespondance$1.set('COACH', Constants.ROUTING_MODE.BUS);
1945
+ routingModeCorrespondance$1.set('SCHOOL', Constants.ROUTING_MODE.BUS);
1946
+ routingModeCorrespondance$1.set('BUS_PMR', Constants.ROUTING_MODE.BUS);
1947
+ routingModeCorrespondance$1.set('MINIBUS', Constants.ROUTING_MODE.BUS);
1948
+ routingModeCorrespondance$1.set('TROLLEY_BUS', Constants.ROUTING_MODE.BUS);
1949
+ routingModeCorrespondance$1.set('TAXIBUS', Constants.ROUTING_MODE.BUS);
1950
+ routingModeCorrespondance$1.set('SHUTTLE', Constants.ROUTING_MODE.BUS);
1951
+ routingModeCorrespondance$1.set('TRAIN', Constants.ROUTING_MODE.TRAIN);
1952
+ routingModeCorrespondance$1.set('HST', Constants.ROUTING_MODE.TRAIN);
1953
+ routingModeCorrespondance$1.set('LOCAL_TRAIN', Constants.ROUTING_MODE.TRAIN);
1954
+ routingModeCorrespondance$1.set('AIR', Constants.ROUTING_MODE.AIRPLANE);
1955
+ routingModeCorrespondance$1.set('FERRY', Constants.ROUTING_MODE.BOAT);
1956
+ routingModeCorrespondance$1.set('TAXI', Constants.ROUTING_MODE.UNKNOWN);
1957
+ routingModeCorrespondance$1.set('CAR_POOL', Constants.ROUTING_MODE.UNKNOWN);
1958
+ routingModeCorrespondance$1.set('PRIVATE_VEHICLE', Constants.ROUTING_MODE.CAR);
1959
+ routingModeCorrespondance$1.set('SCOOTER', Constants.ROUTING_MODE.MOTO);
1960
+
1961
+ /**
1962
+ * List of all plan trip supported by the API
1963
+ * Routing mode UNKNOWN means that the itinerary will not be parsed by the router
1964
+ */
1965
+ const planTripType = new Map();
1966
+ planTripType.set(0, Constants.ROUTING_MODE.BUS);
1967
+ planTripType.set(1, Constants.ROUTING_MODE.WALK);
1968
+ planTripType.set(2, Constants.ROUTING_MODE.BIKE);
1969
+ planTripType.set(3, Constants.ROUTING_MODE.CAR);
1970
+ planTripType.set(4, Constants.ROUTING_MODE.UNKNOWN);
1971
+ planTripType.set(5, Constants.ROUTING_MODE.UNKNOWN);
1972
+ planTripType.set(6, Constants.ROUTING_MODE.UNKNOWN);
1973
+ planTripType.set(7, Constants.ROUTING_MODE.UNKNOWN);
1974
+ planTripType.set(8, Constants.ROUTING_MODE.UNKNOWN);
1975
+ planTripType.set(9, Constants.ROUTING_MODE.UNKNOWN);
1976
+ planTripType.set(10, Constants.ROUTING_MODE.UNKNOWN);
1977
+ planTripType.set(11, Constants.ROUTING_MODE.UNKNOWN);
1978
+ planTripType.set(12, Constants.ROUTING_MODE.UNKNOWN);
1979
+ planTripType.set(13, Constants.ROUTING_MODE.UNKNOWN);
1980
+ planTripType.set(14, Constants.ROUTING_MODE.UNKNOWN);
1981
+
1982
+
1983
+ /**
1984
+ * Singleton.
1985
+ */
1986
+ class CitywayRemoteRouter extends RemoteRouter {
1987
+
1988
+ /**
1989
+ * @override
1990
+ */
1991
+ get rname() {
1992
+ return 'cityway';
1993
+ }
1994
+
1995
+
1996
+ /**
1997
+ * @override
1998
+ */
1999
+ async getItineraries(endpointUrl, mode, waypoints) {
2000
+ const url = this.getURL(endpointUrl, mode, waypoints);
2001
+ const response = await request(url);
2002
+ return this.createRouterResponseFromJson(response);
2003
+ }
2004
+
2005
+
2006
+ /**
2007
+ * @param {string} endpointUrl
2008
+ * @param {string} mode
2009
+ * @param {Array<Coordinates>} waypoints
2010
+ */
2011
+ getURL(endpointUrl, mode, waypoints) {
2012
+ const citywayMode = inputModeCorrespondance.get(mode);
2013
+ if (!citywayMode) {
2014
+ throw new CitywayRoutingModeCorrespondanceNotFound(mode);
2015
+ }
2016
+ if (waypoints.length > 2) {
2017
+ Logger.warn(`${this.rname} router uses only the first 2 waypoints (asked ${waypoints.length})`);
2018
+ }
2019
+ const fromPlace = `DepartureLatitude=${waypoints[0].latitude}&DepartureLongitude=${waypoints[0].longitude}`;
2020
+ const toPlace = `ArrivalLatitude=${waypoints[1].latitude}&ArrivalLongitude=${waypoints[1].longitude}`;
2021
+ const queryMode = `TripModes=${citywayMode}`;
2022
+
2023
+ const url = new URL(endpointUrl);
2024
+ let { search } = url;
2025
+ search = (search ? `${search}&` : '?') + `${fromPlace}&${toPlace}&${queryMode}`;
2026
+
2027
+ return `${url.origin}${url.pathname}${search}`;
2028
+ }
2029
+
2030
+ /**
2031
+ * Generate multi itineraries from Cityway JSON
2032
+ * @param {object} json JSON file provided by Cityway.
2033
+ * @returns {?RouterResponse}
2034
+ * @example https://preprod.api.lia2.cityway.fr/journeyplanner/api/opt/PlanTrips/json?DepartureLatitude=49.51509388236216&DepartureLongitude=0.09341749619366316&ArrivalLatitude=49.5067090188444&ArrivalLongitude=0.1694842115417831&DepartureType=COORDINATES&ArrivalType=COORDINATES
2035
+ */
2036
+ createRouterResponseFromJson(json) {
2037
+
2038
+ if (json.StatusCode !== 200 || !json.Data || !json.Data.length) {
2039
+ return null;
2040
+ }
2041
+
2042
+ const routerResponse = new RouterResponse();
2043
+ routerResponse.routerName = this.rname;
2044
+
2045
+
2046
+ // Do not know if it the best approach, but it works...
2047
+ const allJsonTrips = json.Data.reduce((acc, dataObj) => {
2048
+ acc.push(...dataObj.response.trips.Trip.map(trip => ({
2049
+ ...trip,
2050
+ ...(dataObj.hasOwnProperty('PlanTripType') ? { PlanTripType: dataObj.PlanTripType } : {})
2051
+ })));
2052
+ return acc;
2053
+ }, []);
2054
+
2055
+ // eslint-disable-next-line no-labels
2056
+ itineraryLoop:
2057
+ for (const trip of allJsonTrips) {
2058
+
2059
+ if (trip.hasOwnProperty('PlanTripType') && planTripType.get(trip.PlanTripType) === Constants.ROUTING_MODE.UNKNOWN) {
2060
+ continue;
2061
+ }
2062
+
2063
+ const itinerary = new Itinerary();
2064
+
2065
+ itinerary.duration = this.parseDuration(trip.Duration);
2066
+ itinerary.startTime = this.jsonDateToTimestamp(trip.Departure.Time);
2067
+ itinerary.from = this.jsonToCoordinates(trip.Departure.Site.Position);
2068
+ itinerary.endTime = this.jsonDateToTimestamp(trip.Arrival.Time);
2069
+ itinerary.to = this.jsonToCoordinates(trip.Arrival.Site.Position);
2070
+
2071
+ for (const jsonSection of trip.sections.Section) {
2072
+
2073
+ const jsonLeg = jsonSection.Leg ? jsonSection.Leg : jsonSection.PTRide;
2074
+
2075
+ const leg = new Leg();
2076
+
2077
+ leg.mode = routingModeCorrespondance$1.get(jsonLeg.TransportMode);
2078
+ leg.duration = this.parseDuration(jsonLeg.Duration);
2079
+ leg.startTime = this.jsonDateToTimestamp(jsonLeg.Departure.Time);
2080
+ leg.endTime = this.jsonDateToTimestamp(jsonLeg.Arrival.Time);
2081
+ leg.coords = [];
2082
+
2083
+ if (leg.mode === Constants.ROUTING_MODE.UNKNOWN) {
2084
+ // eslint-disable-next-line
2085
+ continue itineraryLoop;
2086
+ }
1085
2087
 
1086
- const lastLegId = itinerary.legs.length - 1;
2088
+ if (leg.mode === Constants.ROUTING_MODE.WALK
2089
+ || leg.mode === Constants.ROUTING_MODE.BIKE
2090
+ || leg.mode === Constants.ROUTING_MODE.CAR) {
2091
+
2092
+ leg.from = {
2093
+ name: jsonLeg.Departure.Site.Name,
2094
+ coords: this.jsonToCoordinates(jsonLeg.Departure.Site.Position)
2095
+ };
2096
+ leg.to = {
2097
+ name: jsonLeg.Arrival.Site.Name,
2098
+ coords: this.jsonToCoordinates(jsonLeg.Arrival.Site.Position)
2099
+ };
2100
+
2101
+ leg.steps = [];
2102
+ for (const jsonPathLink of jsonLeg.pathLinks.PathLink) {
2103
+ const step = new Step();
2104
+ let stepCoords;
2105
+ if (jsonPathLink.Geometry) {
2106
+ stepCoords = this.parseWKTGeometry(jsonPathLink.Geometry);
2107
+ } else {
2108
+ stepCoords = [leg.from.coords, leg.to.coords];
2109
+ }
2110
+ step.coords = stepCoords[0];
2111
+ step._idCoordsInLeg = leg.coords.length;
2112
+ stepCoords.forEach((coords, idx) => {
2113
+ if (
2114
+ idx !== 0
2115
+ || leg.coords.length === 0
2116
+ || !leg.coords[leg.coords.length - 1].equalsTo(coords)
2117
+ ) {
2118
+ leg.coords.push(coords);
2119
+ }
2120
+ });
1087
2121
 
1088
- const jsonLegs = itinerary.legs.map(({ distance, duration, coords, steps }, idLeg) => {
1089
2122
 
1090
- const lastStepId = steps.length - 1;
2123
+ step.name = jsonPathLink.Departure.Site.Name;
2124
+ step.levelChange = null;
1091
2125
 
1092
- return {
1093
- distance,
1094
- duration,
1095
- steps: steps.map((step, idStep, arr) => {
1096
-
1097
- let type = idStep === 0 && idLeg === 0 ? 'depart' : 'turn';
1098
- type = idStep === lastStepId && idLeg === lastLegId ? 'arrive' : type;
1099
-
1100
- const stepCoordsIdx = coords.findIndex(p => p.equalsTo(step.coords));
1101
- const nextStepCoordsIdx = idStep === lastStepId
1102
- ? stepCoordsIdx
1103
- : coords.findIndex(p => p.equalsTo(arr[idStep + 1].coords));
1104
-
1105
- const jsonStep = {
1106
- geometry: {
1107
- type: 'LineString',
1108
- coordinates: coords.slice(stepCoordsIdx, nextStepCoordsIdx + 1).map(coordinatesToJson)
1109
- },
1110
- distance: step.distance,
1111
- duration: step.duration,
1112
- name: step.name,
1113
- maneuver: {
1114
- bearing_before: rad2deg(step.previousBearing),
1115
- bearing_after: rad2deg(step.nextBearing),
1116
- location: coordinatesToJson(step.coords),
1117
- modifier: getModifierFromAngle(step.angle),
1118
- type
2126
+ step.distance = jsonPathLink.Distance;
2127
+
2128
+ leg.steps.push(step);
1119
2129
  }
1120
- };
1121
- if (step.levelChange !== null) {
1122
- jsonStep.levelChange = step.levelChange.toJson();
1123
- }
1124
- if (typeof step.extras === 'object' && Object.keys(step.extras).length !== 0) {
1125
- jsonStep.extras = step.extras;
2130
+
2131
+ } else if (Constants.PUBLIC_TRANSPORT.includes(leg.mode)) {
2132
+
2133
+ leg.from = {
2134
+ name: jsonLeg.Departure.StopPlace.Name,
2135
+ coords: this.jsonToCoordinates(jsonLeg.Departure.StopPlace.Position)
2136
+ };
2137
+ leg.to = {
2138
+ name: jsonLeg.Arrival.StopPlace.Name,
2139
+ coords: this.jsonToCoordinates(jsonLeg.Arrival.StopPlace.Position)
2140
+ };
2141
+
2142
+ let transportName = jsonLeg.Line.Number;
2143
+ if (leg.mode === Constants.ROUTING_MODE.TRAM && transportName.toLowerCase().includes('tram')) {
2144
+ // In order to remove the "TRAM " prefix.
2145
+ transportName = transportName.substr(5);
2146
+ }
2147
+
2148
+ leg.transportInfo = {
2149
+ name: transportName,
2150
+ routeColor: jsonLeg.Line.Color,
2151
+ routeTextColor: jsonLeg.Line.TextColor,
2152
+ directionName: jsonLeg.Destination
2153
+ };
2154
+
2155
+ for (const jsonStep of jsonLeg.steps.Step) {
2156
+ const stepCoords = this.parseWKTGeometry(jsonStep.Geometry);
2157
+ stepCoords.forEach((coords, idx) => {
2158
+ if (
2159
+ idx !== 0
2160
+ || leg.coords.length === 0
2161
+ || !leg.coords[leg.coords.length - 1].equalsTo(coords)
2162
+ ) {
2163
+ leg.coords.push(coords);
2164
+ }
2165
+ });
2166
+ }
2167
+
2168
+ const legStep = new Step();
2169
+ legStep.coords = leg.coords[0];
2170
+ legStep._idCoordsInLeg = 0;
2171
+ legStep.name = jsonLeg.Line.Name;
2172
+ legStep.levelChange = null;
2173
+ legStep.distance = jsonLeg.Distance;
2174
+ leg.steps = [legStep];
2175
+ } else {
2176
+ Logger.warn(`[CitywayParser] Unknown leg mode: ${jsonLeg.TransportMode}`);
1126
2177
  }
1127
2178
 
1128
- return jsonStep;
1129
- })
1130
- };
1131
- });
2179
+ leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
2180
+ if (idx === 0) {
2181
+ return acc;
2182
+ }
2183
+ return acc + arr[idx - 1].distanceTo(coords);
2184
+ }, 0);
2185
+
2186
+ itinerary.legs.push(leg);
1132
2187
 
1133
- return {
1134
- 'code': 'Ok',
1135
- 'routes': [
1136
- {
1137
- 'geometry': {
1138
- 'type': 'LineString',
1139
- 'coordinates': itinerary.coords.map(coordinatesToJson)
1140
- },
1141
- 'legs': jsonLegs,
1142
- 'distance': itinerary.distance,
1143
- 'duration': itinerary.duration,
1144
- 'weight_name': 'routability',
1145
- 'weight': 0
1146
2188
  }
1147
- ],
1148
- 'waypoints': []
1149
- };
1150
- }
1151
2189
 
2190
+ routerResponse.itineraries.push(itinerary);
1152
2191
 
1153
- /**
1154
- * @param {object} jsonSteps
1155
- * @param {Coordinates[]} legCoords
1156
- * @returns {Step[]}
1157
- */
1158
- function parseJsonSteps$1(jsonSteps, legCoords) {
2192
+ itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
2193
+ if (idx === 0) {
2194
+ return acc;
2195
+ }
2196
+ return acc + arr[idx - 1].distanceTo(coords);
2197
+ }, 0);
1159
2198
 
1160
- if (!jsonSteps) {
1161
- return [];
1162
- }
2199
+ // All legs have to be parsed before computing steps metadata
2200
+ generateStepsMetadata(itinerary);
2201
+ }
1163
2202
 
1164
- return jsonSteps.map(jsonStep => {
2203
+ routerResponse.from = routerResponse.itineraries[0].from;
2204
+ routerResponse.to = routerResponse.itineraries[routerResponse.itineraries.length - 1].to;
1165
2205
 
1166
- const step = new Step();
1167
- step.coords = jsonToCoordinates$2(jsonStep.maneuver.location);
2206
+ return routerResponse;
2207
+ }
1168
2208
 
1169
- // Sometimes, OSRM step does not have the same coordinates than a point in legCoords.
1170
- // ex: first step of https://routing.getwemap.com/route/v1/walking/2.33222164147,48.87084765712;2.3320734,48.8730212?geometries=geojson&overview=full&steps=true
1171
- // That is why we look for the closest point.
1172
- const distances = legCoords.map(coords => coords.distanceTo(step.coords));
1173
- const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
1174
- if (idStepCoordsInLeg < 0) {
1175
- throw new Error('Osrm Parser: Cannot find step coords in leg coordinates');
1176
- }
1177
- step._idCoordsInLeg = idStepCoordsInLeg;
2209
+ /**
2210
+ * @param {object} json
2211
+ * @returns {Coordinates}
2212
+ */
2213
+ jsonToCoordinates(json) {
2214
+ return new Coordinates(json.Lat, json.Long);
2215
+ }
1178
2216
 
1179
- step.name = jsonStep.name;
1180
- step.levelChange = jsonStep.levelChange ? LevelChange.fromJson(jsonStep.levelChange) : null;
2217
+ /**
2218
+ * @param {string} jsonDate
2219
+ * @returns {number}
2220
+ */
2221
+ jsonDateToTimestamp(jsonDate) {
2222
+ const [dateStr, timeStr] = jsonDate.split(' ');
2223
+ const [dayStr, monthStr, yearStr] = dateStr.split('/');
2224
+ const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
2225
+
2226
+ return dateWithTimeZone(
2227
+ Number(yearStr),
2228
+ Number(monthStr) - 1,
2229
+ Number(dayStr),
2230
+ Number(hoursStr),
2231
+ Number(minutesStr),
2232
+ Number(secondsStr)
2233
+ ).getTime();
2234
+ }
1181
2235
 
1182
- step.distance = jsonStep.distance;
1183
- step.duration = jsonStep.duration;
2236
+ /**
2237
+ * @param {string} wktGeometry
2238
+ * @returns {Coordinates[]}
2239
+ */
2240
+ parseWKTGeometry(wktGeometry) {
2241
+ const tmpCoordsStr = wktGeometry.match(/LINESTRING \((.*)\)/i);
2242
+ const tmpCoordsPt = wktGeometry.match(/POINT \((.*)\)/i);
2243
+ if (!tmpCoordsStr && !tmpCoordsPt) {
2244
+ return null;
2245
+ }
1184
2246
 
1185
- if (jsonStep.extras && jsonStep.extras.subwayEntrance) {
1186
- step.extras.subwayEntrance = true;
1187
- if (jsonStep.extras.subwayEntranceRef) {
1188
- step.extras.subwayEntranceRef = jsonStep.extras.subwayEntranceRef;
1189
- }
2247
+ if (tmpCoordsPt) {
2248
+ const [lng, lat] = tmpCoordsPt[1].split(' ');
2249
+ return [new Coordinates(Number(lat), Number(lng))];
1190
2250
  }
1191
2251
 
1192
- return step;
1193
- });
2252
+ return tmpCoordsStr[1].split(',').map(str => {
2253
+ const sp = str.trim().split(' ');
2254
+ return new Coordinates(Number(sp[1]), Number(sp[0]));
2255
+ });
2256
+ }
2257
+
2258
+ /**
2259
+ * @param {string} iso8601Duration
2260
+ * @see https://stackoverflow.com/a/29153059/2239938
2261
+ */
2262
+ parseDuration(iso8601Duration) {
2263
+ const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
2264
+
2265
+ var matches = iso8601Duration.match(iso8601DurationRegex);
2266
+
2267
+ // const sign = typeof matches[1] === 'undefined' ? '+' : '-',
2268
+ const years = typeof matches[2] === 'undefined' ? 0 : Number(matches[2]);
2269
+ const months = typeof matches[3] === 'undefined' ? 0 : Number(matches[3]);
2270
+ const weeks = typeof matches[4] === 'undefined' ? 0 : Number(matches[4]);
2271
+ const days = typeof matches[5] === 'undefined' ? 0 : Number(matches[5]);
2272
+ const hours = typeof matches[6] === 'undefined' ? 0 : Number(matches[6]);
2273
+ const minutes = typeof matches[7] === 'undefined' ? 0 : Number(matches[7]);
2274
+ const seconds = typeof matches[8] === 'undefined' ? 0 : Number(matches[8]);
2275
+
2276
+ return seconds
2277
+ + minutes * 60
2278
+ + hours * 3600
2279
+ + days * 86400
2280
+ + weeks * (86400 * 7)
2281
+ + months * (86400 * 30)
2282
+ + years * (86400 * 365.25);
2283
+ }
1194
2284
  }
1195
2285
 
2286
+ var CitywayRemoteRouter$1 = new CitywayRemoteRouter();
2287
+
2288
+ /* eslint-disable max-statements */
1196
2289
 
1197
2290
  /**
1198
- * Generate multi itineraries from OSRM JSON
1199
- * @param {object} json JSON file provided by OSRM.
1200
- * @param {Coordinates} from itinerary start
1201
- * @param {Coordinates} to itinerary end
1202
- * @param {?string} routingMode [walking|driving|bicycle]
1203
- * @returns {?RouterResponse}
2291
+ * Singleton.
1204
2292
  */
1205
- function createRouterResponseFromJson$3(json, from, to, routingMode = 'walking') {
2293
+ class DeutscheBahnRemoteRouter extends RemoteRouter {
1206
2294
 
1207
- const { routes: jsonRoutes } = json;
2295
+ /**
2296
+ * @override
2297
+ */
2298
+ get rname() {
2299
+ return 'deutsche-bahn';
2300
+ }
1208
2301
 
1209
- if (!jsonRoutes) {
1210
- return null;
2302
+ /**
2303
+ * @override
2304
+ */
2305
+ async getItineraries(endpointUrl, mode, waypoints) {
2306
+ const url = this.getURL(endpointUrl, mode, waypoints);
2307
+ const response = await request(url);
2308
+ return this.createRouterResponseFromJson(response, waypoints[0], waypoints[1]);
1211
2309
  }
1212
2310
 
1213
- const routingModeCorrespondance = new Map();
1214
- routingModeCorrespondance.set('walking', 'WALK');
1215
- routingModeCorrespondance.set('driving', 'CAR');
1216
- routingModeCorrespondance.set('bicycle', 'BIKE');
1217
- const mode = routingModeCorrespondance.get(routingMode) || null;
2311
+ /**
2312
+ * @param {string} endpointUrl
2313
+ * @param {string} mode
2314
+ * @param {Array<Coordinates>} waypoints
2315
+ */
2316
+ getURL(endpointUrl, mode, waypoints) {
2317
+ let url = endpointUrl + '/route/v1/' + mode + '/';
1218
2318
 
1219
- const routerResponse = new RouterResponse();
1220
- routerResponse.routerName = 'osrm';
2319
+ url += waypoints.map(waypoint => {
2320
+ if (waypoint.level) {
2321
+ const altitude = waypoint.level.isRange ? waypoint.level.low : waypoint.level.val;
1221
2322
 
1222
- routerResponse.from = from;
1223
- routerResponse.to = to;
2323
+ return waypoint.longitude + ',' + waypoint.latitude + ',' + altitude;
2324
+ }
1224
2325
 
1225
- for (const jsonItinerary of jsonRoutes) {
2326
+ return waypoint.longitude + ',' + waypoint.latitude;
2327
+ }).join(';');
1226
2328
 
1227
- const itinerary = new Itinerary();
2329
+ url += '?geometries=geojson&overview=full&steps=true';
1228
2330
 
1229
- // itinerary.coords = jsonItinerary.geometry.coordinates.map(jsonToCoordinates);
1230
- itinerary.distance = jsonItinerary.distance;
1231
- itinerary.duration = jsonItinerary.duration;
1232
- itinerary.from = from;
1233
- itinerary.to = to;
2331
+ return url;
2332
+ }
1234
2333
 
1235
- routerResponse.itineraries.push(itinerary);
2334
+ /**
2335
+ * Generate multi itineraries from the DB JSON
2336
+ * @param {object} json JSON file provided by the DB.
2337
+ * @param {Coordinates} from itinerary start
2338
+ * @param {Coordinates} to itinerary end
2339
+ * @returns {?RouterResponse}
2340
+ */
2341
+ createRouterResponseFromJson(json, from, to) {
1236
2342
 
1237
- for (const jsonLeg of jsonItinerary.legs) {
2343
+ const { segments: jsonSegments } = json;
1238
2344
 
1239
- const leg = new Leg();
2345
+ if (!jsonSegments) {
2346
+ return null;
2347
+ }
1240
2348
 
1241
- leg.mode = mode;
1242
- leg.distance = jsonLeg.distance;
1243
- leg.duration = jsonLeg.duration;
2349
+ const routerResponse = new RouterResponse();
2350
+ routerResponse.routerName = this.rname;
1244
2351
 
1245
- leg.coords = jsonLeg.steps
1246
- .map(step => step.geometry.coordinates.map(jsonToCoordinates$2))
1247
- .flat()
1248
- // Remove duplicates
1249
- .filter((coords, idx, arr) => idx === 0 || !arr[idx - 1].equalsTo(coords));
2352
+ routerResponse.from = from;
2353
+ routerResponse.to = to;
1250
2354
 
1251
- leg.from = {
1252
- name: null,
1253
- coords: leg.coords[0]
1254
- };
1255
- leg.to = {
1256
- name: null,
1257
- coords: leg.coords[leg.coords.length - 1]
1258
- };
2355
+ /** @type {GraphEdge<OsmElement>[]} */
2356
+ const edges = [];
2357
+
2358
+ /** @type {GraphNode<OsmElement>[]} */
2359
+ const nodes = [];
2360
+
2361
+ /** @type {number[]} */
2362
+ const edgesWeights = [];
2363
+
2364
+ let id = 1;
2365
+ for (const jsonSegment of jsonSegments) {
2366
+
2367
+ const level = new Level(jsonSegment.fromLevel, jsonSegment.toLevel);
2368
+ const osmWay = new OsmWay(id++, null, level);
2369
+
2370
+ for (const jsonPoint of jsonSegment.polyline) {
2371
+ const coord = new Coordinates(jsonPoint.lat, jsonPoint.lon, null, level);
2372
+
2373
+ if (nodes.length !== 0
2374
+ && nodes[nodes.length - 1].coords.equalsTo(coord)) {
2375
+ continue;
2376
+ }
1259
2377
 
1260
- leg.steps = parseJsonSteps$1(jsonLeg.steps, leg.coords);
2378
+ const osmNode = new OsmNode(id++, coord);
2379
+ const node = new GraphNode(osmNode.coords, osmNode);
1261
2380
 
1262
- itinerary.legs.push(leg);
2381
+ if (nodes.length !== 0) {
2382
+ const prevNode = nodes[nodes.length - 1];
2383
+ const edge = new GraphEdge(prevNode, node, level, osmWay);
2384
+ edges.push(edge);
2385
+ edgesWeights.push(prevNode.coords.distanceTo(osmNode));
2386
+ }
2387
+
2388
+ nodes.push(node);
2389
+
2390
+ }
1263
2391
  }
1264
2392
 
2393
+ /** @type {GraphItinerary<OsmElement>} */
2394
+ const graphItinerary = new GraphItinerary();
2395
+ graphItinerary.nodes = nodes;
2396
+ graphItinerary.edges = edges;
2397
+ graphItinerary.edgesWeights = edgesWeights;
2398
+ graphItinerary.start = nodes[0].coords;
2399
+ graphItinerary.end = nodes[nodes.length - 1].coords;
2400
+
2401
+ const points = nodes.map(node => node.coords);
2402
+ const itinerary = Itinerary.fromOrderedCoordinates(points, from, to);
2403
+ itinerary.legs[0].steps = WemapStepsGeneration.fromGraphItinerary(graphItinerary);
2404
+ itinerary.legs[0].steps.map((step, idx) => (step._idCoordsInLeg = idx));
2405
+
1265
2406
  // All legs have to be parsed before computing steps metadata
1266
2407
  generateStepsMetadata(itinerary);
1267
2408
 
1268
- }
2409
+ routerResponse.itineraries.push(itinerary);
1269
2410
 
1270
- return routerResponse;
2411
+ return routerResponse;
2412
+ }
1271
2413
  }
1272
2414
 
1273
- var OsrmUtils = /*#__PURE__*/Object.freeze({
1274
- __proto__: null,
1275
- coordinatesToJson: coordinatesToJson,
1276
- jsonToCoordinates: jsonToCoordinates$2,
1277
- nodesToJsonCoords: nodesToJsonCoords,
1278
- getModifierFromAngle: getModifierFromAngle,
1279
- noRouteFoundJson: noRouteFoundJson,
1280
- itineraryToOsrmJson: itineraryToOsrmJson,
1281
- createRouterResponseFromJson: createRouterResponseFromJson$3
1282
- });
2415
+ var DeutscheBahnRemoteRouter$1 = new DeutscheBahnRemoteRouter();
1283
2416
 
1284
2417
  /* eslint-disable max-statements */
1285
2418
 
1286
2419
  /**
1287
- * @param {object} json
1288
- * @returns {Coordinates}
2420
+ * List of all modes supported by the API
2421
+ * http://doc.navitia.io/#physical-mode
2422
+ */
2423
+
2424
+ const routingModeCorrespondance = new Map();
2425
+ routingModeCorrespondance.set('Air', Constants.ROUTING_MODE.AIRPLANE);
2426
+ routingModeCorrespondance.set('Boat', Constants.ROUTING_MODE.BOAT);
2427
+ routingModeCorrespondance.set('Bus', Constants.ROUTING_MODE.BUS);
2428
+ routingModeCorrespondance.set('BusRapidTransit', Constants.ROUTING_MODE.BUS);
2429
+ routingModeCorrespondance.set('Coach', Constants.ROUTING_MODE.BUS);
2430
+ routingModeCorrespondance.set('Ferry', Constants.ROUTING_MODE.FERRY);
2431
+ routingModeCorrespondance.set('Funicular', Constants.ROUTING_MODE.FUNICULAR);
2432
+ routingModeCorrespondance.set('LocalTrain', Constants.ROUTING_MODE.TRAIN);
2433
+ routingModeCorrespondance.set('LongDistanceTrain', Constants.ROUTING_MODE.TRAIN);
2434
+ routingModeCorrespondance.set('Metro', Constants.ROUTING_MODE.METRO);
2435
+ routingModeCorrespondance.set('Métro', Constants.ROUTING_MODE.METRO);
2436
+ routingModeCorrespondance.set('RailShuttle', Constants.ROUTING_MODE.TRAIN);
2437
+ routingModeCorrespondance.set('RapidTransit', Constants.ROUTING_MODE.BUS);
2438
+ routingModeCorrespondance.set('Shuttle', Constants.ROUTING_MODE.BUS);
2439
+ routingModeCorrespondance.set('SuspendedCableCar', Constants.ROUTING_MODE.FUNICULAR);
2440
+ routingModeCorrespondance.set('Taxi', Constants.ROUTING_MODE.TAXI);
2441
+ routingModeCorrespondance.set('Train', Constants.ROUTING_MODE.TRAIN);
2442
+ routingModeCorrespondance.set('RER', Constants.ROUTING_MODE.TRAIN);
2443
+ routingModeCorrespondance.set('Tramway', Constants.ROUTING_MODE.TRAM);
2444
+ routingModeCorrespondance.set('walking', Constants.ROUTING_MODE.WALK);
2445
+ routingModeCorrespondance.set('bike', Constants.ROUTING_MODE.BIKE);
2446
+
2447
+ /**
2448
+ * List of transports modes
2449
+ */
2450
+ const TRANSPORT_IDS = [
2451
+ 'physical_mode:Air',
2452
+ 'physical_mode:Boat',
2453
+ 'physical_mode:Bus',
2454
+ 'physical_mode:BusRapidTransit',
2455
+ 'physical_mode:Coach',
2456
+ 'physical_mode:Ferry',
2457
+ 'physical_mode:Funicular',
2458
+ 'physical_mode:LocalTrain',
2459
+ 'physical_mode:LongDistanceTrain',
2460
+ 'physical_mode:Metro',
2461
+ 'physical_mode:RailShuttle',
2462
+ 'physical_mode:RapidTransit',
2463
+ 'physical_mode:Shuttle',
2464
+ 'physical_mode:SuspendedCableCar',
2465
+ 'physical_mode:Taxi',
2466
+ 'physical_mode:Train',
2467
+ 'physical_mode:Tramway'
2468
+ ];
2469
+
2470
+ const clientId = '539eec73-3bb5-4327-bb5e-a52672658592';
2471
+ const clientSecret = '899f4bb9-f1d5-45d3-9f67-530827bb6734';
2472
+
2473
+ /**
2474
+ * Get last item of a given array
2475
+ * @param {Array} array
2476
+ * @returns {any}
1289
2477
  */
1290
- function jsonToCoordinates$1(json) {
1291
- return new Coordinates(json.lat, json.lon);
2478
+ function last(array) {
2479
+ return array[array.length - 1];
1292
2480
  }
1293
2481
 
1294
2482
  /**
1295
- * @param {object} jsonSteps
1296
- * @param {Coordinates[]} legCoords
1297
- * @returns {Step[]}
2483
+ * Singleton.
1298
2484
  */
1299
- function parseJsonSteps(jsonSteps, legCoords) {
2485
+ class IdfmRemoteRouter extends RemoteRouter {
1300
2486
 
1301
- if (!jsonSteps) {
1302
- return [];
2487
+ isLogged = false;
2488
+ token = null;
2489
+ expiresAt = null;
2490
+
2491
+ /**
2492
+ * @override
2493
+ */
2494
+ get rname() {
2495
+ return 'idfm';
1303
2496
  }
1304
2497
 
1305
- return jsonSteps.map(jsonStep => {
2498
+ /**
2499
+ * @override
2500
+ */
2501
+ async getItineraries(endpointUrl, mode, waypoints) {
2502
+ if (!this.canRequestService()) {
2503
+ await this._connect();
2504
+ }
1306
2505
 
1307
- const step = new Step();
1308
- const stepCoords = jsonToCoordinates$1(jsonStep);
2506
+ const url = this.getURL(endpointUrl, mode, waypoints);
1309
2507
 
1310
- // OTP step does not have the same coordinates than a point in legCoords.
1311
- // That is why we look for the closest point.
1312
- const distances = legCoords.map(coords => coords.distanceTo(stepCoords));
1313
- const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
1314
- if (idStepCoordsInLeg < 0) {
1315
- throw new Error('OTP Parser: Cannot find closest step');
1316
- }
1317
- step.coords = legCoords[idStepCoordsInLeg];
1318
- step._idCoordsInLeg = idStepCoordsInLeg;
2508
+ const response = await request(url, {
2509
+ headers: { Authorization: 'Bearer ' + this.token }
2510
+ });
1319
2511
 
1320
- step.name = jsonStep.streetName;
1321
- step.levelChange = null;
2512
+ return this.createRouterResponseFromJson(response);
2513
+ }
1322
2514
 
1323
- step.distance = jsonStep.distance;
2515
+ _connect() {
1324
2516
 
1325
- return step;
1326
- });
1327
- }
2517
+ const details = {
2518
+ 'grant_type': 'client_credentials',
2519
+ 'scope': 'read-data',
2520
+ 'client_id': clientId,
2521
+ 'client_secret': clientSecret
2522
+ };
1328
2523
 
1329
- /**
1330
- * Generate multi itineraries from OTP JSON
1331
- * @param {object} json JSON file provided by OTP.
1332
- * @returns {?RouterResponse}
1333
- */
1334
- function createRouterResponseFromJson$2(json) {
2524
+ const data = new URLSearchParams();
2525
+ for (const property in details) {
2526
+ if (details.hasOwnProperty(property)) {
2527
+ const encodedKey = encodeURIComponent(property);
2528
+ const encodedValue = encodeURIComponent(details[property]);
2529
+ data.append(encodedKey, encodedValue);
2530
+ }
2531
+ }
1335
2532
 
1336
- const { plan: jsonPlan } = json;
2533
+ return request('https://idfm.getwemap.com/api/oauth/token', {
2534
+ method: 'POST',
2535
+ data
2536
+ }).then((response) => {
2537
+ if (response.access_token) {
2538
+ this.token = response.access_token;
2539
+ this.isLogged = true;
2540
+ this.expiresAt = new Date(new Date().getTime() + response.expires_in * 1000);
2541
+ }
2542
+ });
2543
+ }
1337
2544
 
1338
- if (!jsonPlan) {
1339
- return null;
2545
+ canRequestService() {
2546
+ return this.token && this.expiresAt && this.expiresAt - new Date() > 0;
1340
2547
  }
1341
2548
 
1342
- const routerResponse = new RouterResponse();
1343
- routerResponse.routerName = 'otp';
2549
+ /**
2550
+ * @param {string} endpointUrl
2551
+ * @param {string} mode
2552
+ * @param {Array<Coordinates>} waypoints
2553
+ */
2554
+ getURL(endpointUrl, mode, waypoints) {
1344
2555
 
1345
- routerResponse.from = jsonToCoordinates$1(jsonPlan.from);
1346
- routerResponse.to = jsonToCoordinates$1(jsonPlan.to);
2556
+ if (waypoints.length > 2) {
2557
+ Logger.warn(`${this.rname} router uses only the first 2 waypoints (asked ${waypoints.length})`);
2558
+ }
1347
2559
 
1348
- for (const jsonItinerary of jsonPlan.itineraries) {
2560
+ const fromPlace = `from=${waypoints[0].longitude};${waypoints[0].latitude}`;
2561
+ const toPlace = `to=${waypoints[1].longitude};${waypoints[1].latitude}`;
2562
+
2563
+ let url = new URL(endpointUrl);
2564
+ let { search } = url;
2565
+ search = (search ? `${search}&` : '?') + `${fromPlace}&${toPlace}`;
2566
+
2567
+ let query = '';
2568
+ switch (mode) {
2569
+ case Constants.ITINERARY_MODE.FOOT:
2570
+ query = this.getWalkingQuery();
2571
+ break;
2572
+ case Constants.ITINERARY_MODE.BIKE:
2573
+ query = this.getBikeQuery();
2574
+ break;
2575
+ case Constants.ITINERARY_MODE.DRIVING:
2576
+ query = this.getCarQuery();
2577
+ break;
2578
+ }
1349
2579
 
1350
- const itinerary = new Itinerary();
2580
+ url = `${url.origin}${url.pathname}${search}${query}`;
1351
2581
 
1352
- itinerary.duration = jsonItinerary.duration;
1353
- itinerary.startTime = jsonItinerary.startTime;
1354
- itinerary.endTime = jsonItinerary.endTime;
1355
- itinerary.from = routerResponse.from;
1356
- itinerary.to = routerResponse.to;
2582
+ return url;
2583
+ }
1357
2584
 
1358
- routerResponse.itineraries.push(itinerary);
2585
+ getCarQuery() {
2586
+ const forbiddenTransport = TRANSPORT_IDS.map((id) => `forbidden_uris[]=${id}`).join('&');
2587
+ const allowCar = 'first_section_mode[]=walking&first_section_mode[]=car&last_section_mode[]=walking&last_section_mode[]=car';
1359
2588
 
1360
- for (const jsonLeg of jsonItinerary.legs) {
2589
+ return `&${forbiddenTransport}&${allowCar}`;
2590
+ }
1361
2591
 
1362
- const leg = new Leg();
2592
+ getWalkingQuery() {
2593
+ const forbiddenTransport = TRANSPORT_IDS.map((id) => `forbidden_uris[]=${id}`).join('&');
2594
+ const allowWalking = 'first_section_mode[]=walking&last_section_mode[]=walking';
1363
2595
 
1364
- leg.mode = jsonLeg.mode;
1365
- leg.duration = jsonLeg.duration;
1366
- leg.startTime = jsonLeg.startTime;
1367
- leg.endTime = jsonLeg.endTime;
1368
- leg.from = {
1369
- name: jsonLeg.from.name,
1370
- coords: jsonToCoordinates$1(jsonLeg.from)
1371
- };
1372
- leg.to = {
1373
- name: jsonLeg.to.name,
1374
- coords: jsonToCoordinates$1(jsonLeg.to)
1375
- };
1376
- leg.coords = Polyline.decode(jsonLeg.legGeometry.points).map(([lat, lon]) => new Coordinates(lat, lon));
2596
+ return `&${forbiddenTransport}&${allowWalking}`;
2597
+ }
1377
2598
 
1378
- leg.steps = parseJsonSteps(jsonLeg.steps, leg.coords);
2599
+ getBikeQuery() {
2600
+ const forbiddenTransport = TRANSPORT_IDS.map((id) => `forbidden_uris[]=${id}`).join('&');
2601
+ const allowBike = 'first_section_mode[]=bike&last_section_mode[]=bike';
1379
2602
 
1380
- if (leg.mode === 'BUS' || leg.mode === 'TRAM') {
1381
- leg.transportInfo = {
1382
- name: jsonLeg.route,
1383
- routeColor: jsonLeg.routeColor,
1384
- routeTextColor: jsonLeg.routeTextColor,
1385
- directionName: jsonLeg.headsign
1386
- };
2603
+ return `&${forbiddenTransport}&${allowBike}`;
2604
+ }
1387
2605
 
1388
- const legStep = new Step();
1389
- legStep.coords = leg.coords[0];
1390
- legStep._idCoordsInLeg = 0;
1391
- legStep.name = jsonLeg.headsign;
1392
- legStep.levelChange = null;
1393
- legStep.distance = jsonLeg.distance;
1394
- leg.steps = [legStep];
1395
- }
2606
+ /**
2607
+ * @param {object} json
2608
+ * @returns {Coordinates}
2609
+ */
2610
+ jsonToCoordinates(json) {
2611
+ return new Coordinates(Number(json.lat), Number(json.lon));
2612
+ }
1396
2613
 
1397
- // jsonLeg.distance is not reliable when compared to the array of leg coords.
1398
- // leg.distance = jsonLeg.distance;
1399
- leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
1400
- if (idx === 0) {
1401
- return acc;
1402
- }
1403
- return acc + arr[idx - 1].distanceTo(coords);
1404
- }, 0);
2614
+ getSectionCoords(section) {
2615
+ const from = section.from.stop_point ? this.jsonToCoordinates(section.from.stop_point.coord) : this.jsonToCoordinates(section.from.address.coord);
2616
+ const to = section.to.stop_point ? this.jsonToCoordinates(section.to.stop_point.coord) : this.jsonToCoordinates(section.to.address.coord);
1405
2617
 
1406
- itinerary.legs.push(leg);
2618
+ return {
2619
+ from,
2620
+ to
2621
+ };
2622
+ }
1407
2623
 
1408
- }
2624
+ /**
2625
+ * Since the IDFM API does not provide coords for each step, we need to compute them
2626
+ * We trim the coordinates of the leg with the distance of each step and keep the last result as the coords of the step
2627
+ * @param {Leg} leg
2628
+ */
2629
+ findStepsCoord(leg) {
2630
+ const { steps, coords } = leg;
2631
+
2632
+ const duplicatedCoords = [...coords];
2633
+ let previousStep = steps[0];
2634
+ let accumulatedIndex = 0;
2635
+
2636
+ for (const [idx, step] of steps.entries()) {
2637
+ let newCoords;
1409
2638
 
1410
- itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
1411
2639
  if (idx === 0) {
1412
- return acc;
1413
- }
1414
- return acc + arr[idx - 1].distanceTo(coords);
1415
- }, 0);
2640
+ step._idCoordsInLeg = 0;
2641
+ newCoords = coords[0];
2642
+ } else if (idx === steps.length - 1) {
2643
+ step._idCoordsInLeg = coords.length - 1;
2644
+ newCoords = last(coords);
2645
+ } else if (duplicatedCoords.length === 1) {
2646
+ accumulatedIndex++;
1416
2647
 
1417
- // All legs have to be parsed before computing steps metadata
1418
- generateStepsMetadata(itinerary);
1419
- }
2648
+ step._idCoordsInLeg = accumulatedIndex;
1420
2649
 
1421
- return routerResponse;
1422
- }
2650
+ newCoords = duplicatedCoords[0];
1423
2651
 
1424
- var OtpUtils = /*#__PURE__*/Object.freeze({
1425
- __proto__: null,
1426
- jsonToCoordinates: jsonToCoordinates$1,
1427
- createRouterResponseFromJson: createRouterResponseFromJson$2
1428
- });
2652
+ coords[step._idCoordsInLeg] = newCoords;
2653
+ } else {
2654
+ const result = Utils.trimRoute(duplicatedCoords, duplicatedCoords[0], previousStep.distance);
2655
+ accumulatedIndex += result.length - 1;
1429
2656
 
1430
- /* eslint-disable max-depth */
2657
+ duplicatedCoords.splice(0, result.length - 1);
1431
2658
 
1432
- /**
1433
- * @param {object} json
1434
- * @returns {Coordinates}
1435
- */
1436
- function jsonToCoordinates(json) {
1437
- return new Coordinates(json.Lat, json.Long);
1438
- }
2659
+ step._idCoordsInLeg = accumulatedIndex;
1439
2660
 
1440
- /**
1441
- * @param {string} jsonDate
1442
- * @returns {number}
1443
- */
1444
- function jsonDateToTimestamp(jsonDate) {
1445
- const [dateStr, timeStr] = jsonDate.split(' ');
1446
- const [dayStr, monthStr, yearStr] = dateStr.split('/');
1447
- const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
1448
- const date = new Date(
1449
- Number(yearStr), Number(monthStr) - 1, Number(dayStr),
1450
- Number(hoursStr), Number(minutesStr), Number(secondsStr)
1451
- );
1452
- return date.getTime();
1453
- }
2661
+ newCoords = last(result);
1454
2662
 
1455
- /**
1456
- * @param {string} wktGeometry
1457
- * @returns {Coordinates[]}
1458
- */
1459
- function parseWKTGeometry(wktGeometry) {
1460
- const tmpCoordsStr = wktGeometry.match(/LINESTRING \((.*)\)/i);
1461
- if (!tmpCoordsStr) {
1462
- return null;
1463
- }
1464
- return tmpCoordsStr[1].split(',').map(str => {
1465
- const sp = str.trim().split(' ');
1466
- return new Coordinates(Number(sp[1]), Number(sp[0]));
1467
- });
1468
- }
2663
+ coords[step._idCoordsInLeg] = newCoords;
2664
+ }
1469
2665
 
1470
- /**
1471
- * @param {string} iso8601Duration
1472
- * @see https://stackoverflow.com/a/29153059/2239938
1473
- */
1474
- function parseDuration(iso8601Duration) {
1475
- const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
1476
-
1477
- var matches = iso8601Duration.match(iso8601DurationRegex);
1478
-
1479
- // const sign = typeof matches[1] === 'undefined' ? '+' : '-',
1480
- const years = typeof matches[2] === 'undefined' ? 0 : Number(matches[2]);
1481
- const months = typeof matches[3] === 'undefined' ? 0 : Number(matches[3]);
1482
- const weeks = typeof matches[4] === 'undefined' ? 0 : Number(matches[4]);
1483
- const days = typeof matches[5] === 'undefined' ? 0 : Number(matches[5]);
1484
- const hours = typeof matches[6] === 'undefined' ? 0 : Number(matches[6]);
1485
- const minutes = typeof matches[7] === 'undefined' ? 0 : Number(matches[7]);
1486
- const seconds = typeof matches[8] === 'undefined' ? 0 : Number(matches[8]);
1487
-
1488
- return seconds
1489
- + minutes * 60
1490
- + hours * 3600
1491
- + days * 86400
1492
- + weeks * (86400 * 7)
1493
- + months * (86400 * 30)
1494
- + years * (86400 * 365.25);
1495
- }
2666
+ step.coords = newCoords;
1496
2667
 
1497
- /**
1498
- * Generate multi itineraries from Cityway JSON
1499
- * @param {object} json JSON file provided by Cityway.
1500
- * @returns {?RouterResponse}
1501
- * @example https://preprod.api.lia2.cityway.fr/journeyplanner/api/opt/PlanTrips/json?DepartureLatitude=49.51509388236216&DepartureLongitude=0.09341749619366316&ArrivalLatitude=49.5067090188444&ArrivalLongitude=0.1694842115417831&DepartureType=COORDINATES&ArrivalType=COORDINATES
1502
- */
1503
- function createRouterResponseFromJson$1(json) {
2668
+ previousStep = step;
2669
+ }
2670
+ }
1504
2671
 
1505
- if (json.StatusCode !== 200 || !json.Data || !json.Data.length) {
1506
- return null;
2672
+ /**
2673
+ * @param {string} stringDate (e.g. 20211117T104516)
2674
+ * @returns {number}
2675
+ */
2676
+ dateStringToTimestamp(stringDate, timeZone) {
2677
+ const yearStr = stringDate.substr(0, 4);
2678
+ const monthStr = stringDate.substr(4, 2);
2679
+ const dayStr = stringDate.substr(6, 2);
2680
+ const hoursStr = stringDate.substr(9, 2);
2681
+ const minutesStr = stringDate.substr(11, 2);
2682
+ const secondsStr = stringDate.substr(13, 2);
2683
+
2684
+ return dateWithTimeZone(
2685
+ Number(yearStr),
2686
+ Number(monthStr) - 1,
2687
+ Number(dayStr),
2688
+ Number(hoursStr),
2689
+ Number(minutesStr),
2690
+ Number(secondsStr),
2691
+ timeZone
2692
+ ).getTime();
1507
2693
  }
1508
2694
 
1509
- const routerResponse = new RouterResponse();
1510
- routerResponse.routerName = 'cityway';
2695
+ /**
2696
+ * Generate multi itineraries from OTP JSON
2697
+ * @param {object} json JSON file provided by OTP.
2698
+ * @returns {?RouterResponse}
2699
+ */
2700
+ createRouterResponseFromJson(json) {
1511
2701
 
2702
+ if (!json || !json.journeys) {
2703
+ return null;
2704
+ }
1512
2705
 
1513
- // Do not know if it the best approach, but it works...
1514
- const allJsonTrips = json.Data.reduce((acc, dataObj) => {
1515
- acc.push(...dataObj.response.trips.Trip);
1516
- return acc;
1517
- }, []);
2706
+ const routerResponse = new RouterResponse();
2707
+ routerResponse.routerName = this.rname;
1518
2708
 
1519
- for (const trip of allJsonTrips) {
2709
+ routerResponse.from = this.getSectionCoords(json.journeys[0].sections[0]).from;
2710
+ routerResponse.to = this.getSectionCoords(last(json.journeys[0].sections)).to;
1520
2711
 
1521
- const itinerary = new Itinerary();
2712
+ const timeZone = json.context.timezone;
1522
2713
 
1523
- itinerary.duration = parseDuration(trip.Duration);
1524
- itinerary.startTime = jsonDateToTimestamp(trip.Departure.Time);
1525
- itinerary.from = jsonToCoordinates(trip.Departure.Site.Position);
1526
- itinerary.endTime = jsonDateToTimestamp(trip.Arrival.Time);
1527
- itinerary.to = jsonToCoordinates(trip.Arrival.Site.Position);
1528
- routerResponse.itineraries.push(itinerary);
2714
+ for (const jsonItinerary of json.journeys) {
1529
2715
 
1530
- for (const jsonSection of trip.sections.Section) {
2716
+ const itinerary = new Itinerary();
1531
2717
 
1532
- const jsonLeg = jsonSection.Leg ? jsonSection.Leg : jsonSection.PTRide;
2718
+ itinerary.duration = jsonItinerary.duration;
2719
+ itinerary.startTime = this.dateStringToTimestamp(jsonItinerary.departure_date_time, timeZone);
2720
+ itinerary.endTime = this.dateStringToTimestamp(jsonItinerary.arrival_date_time, timeZone);
2721
+ itinerary.from = routerResponse.from;
2722
+ itinerary.to = routerResponse.to;
2723
+ itinerary.distance = 0;
1533
2724
 
1534
- const leg = new Leg();
2725
+ routerResponse.itineraries.push(itinerary);
1535
2726
 
1536
- leg.mode = jsonLeg.TransportMode;
1537
- leg.duration = parseDuration(jsonLeg.Duration);
1538
- leg.startTime = jsonDateToTimestamp(jsonLeg.Departure.Time);
1539
- leg.endTime = jsonDateToTimestamp(jsonLeg.Arrival.Time);
1540
- leg.coords = [];
2727
+ for (const jsonSection of jsonItinerary.sections) {
1541
2728
 
1542
- if (leg.mode === 'WALK' || leg.mode === 'BICYCLE') {
2729
+ if (jsonSection.type === 'waiting' || jsonSection.type === 'transfer') {
2730
+ continue;
2731
+ }
2732
+
2733
+ const leg = new Leg();
2734
+ let existingCoords = [];
2735
+ const { from, to } = this.getSectionCoords(jsonSection);
2736
+
2737
+ leg.distance = 0;
2738
+ leg.mode = routingModeCorrespondance.get(jsonSection.mode);
2739
+ leg.duration = jsonSection.duration;
2740
+ leg.startTime = this.dateStringToTimestamp(jsonSection.departure_date_time, timeZone);
2741
+ leg.endTime = this.dateStringToTimestamp(jsonSection.arrival_date_time, timeZone);
1543
2742
 
1544
2743
  leg.from = {
1545
- name: jsonLeg.Departure.Site.Name,
1546
- coords: jsonToCoordinates(jsonLeg.Departure.Site.Position)
2744
+ name: jsonSection.from.name,
2745
+ coords: from
1547
2746
  };
2747
+
1548
2748
  leg.to = {
1549
- name: jsonLeg.Arrival.Site.Name,
1550
- coords: jsonToCoordinates(jsonLeg.Arrival.Site.Position)
2749
+ name: jsonSection.to.name,
2750
+ coords: to
1551
2751
  };
1552
2752
 
1553
- leg.steps = [];
1554
- for (const jsonPathLink of jsonLeg.pathLinks.PathLink) {
1555
- const step = new Step();
1556
- let stepCoords;
1557
- if (jsonPathLink.Geometry) {
1558
- stepCoords = parseWKTGeometry(jsonPathLink.Geometry);
1559
- } else {
1560
- stepCoords = [leg.from.coords, leg.to.coords];
2753
+ // A section can have multiple same coordinates, we need to remove them
2754
+ leg.coords = jsonSection.geojson.coordinates.reduce((acc, [lon, lat]) => {
2755
+ if (!existingCoords.includes(`${lon}-${lat}`)) {
2756
+ existingCoords = existingCoords.concat(`${lon}-${lat}`);
2757
+ acc.push(new Coordinates(lat, lon));
1561
2758
  }
1562
- step.coords = stepCoords[0];
1563
- step._idCoordsInLeg = leg.coords.length;
1564
- stepCoords.forEach((coords, idx) => {
1565
- if (
1566
- idx !== 0
1567
- || leg.coords.length === 0
1568
- || !leg.coords[leg.coords.length - 1].equalsTo(coords)
1569
- ) {
1570
- leg.coords.push(coords);
1571
- }
1572
- });
1573
2759
 
2760
+ return acc;
2761
+ }, []);
1574
2762
 
1575
- step.name = jsonPathLink.Departure.Site.Name;
1576
- step.levelChange = null;
2763
+ leg.steps = [];
1577
2764
 
1578
- step.distance = jsonPathLink.Distance;
2765
+ if (jsonSection.path) {
2766
+ for (const jsonPathLink of jsonSection.path) {
2767
+ const step = new Step();
1579
2768
 
1580
- leg.steps.push(step);
1581
- }
2769
+ step.levelChange = null;
1582
2770
 
1583
- } else if (leg.mode === 'BUS' || leg.mode === 'TRAMWAY' || leg.mode === 'FUNICULAR' || leg.mode === 'TRAIN') {
2771
+ step.name = jsonPathLink.name;
2772
+ step.distance = jsonPathLink.length;
1584
2773
 
1585
- leg.from = {
1586
- name: jsonLeg.Departure.StopPlace.Name,
1587
- coords: jsonToCoordinates(jsonLeg.Departure.StopPlace.Position)
1588
- };
1589
- leg.to = {
1590
- name: jsonLeg.Arrival.StopPlace.Name,
1591
- coords: jsonToCoordinates(jsonLeg.Arrival.StopPlace.Position)
1592
- };
2774
+ leg.distance += step.distance;
2775
+ leg.steps.push(step);
2776
+ }
1593
2777
 
1594
- let transportName = jsonLeg.Line.Number;
1595
- if (leg.mode === 'TRAMWAY') {
1596
- leg.mode = 'TRAM';
1597
- // In order to remove the "TRAM " prefix.
1598
- transportName = transportName.substr(5);
2778
+ this.findStepsCoord(leg);
1599
2779
  }
1600
2780
 
1601
- leg.transportInfo = {
1602
- name: transportName,
1603
- routeColor: jsonLeg.Line.Color,
1604
- routeTextColor: jsonLeg.Line.TextColor,
1605
- directionName: jsonLeg.Destination
1606
- };
2781
+ if (jsonSection.type === 'public_transport') {
2782
+ leg.transportInfo = {
2783
+ name: jsonSection.display_informations.code,
2784
+ routeColor: jsonSection.display_informations.color,
2785
+ routeTextColor: jsonSection.display_informations.text_color,
2786
+ directionName: jsonSection.display_informations.direction
2787
+ };
1607
2788
 
1608
- for (const jsonStep of jsonLeg.steps.Step) {
1609
- const stepCoords = parseWKTGeometry(jsonStep.Geometry);
1610
- stepCoords.forEach((coords, idx) => {
1611
- if (
1612
- idx !== 0
1613
- || leg.coords.length === 0
1614
- || !leg.coords[leg.coords.length - 1].equalsTo(coords)
1615
- ) {
1616
- leg.coords.push(coords);
1617
- }
1618
- });
1619
- }
2789
+ leg.mode = routingModeCorrespondance.get(jsonSection.display_informations.physical_mode);
1620
2790
 
1621
- const legStep = new Step();
1622
- legStep.coords = leg.coords[0];
1623
- legStep._idCoordsInLeg = 0;
1624
- legStep.name = jsonLeg.Line.Name;
1625
- legStep.levelChange = null;
1626
- legStep.distance = jsonLeg.Distance;
1627
- leg.steps = [legStep];
1628
- } else {
1629
- Logger.warn(`[CitywayParser] Unknown leg mode: ${leg.mode}`);
1630
- }
2791
+ const legStep = new Step();
2792
+ legStep.coords = leg.coords[0];
2793
+ legStep._idCoordsInLeg = 0;
2794
+ legStep.name = leg.transportInfo.directionName;
2795
+ legStep.levelChange = null;
2796
+ legStep.distance = jsonSection.geojson.properties[0].length;
1631
2797
 
1632
- leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
1633
- if (idx === 0) {
1634
- return acc;
2798
+ leg.steps = [legStep];
1635
2799
  }
1636
- return acc + arr[idx - 1].distanceTo(coords);
1637
- }, 0);
1638
2800
 
1639
- itinerary.legs.push(leg);
2801
+ itinerary.distance += leg.distance;
1640
2802
 
1641
- }
2803
+ itinerary.legs.push(leg);
1642
2804
 
1643
- itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
1644
- if (idx === 0) {
1645
- return acc;
1646
2805
  }
1647
- return acc + arr[idx - 1].distanceTo(coords);
1648
- }, 0);
1649
-
1650
- // All legs have to be parsed before computing steps metadata
1651
- generateStepsMetadata(itinerary);
1652
- }
1653
2806
 
1654
- routerResponse.from = routerResponse.itineraries[0].from;
1655
- routerResponse.to = routerResponse.itineraries[routerResponse.itineraries.length - 1].to;
2807
+ // All legs have to be parsed before computing steps metadata
2808
+ generateStepsMetadata(itinerary);
2809
+ }
1656
2810
 
1657
- return routerResponse;
2811
+ return routerResponse;
2812
+ }
1658
2813
  }
1659
2814
 
1660
- var CitywayUtils = /*#__PURE__*/Object.freeze({
1661
- __proto__: null,
1662
- jsonToCoordinates: jsonToCoordinates,
1663
- createRouterResponseFromJson: createRouterResponseFromJson$1
1664
- });
2815
+ var IdfmRemoteRouter$1 = new IdfmRemoteRouter();
1665
2816
 
1666
2817
  /* eslint-disable max-statements */
1667
2818
 
2819
+
1668
2820
  /**
1669
- * Generate multi itineraries from the DB JSON
1670
- * @param {object} json JSON file provided by the DB.
1671
- * @param {Coordinates} from itinerary start
1672
- * @param {Coordinates} to itinerary end
1673
- * @returns {?RouterResponse}
2821
+ * Singleton.
1674
2822
  */
1675
- function createRouterResponseFromJson(json, from, to) {
2823
+ class WemapMetaRemoteRouter extends RemoteRouter {
1676
2824
 
1677
- const { segments: jsonSegments } = json;
1678
-
1679
- if (!jsonSegments) {
1680
- return null;
2825
+ /**
2826
+ * @override
2827
+ */
2828
+ get rname() {
2829
+ return 'wemap-meta';
1681
2830
  }
1682
2831
 
1683
- const routerResponse = new RouterResponse();
1684
- routerResponse.routerName = 'deutsche-bahn';
1685
-
1686
- routerResponse.from = from;
1687
- routerResponse.to = to;
1688
-
1689
- /** @type {GraphEdge<OsmElement>[]} */
1690
- const edges = [];
1691
-
1692
- /** @type {GraphNode<OsmElement>[]} */
1693
- const nodes = [];
1694
-
1695
- /** @type {number[]} */
1696
- const edgesWeights = [];
1697
-
1698
- let id = 1;
1699
- for (const jsonSegment of jsonSegments) {
1700
-
1701
- const level = new Level(jsonSegment.fromLevel, jsonSegment.toLevel);
1702
- const osmWay = new OsmWay(id++, null, level);
1703
-
1704
- for (const jsonPoint of jsonSegment.polyline) {
1705
- const coord = new Coordinates(jsonPoint.lat, jsonPoint.lon, null, level);
2832
+ /**
2833
+ * @override
2834
+ */
2835
+ async getItineraries(endpointUrl, mode, waypoints, options) {
2836
+ const url = this.getURL(endpointUrl, mode, waypoints, options);
2837
+ const response = await request(url);
2838
+ return RouterResponse.fromJson(response);
2839
+ }
1706
2840
 
1707
- if (nodes.length !== 0
1708
- && nodes[nodes.length - 1].coords.equalsTo(coord)) {
1709
- continue;
1710
- }
2841
+ /**
2842
+ * @param {string} endpointUrl
2843
+ * @param {string} mode
2844
+ * @param {Array<Coordinates>} waypoints
2845
+ * @param {WemapMetaRemoteRouterOptions} options
2846
+ */
2847
+ getURL(endpointUrl, mode, waypoints, options) {
1711
2848
 
1712
- const osmNode = new OsmNode(id++, coord);
1713
- const node = new GraphNode(osmNode.coords, osmNode);
2849
+ const url = new URL(endpointUrl);
1714
2850
 
1715
- if (nodes.length !== 0) {
1716
- const prevNode = nodes[nodes.length - 1];
1717
- const edge = new GraphEdge(prevNode, node, level, osmWay);
1718
- edges.push(edge);
1719
- edgesWeights.push(prevNode.coords.distanceTo(osmNode));
2851
+ url.pathname = url.pathname + waypoints.map(waypoint => {
2852
+ if (waypoint.level) {
2853
+ const altitude = waypoint.level.isRange ? waypoint.level.low : waypoint.level.val;
2854
+ return waypoint.longitude + ',' + waypoint.latitude + ',' + altitude;
1720
2855
  }
2856
+ return waypoint.longitude + ',' + waypoint.latitude;
2857
+ }).join(';');
1721
2858
 
1722
- nodes.push(node);
2859
+ url.searchParams.set('externalRouterName', options.externalRouterName);
2860
+ url.searchParams.set('externalRouterEndpoint', encodeURIComponent(options.externalRouterEndpointUrl));
2861
+ url.searchParams.set('externalRouterMode', mode);
1723
2862
 
1724
- }
2863
+ return url;
1725
2864
  }
1726
-
1727
- /** @type {GraphItinerary<OsmElement>} */
1728
- const graphItinerary = new GraphItinerary();
1729
- graphItinerary.nodes = nodes;
1730
- graphItinerary.edges = edges;
1731
- graphItinerary.edgesWeights = edgesWeights;
1732
- graphItinerary.start = nodes[0].coords;
1733
- graphItinerary.end = nodes[nodes.length - 1].coords;
1734
-
1735
- const points = nodes.map(node => node.coords);
1736
- const itinerary = Itinerary.fromOrderedCoordinates(points, from, to);
1737
- itinerary.legs[0].steps = WemapStepsGeneration.fromGraphItinerary(graphItinerary);
1738
- itinerary.legs[0].steps.map((step, idx) => (step._idCoordsInLeg = idx));
1739
-
1740
- // All legs have to be parsed before computing steps metadata
1741
- generateStepsMetadata(itinerary);
1742
-
1743
- routerResponse.itineraries.push(itinerary);
1744
-
1745
- return routerResponse;
1746
2865
  }
1747
2866
 
1748
- var DeutscheBahnRouterUtils = /*#__PURE__*/Object.freeze({
1749
- __proto__: null,
1750
- createRouterResponseFromJson: createRouterResponseFromJson
1751
- });
2867
+ var WemapMetaRemoteRouter$1 = new WemapMetaRemoteRouter();
1752
2868
 
1753
2869
  /* eslint-disable max-statements */
1754
2870
 
@@ -1891,5 +3007,5 @@ class ItineraryInfoManager {
1891
3007
 
1892
3008
  }
1893
3009
 
1894
- export { CitywayUtils, DeutscheBahnRouterUtils, Itinerary, ItineraryInfo, ItineraryInfoManager, Leg, LevelChange, OsrmUtils, OtpUtils, RouterResponse, Step, WemapNetworkUtils, WemapRouter, WemapRouterOptions, WemapRouterUtils, generateStepsMetadata, getDurationFromLength };
3010
+ export { CitywayRemoteRouter$1 as CitywayRemoteRouter, DeutscheBahnRemoteRouter$1 as DeutscheBahnRemoteRouter, IdfmRemoteRouter$1 as IdfmRemoteRouter, Itinerary, ItineraryInfo, ItineraryInfoManager, Leg, LevelChange, OsrmRemoteRouter$1 as OsrmRemoteRouter, OtpRemoteRouter$1 as OtpRemoteRouter, RouterResponse, Step, WemapMetaRemoteRouter$1 as WemapMetaRemoteRouter, WemapNetworkUtils, WemapRouter, WemapRouterOptions, WemapRouterUtils, getDurationFromLength };
1895
3011
  //# sourceMappingURL=wemap-routers.es.js.map