@wemap/routers 6.2.2 → 6.2.3

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.
@@ -1,1895 +0,0 @@
1
- import { GraphUtils, Level, Coordinates, Network, GraphRouterOptions, GraphRouter, GraphEdge, GraphNode, GraphItinerary, MapMatching } from '@wemap/geo';
2
- import { OsmWay, OsmNode } from '@wemap/osm';
3
- import { diffAngle, deg2rad, positiveMod, rad2deg } from '@wemap/maths';
4
- import Polyline from '@mapbox/polyline';
5
- import Logger from '@wemap/logger';
6
-
7
- class LevelChange {
8
-
9
- /** @type {!string} [up|down] */
10
- direction;
11
-
12
- /** @type {!number} [-2, -1, 1, ...] */
13
- difference;
14
-
15
- /** @type {?string} [elevator|conveyor|stairs] */
16
- type = null;
17
-
18
- /**
19
- * @param {GraphNode<OsmElement>} firstNode
20
- * @param {GraphNode<OsmElement>} secondNode
21
- * @returns {LevelChange}
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';
39
-
40
- return levelChange;
41
- }
42
-
43
- /**
44
- * @returns {object}
45
- */
46
- toJson() {
47
- return {
48
- direction: this.direction,
49
- difference: this.difference,
50
- type: this.type
51
- };
52
- }
53
-
54
- /**
55
- * @param {object} json
56
- * @returns {LevelChange}
57
- */
58
- static fromJson(json) {
59
- const levelChange = new LevelChange();
60
- levelChange.direction = json.direction;
61
- levelChange.difference = json.difference;
62
- levelChange.type = json.type;
63
- return levelChange;
64
- }
65
- }
66
-
67
- class Step {
68
-
69
- /** @type {!boolean} */
70
- firstStep = false;
71
-
72
- /** @type {!boolean} */
73
- lastStep = false;
74
-
75
- /** @type {!number} */
76
- number;
77
-
78
- /** @type {!Coordinates} */
79
- coords = [];
80
-
81
-
82
- /** @type {!number} */
83
- angle;
84
-
85
- /** @type {!number} */
86
- previousBearing;
87
-
88
- /** @type {!number} */
89
- nextBearing;
90
-
91
-
92
- /** @type {!number} */
93
- distance;
94
-
95
- /** @type {?number} */
96
- duration = null;
97
-
98
- /** @type {?string} */
99
- name = null;
100
-
101
-
102
- /** @type {?LevelChange} */
103
- levelChange = null;
104
-
105
- /** @type {?{?subwayEntrance: boolean, ?subwayEntranceRef: string}} */
106
- extras = {};
107
-
108
- /** @type {!number} */
109
- _idCoordsInLeg = null;
110
-
111
- /**
112
- * @returns {object}
113
- */
114
- toJson() {
115
- const output = {
116
- number: this.number,
117
- coords: this.coords.toCompressedJson(),
118
- angle: this.angle,
119
- previousBearing: this.previousBearing,
120
- nextBearing: this.nextBearing,
121
- distance: this.distance,
122
- _idCoordsInLeg: this._idCoordsInLeg
123
- };
124
- if (this.firstStep) {
125
- output.firstStep = true;
126
- }
127
- if (this.lastStep) {
128
- output.lastStep = true;
129
- }
130
- if (this.duration !== null) {
131
- output.duration = this.duration;
132
- }
133
- if (this.name !== null) {
134
- output.name = this.name;
135
- }
136
- if (this.levelChange !== null) {
137
- output.levelChange = this.levelChange.toJson();
138
- }
139
- if (this.extras && Object.keys(this.extras).length !== 0) {
140
- output.extras = this.extras;
141
- }
142
- return output;
143
- }
144
-
145
- /**
146
- * @param {object} json
147
- * @returns {Step}
148
- */
149
- static fromJson(json) {
150
- const step = new Step();
151
- step.number = json.number;
152
- step.coords = Coordinates.fromCompressedJson(json.coords);
153
- step.angle = json.angle;
154
- step.previousBearing = json.previousBearing;
155
- step.nextBearing = json.nextBearing;
156
- step.distance = json.distance;
157
- step._idCoordsInLeg = json._idCoordsInLeg;
158
- if (json.firstStep) {
159
- step.firstStep = json.firstStep;
160
- }
161
- if (json.lastStep) {
162
- step.lastStep = json.lastStep;
163
- }
164
- if (json.duration) {
165
- step.duration = json.duration;
166
- }
167
- if (json.name) {
168
- step.name = json.name;
169
- }
170
- if (json.levelChange) {
171
- step.levelChange = LevelChange.fromJson(json.levelChange);
172
- }
173
- if (json.extras) {
174
- step.extras = json.extras;
175
- }
176
- return step;
177
- }
178
- }
179
-
180
- class Leg {
181
-
182
- /** @type {!string} can be WALK, BIKE, BUS, TRAM, CAR, FUNICULAR */
183
- mode;
184
-
185
- /** @type {!number} */
186
- distance;
187
-
188
- /** @type {!number} */
189
- duration;
190
-
191
- /** @type {?number} */
192
- startTime = null;
193
-
194
- /** @type {?number} */
195
- endTime = null;
196
-
197
- /** @type {!{name: ?string, coords: !Coordinates}} */
198
- from;
199
-
200
- /** @type {!{name: ?string, coords: !Coordinates}} */
201
- to;
202
-
203
- /** @type {!Coordinates[]} */
204
- coords;
205
-
206
- /** @type {?{name: !string, routeColor: ?string, routeTextColor: ?string, directionName: ?string}} */
207
- transportInfo = null;
208
-
209
- /** @type {?(Step[])} */
210
- steps = null;
211
-
212
- /**
213
- * @returns {Network}
214
- */
215
- toNetwork() {
216
- return Network.fromCoordinates([this.coords]);
217
- }
218
-
219
- /**
220
- * @returns {object}
221
- */
222
- toJson() {
223
- const output = {
224
- mode: this.mode,
225
- from: { coords: this.from.coords.toCompressedJson() },
226
- to: { coords: this.to.coords.toCompressedJson() },
227
- distance: this.distance,
228
- duration: this.duration,
229
- coords: this.coords.map(coords => coords.toCompressedJson())
230
- };
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
- if (this.startTime !== null) {
238
- output.startTime = this.startTime;
239
- }
240
- if (this.endTime !== null) {
241
- output.endTime = this.endTime;
242
- }
243
- if (this.transportInfo !== null) {
244
- output.transportInfo = this.transportInfo;
245
- }
246
- if (this.steps !== null && this.steps.length > 0) {
247
- output.steps = this.steps.map(step => step.toJson());
248
- }
249
- return output;
250
- }
251
-
252
-
253
- /**
254
- * @param {object} json
255
- * @returns {Leg}
256
- */
257
- static fromJson(json) {
258
- const leg = new Leg();
259
- leg.mode = json.mode;
260
- leg.from = { coords: Coordinates.fromCompressedJson(json.from.coords) };
261
- leg.to = { coords: Coordinates.fromCompressedJson(json.to.coords) };
262
- leg.distance = json.distance;
263
- 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) {
272
- leg.startTime = json.startTime;
273
- }
274
- if (json.endTime) {
275
- leg.endTime = json.endTime;
276
- }
277
- if (json.transportInfo) {
278
- leg.transportInfo = json.transportInfo;
279
- }
280
- if (json.steps) {
281
- leg.steps = json.steps.map(Step.fromJson);
282
- }
283
- return leg;
284
- }
285
-
286
- }
287
-
288
- /**
289
- * Get route duration
290
- * @param {Number} speed in km/h
291
- * @returns {Number} duration in seconds
292
- */
293
- function getDurationFromLength(length, speed = 5) {
294
- return length / (speed * 1000 / 3600);
295
- }
296
-
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
- /* eslint-disable max-statements */
350
-
351
- /**
352
- * Main attributes are:
353
- * nodes: the ordered list of Node
354
- * edges: the ordered list of Edge
355
- * start: the start point (Coordinates)
356
- * end: the end point (Coordinates)
357
- * length: the route length
358
- */
359
- class Itinerary {
360
-
361
- /** @type {!Coordinates} */
362
- from;
363
-
364
- /** @type {!Coordinates} */
365
- to;
366
-
367
- /** @type {!number} */
368
- distance;
369
-
370
- /** @type {!number} */
371
- duration;
372
-
373
- /** @type {!string} can be WALK, BIKE, CAR, PT */
374
- _mode;
375
-
376
- /** @type {?number} */
377
- startTime = null;
378
-
379
- /** @type {?number} */
380
- endTime = null;
381
-
382
- /** @type {!(Leg[])} */
383
- legs = [];
384
-
385
- /** @type {?Coordinates[]} */
386
- _coords = null;
387
-
388
- set coords(_) {
389
- throw new Error('Itinerary.coords cannot be set. They are calculated from Itinerary.legs.');
390
- }
391
-
392
- /** @type {!(Coordinates[])} */
393
- get coords() {
394
- if (!this._coords) {
395
- // Returns the coordinates contained in all legs and remove duplicates between array
396
- this._coords = this.legs.reduce((acc, val) => {
397
- const isDuplicate = acc.length && val.coords.length && acc[acc.length - 1].equalsTo(val.coords[0]);
398
- acc.push(...val.coords.slice(isDuplicate ? 1 : 0));
399
- return acc;
400
- }, []);
401
- }
402
- return this._coords;
403
- }
404
-
405
- set steps(_) {
406
- throw new Error('Itinerary.step cannot be set. They are calculated from Itinerary.legs.');
407
- }
408
-
409
- /** @type {!(Step[])} */
410
- get steps() {
411
- return this.legs.map(leg => leg.steps).flat();
412
- }
413
-
414
- set mode(_) {
415
- throw new Error('Itinerary.mode cannot be set. They are calculated from Itinerary.legs.');
416
- }
417
-
418
- get mode() {
419
- if (!this._mode) {
420
- let isPublicTransport;
421
- let isBicycle;
422
- let isDriving;
423
-
424
- 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';
428
- });
429
-
430
- if (isPublicTransport) {
431
- this._mode = 'PT';
432
- } else if (isDriving) {
433
- this._mode = 'CAR';
434
- } else if (isBicycle) {
435
- this._mode = 'BIKE';
436
- } else {
437
- this._mode = 'WALK';
438
- }
439
- }
440
-
441
- return this._mode;
442
-
443
- }
444
-
445
- /**
446
- * @returns {Network}
447
- */
448
- toNetwork() {
449
- return Network.fromCoordinates([this.coords]);
450
- }
451
-
452
- /**
453
- * @param {Itinerary[]} itineraries
454
- * @returns {Itinerary}
455
- */
456
- static fromItineraries(...itineraries) {
457
- const itinerary = new Itinerary();
458
- itinerary.from = itineraries[0].from;
459
- itinerary.to = itineraries[itineraries.length - 1].to;
460
- itinerary.distance = 0;
461
- itinerary.duration = 0;
462
- itinerary.legs = [];
463
-
464
- itineraries.forEach(_itinerary => {
465
- itinerary.distance += _itinerary.distance;
466
- itinerary.duration += _itinerary.duration;
467
- itinerary.legs.push(..._itinerary.legs);
468
- itinerary.legs.forEach(leg => {
469
- leg.steps[0].firstStep = false;
470
- leg.steps[leg.steps.length - 1].lastStep = false;
471
- });
472
- });
473
-
474
- itinerary.legs[0].steps[0].firstStep = true;
475
- const lastLeg = itinerary.legs[itinerary.legs.length - 1];
476
- lastLeg.steps[lastLeg.steps.length - 1].lastStep = true;
477
-
478
- return itinerary;
479
- }
480
-
481
- /**
482
- * Convert lat/lng/level points to Itinerary
483
- * @param {number[][]} points 2D points array of lat/lng/level (level is optional)
484
- * @param {Coordinates} from
485
- * @param {Coordinates} to
486
- * @param {string} mode
487
- * @returns {Itinerary}
488
- */
489
- static fromOrderedPointsArray(points, start, end) {
490
-
491
- const pointToCoordinates = point => new Coordinates(point[0], point[1], null, point[2]);
492
-
493
- return this.fromOrderedCoordinates(
494
- points.map(pointToCoordinates),
495
- pointToCoordinates(start),
496
- pointToCoordinates(end)
497
- );
498
- }
499
-
500
- /**
501
- * Convert ordered Coordinates to Itinerary
502
- * @param {Coordinates[]} points
503
- * @param {Coordinates} from
504
- * @param {Coordinates} to
505
- * @param {string} mode
506
- * @returns {Itinerary}
507
- */
508
- static fromOrderedCoordinates(points, from, to, mode = 'WALK') {
509
-
510
- const itinerary = new Itinerary();
511
- itinerary.from = from;
512
- itinerary.to = to;
513
-
514
- const leg = new Leg();
515
- leg.mode = mode;
516
- leg.from = { name: null, coords: from };
517
- leg.to = { name: null, coords: to };
518
-
519
- leg.coords = points;
520
- leg.distance = points.reduce((acc, coords, idx, arr) => {
521
- if (idx !== 0) {
522
- return acc + arr[idx - 1].distanceTo(coords);
523
- }
524
- return acc;
525
- }, 0);
526
- leg.duration = getDurationFromLength(leg.distance);
527
- itinerary.legs.push(leg);
528
-
529
- itinerary.distance = leg.distance;
530
- itinerary.duration = leg.duration;
531
-
532
- return itinerary;
533
- }
534
-
535
- /**
536
- * @returns {object}
537
- */
538
- toJson() {
539
- const output = {
540
- from: this.from.toCompressedJson(),
541
- to: this.to.toCompressedJson(),
542
- distance: this.distance,
543
- duration: this.duration,
544
- mode: this.mode,
545
- legs: this.legs.map(leg => leg.toJson())
546
- };
547
- if (this.startTime !== null) {
548
- output.startTime = this.startTime;
549
- }
550
- if (this.endTime !== null) {
551
- output.endTime = this.endTime;
552
- }
553
- return output;
554
- }
555
-
556
- /**
557
- * @param {object} json
558
- * @returns {Itinerary}
559
- */
560
- static fromJson(json) {
561
- const itinerary = new Itinerary();
562
- itinerary.from = Coordinates.fromCompressedJson(json.from);
563
- itinerary.to = Coordinates.fromCompressedJson(json.to);
564
- itinerary.distance = json.distance;
565
- itinerary.duration = json.duration;
566
- itinerary.legs = json.legs.map(Leg.fromJson);
567
- if (json.startTime) {
568
- itinerary.startTime = json.startTime;
569
- }
570
- if (json.endTime) {
571
- itinerary.endTime = json.endTime;
572
- }
573
- return itinerary;
574
- }
575
- }
576
-
577
- class RouterResponse {
578
-
579
- /** @type {!string} */
580
- routerName;
581
-
582
- /** @type {!Coordinates} */
583
- from;
584
-
585
- /** @type {!Coordinates} */
586
- to;
587
-
588
- /** @type {!(Itinerary[])} */
589
- itineraries = [];
590
- }
591
-
592
- class ItineraryInfo {
593
-
594
- /** @type {Step} */
595
- nextStep;
596
-
597
- /** @type {Step} */
598
- previousStep;
599
-
600
- /** @type {GraphProjection} */
601
- projection;
602
-
603
- /** @type {Leg} */
604
- leg;
605
-
606
- /** @type {number} */
607
- traveledDistance;
608
-
609
- /** @type {number} */
610
- traveledPercentage;
611
-
612
- /** @type {number} */
613
- remainingDistance;
614
-
615
- /** @type {number} */
616
- remainingPercentage;
617
-
618
- }
619
-
620
- class WemapRouterOptions extends GraphRouterOptions {
621
-
622
- /** @type {WemapRouterOptions} */
623
- static DEFAULT = new WemapRouterOptions();
624
-
625
- /**
626
- * @returns {WemapRouterOptions}
627
- */
628
- static get WITHOUT_STAIRS() {
629
- const options = new WemapRouterOptions();
630
- options.acceptEdgeFn = edge => edge.builtFrom.tags.highway !== 'steps';
631
- return options;
632
- }
633
-
634
- /**
635
- * Get route duration
636
- * @param {Number} speed in km/h
637
- */
638
- static getDurationFromLength(length, speed = 5) {
639
- return length / (speed * 1000 / 3600);
640
- }
641
-
642
- /** @type {function(GraphEdge<OsmElement>):boolean} */
643
- weightEdgeFn = edge => edge.builtFrom.isElevator ? 30 : WemapRouterOptions.getDurationFromLength(edge.length);
644
-
645
-
646
- }
647
-
648
- class WemapRouter extends GraphRouter {
649
-
650
- /**
651
- * @param {!Network<OsmElement>} network
652
- */
653
- constructor(network) {
654
- super(network);
655
- }
656
-
657
- /**
658
- * @param {Coordinates} start
659
- * @param {Coordinates} end
660
- * @param {WemapRouterOptions} options
661
- * @returns {GraphItinerary<OsmElement>}
662
- */
663
- getShortestPath(start, end, options = new WemapRouterOptions()) {
664
- return super.getShortestPath(start, end, options);
665
- }
666
-
667
- }
668
-
669
- /* eslint-disable complexity */
670
-
671
- const SKIP_STEP_ANGLE_MAX = deg2rad(20);
672
-
673
- class WemapStepsGeneration {
674
-
675
- /**
676
- * @param {GraphItinerary<OsmElement>} itinerary
677
- * @returns {Step[]}
678
- */
679
- static fromGraphItinerary(itinerary) {
680
-
681
- const steps = [];
682
-
683
- const { start, end, nodes, edges } = itinerary;
684
-
685
- let currentStep, previousStep;
686
- let previousBearing = start.bearingTo(nodes[0].coords);
687
-
688
- for (let i = 0; i < nodes.length - 1; i++) {
689
-
690
- const isFirstStep = !currentStep;
691
-
692
- const node = nodes[i];
693
- const nextNode = nodes[i + 1];
694
- const edge = edges[i];
695
-
696
- const bearing = edge.bearing;
697
- const angle = diffAngle(previousBearing, bearing + Math.PI);
698
-
699
- let splitByAngle = Math.abs(diffAngle(Math.PI, angle)) >= SKIP_STEP_ANGLE_MAX;
700
-
701
- const splitByLevel = edge.level && edge.level.isRange
702
- && node.coords.level && !node.coords.level.isRange;
703
- splitByAngle = splitByAngle && !(node.coords.level && node.coords.level.isRange);
704
-
705
- const splitStepCondition = splitByAngle || splitByLevel;
706
-
707
- const isSubwayEntrance = node ? node.builtFrom.tags.railway === 'subway_entrance' : false;
708
-
709
- // New step creation
710
- if (isFirstStep || splitStepCondition || isSubwayEntrance) {
711
-
712
- previousStep = currentStep;
713
-
714
- currentStep = new Step();
715
- currentStep.coords = node.coords;
716
- currentStep.number = steps.length + 1;
717
- currentStep.angle = angle;
718
- currentStep.previousBearing = previousBearing;
719
- currentStep.nextBearing = bearing;
720
- currentStep.name = edge.builtFrom.tags.name || null;
721
- currentStep.distance = 0;
722
- currentStep.duration = 0;
723
-
724
- if (isSubwayEntrance) {
725
- currentStep.extras.subwayEntrance = true;
726
- currentStep.name = node.builtFrom.tags.name;
727
- if (node.builtFrom.tags.ref) {
728
- currentStep.extras.subwayEntranceRef = node.builtFrom.tags.ref;
729
- }
730
- }
731
-
732
- if (splitByLevel) {
733
- currentStep.levelChange = LevelChange.fromTwoNodes(node, nextNode);
734
- }
735
-
736
- steps.push(currentStep);
737
-
738
- if (!previousStep) {
739
- currentStep.firstStep = true;
740
- }
741
- }
742
-
743
- currentStep.distance += edge.length;
744
- currentStep.duration += itinerary.edgesWeights[i];
745
- previousBearing = bearing;
746
- }
747
-
748
- 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);
754
- if (!Coordinates.equalsTo(lastNode.coords, end)) {
755
- lastStep.nextBearing = lastNode.coords.bearingTo(end);
756
- lastStep.angle = diffAngle(lastStep.previousBearing, lastStep.nextBearing + Math.PI);
757
- }
758
- lastStep.lastStep = true;
759
- steps.push(lastStep);
760
-
761
- return steps;
762
- }
763
-
764
- }
765
-
766
- /**
767
- * @param {GraphItinerary<OsmElement>} graphItinerary
768
- * @param {string} mode
769
- * @returns {Leg}
770
- */
771
- function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
772
-
773
- const leg = new Leg();
774
-
775
- leg.from = { coords: graphItinerary.start };
776
- leg.to = { coords: graphItinerary.end };
777
- leg.coords = graphItinerary.nodes.map(node => node.coords);
778
- leg.distance = graphItinerary.edges.reduce((acc, edge) => acc + edge.length, 0);
779
- leg.duration = graphItinerary.edgesWeights.reduce((acc, weight) => acc + weight, 0);
780
- leg.mode = mode;
781
- leg.steps = WemapStepsGeneration.fromGraphItinerary(graphItinerary);
782
-
783
- return leg;
784
- }
785
-
786
- /**
787
- * @param {GraphItinerary<OsmElement>} graphItinerary
788
- * @param {string} mode
789
- * @returns {Itinerary}
790
- */
791
- function createItineraryFromGraphItinerary(graphItinerary, mode = 'WALK') {
792
-
793
- const leg = createLegFromGraphItinerary(graphItinerary, mode);
794
-
795
- const itinerary = new Itinerary();
796
-
797
- itinerary.from = graphItinerary.start;
798
- itinerary.to = graphItinerary.end;
799
- itinerary.distance = leg.distance;
800
- itinerary.duration = leg.duration;
801
- itinerary.legs.push(leg);
802
-
803
- return itinerary;
804
- }
805
-
806
- var WemapRouterUtils = /*#__PURE__*/Object.freeze({
807
- __proto__: null,
808
- createLegFromGraphItinerary: createLegFromGraphItinerary,
809
- createItineraryFromGraphItinerary: createItineraryFromGraphItinerary
810
- });
811
-
812
- const HIGHWAYS_PEDESTRIANS = ['footway', 'steps', 'pedestrian', 'living_street', 'path', 'track', 'sidewalk'];
813
-
814
- const DEFAULT_WAY_SELECTOR = way => {
815
- return HIGHWAYS_PEDESTRIANS.includes(way.tags.highway)
816
- || way.tags.footway === 'sidewalk'
817
- || way.tags.public_transport === 'platform'
818
- || way.tags.railway === 'platform';
819
- };
820
-
821
- /**
822
- * @param {Network<OsmElement>} network
823
- * @param {string} name
824
- */
825
- function getNodeByName(network, name) {
826
- return network.nodes.find(({ builtFrom }) => builtFrom.tags.name === name);
827
- }
828
-
829
- /**
830
- * @param {Network<OsmElement>} network
831
- * @param {string} name
832
- */
833
- function getEdgeByName(network, name) {
834
- return network.edges.find(({ builtFrom }) => builtFrom.tags.name === name);
835
- }
836
-
837
- /**
838
- * @param {GraphEdge<OsmElement>} edge
839
- * @param {OsmWay} way
840
- * @returns {boolean}
841
- */
842
- function manageOneWay(edge, way) {
843
-
844
- const { highway, oneway, conveying } = way.tags;
845
-
846
- edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
847
- || (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
848
-
849
- if (edge.isOneway && conveying === 'backward') {
850
- const tmpNode = edge.node1;
851
- edge.node1 = edge.node2;
852
- edge.node2 = tmpNode;
853
- }
854
- }
855
-
856
- /**
857
- * @param {Network} networkModel
858
- * @param {GraphNode} node
859
- */
860
- function createNodesAndEdgesFromElevator(networkModel, node) {
861
-
862
- /** @type {GraphNode[]} */
863
- const createdNodes = [];
864
- const getOrCreateLevelNode = (level, builtFrom) => {
865
- let levelNode = createdNodes.find(({ coords }) => Level.equalsTo(level, coords.level));
866
- if (!levelNode) {
867
- levelNode = new GraphNode(node.coords.clone(), builtFrom);
868
- levelNode.coords.level = level;
869
- createdNodes.push(levelNode);
870
- networkModel.nodes.push(levelNode);
871
- }
872
- return levelNode;
873
- };
874
-
875
- // Create nodes from node.edges
876
- node.edges.forEach(edge => {
877
- if (edge.level.isRange) {
878
- throw new Error('Cannot handle this elevator edge due to ambiguity');
879
- }
880
-
881
- const levelNode = getOrCreateLevelNode(edge.level, node.builtFrom);
882
- if (edge.node1 === node) {
883
- edge.node1 = levelNode;
884
- } else {
885
- edge.node2 = levelNode;
886
- }
887
- levelNode.edges.push(edge);
888
- });
889
-
890
- // Create edges from createdNodes
891
- for (let i = 0; i < createdNodes.length; i++) {
892
- for (let j = i + 1; j < createdNodes.length; j++) {
893
-
894
- const createdNode1 = createdNodes[i];
895
- const createdNode2 = createdNodes[j];
896
-
897
- const newEdge = new GraphEdge(
898
- createdNode1,
899
- createdNode2,
900
- new Level(createdNode1.coords.level.val, createdNode2.coords.level.val),
901
- node.builtFrom
902
- );
903
- networkModel.edges.push(newEdge);
904
- }
905
- }
906
-
907
- // Remove the historical elevator node from the network
908
- networkModel.nodes = networkModel.nodes.filter(_node => _node !== node);
909
- }
910
-
911
-
912
- /**
913
- * @param {OsmModel} osmModel
914
- * @param {function} _waySelectionFilter
915
- * @returns {Network<OsmElement>}
916
- */
917
- function createNetworkFromOsmModel(
918
- osmModel,
919
- waySelectionFilter = DEFAULT_WAY_SELECTOR
920
- ) {
921
-
922
- const networkModel = new Network();
923
-
924
- const nodesCreated = {};
925
- const elevatorNodes = [];
926
-
927
- /**
928
- * @param {OsmNode} osmNode
929
- */
930
- const getOrCreateNode = osmNode => {
931
- let node = nodesCreated[osmNode.id];
932
- if (!node) {
933
- node = new GraphNode(osmNode.coords, osmNode);
934
- nodesCreated[osmNode.id] = node;
935
- networkModel.nodes.push(node);
936
-
937
- if (osmNode.tags.highway === 'elevator') {
938
- elevatorNodes.push(node);
939
- }
940
- }
941
- return node;
942
- };
943
-
944
- osmModel.ways.forEach(way => {
945
- if (!waySelectionFilter(way)) {
946
- return;
947
- }
948
-
949
- let firstNode = getOrCreateNode(way.nodes[0]);
950
- for (let i = 1; i < way.nodes.length; i++) {
951
- const secondNode = getOrCreateNode(way.nodes[i]);
952
-
953
- const edge = new GraphEdge(firstNode, secondNode, way.level, way);
954
- manageOneWay(edge, way);
955
- networkModel.edges.push(edge);
956
- firstNode = secondNode;
957
- }
958
-
959
- });
960
-
961
- elevatorNodes.forEach(node => {
962
- // We have to clone this node for each connected edge
963
- createNodesAndEdgesFromElevator(networkModel, node);
964
- });
965
-
966
- GraphNode.generateNodesLevels(networkModel.nodes);
967
-
968
- return networkModel;
969
- }
970
-
971
-
972
- // /**
973
- // * @param {GraphNode} node
974
- // * @param {object} tags
975
- // */
976
- // static _applyNodePropertiesFromTags(node, tags) {
977
- // node.name = tags.name || null;
978
- // node.subwayEntrance = tags.railway === 'subway_entrance';
979
- // if (node.subwayEntrance && tags.ref) {
980
- // node.subwayEntranceRef = tags.ref;
981
- // }
982
- // }
983
-
984
- // /**
985
- // * @param {GraphEdge} edge
986
- // * @param {object} tags
987
- // */
988
- // static _applyEdgePropertiesFromTags(edge, tags) {
989
- // const { highway, oneway, conveying, name } = tags;
990
- // edge.name = name || null;
991
- // edge.isStairs = highway === 'steps';
992
- // edge.isConveying = 'conveying' in tags;
993
- // edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
994
- // || (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
995
-
996
- // if (conveying === 'backward') {
997
- // const tmpNode = edge.node1;
998
- // edge.node1 = edge.node2;
999
- // edge.node2 = tmpNode;
1000
- // }
1001
-
1002
- // }
1003
-
1004
- var WemapNetworkUtils = /*#__PURE__*/Object.freeze({
1005
- __proto__: null,
1006
- HIGHWAYS_PEDESTRIANS: HIGHWAYS_PEDESTRIANS,
1007
- DEFAULT_WAY_SELECTOR: DEFAULT_WAY_SELECTOR,
1008
- getNodeByName: getNodeByName,
1009
- getEdgeByName: getEdgeByName,
1010
- createNetworkFromOsmModel: createNetworkFromOsmModel
1011
- });
1012
-
1013
- /* eslint-disable max-statements */
1014
-
1015
- /**
1016
- * @param {Coordinates} coordinates
1017
- * @returns {object}
1018
- */
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
- }
1026
-
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
- }
1038
-
1039
- function nodesToJsonCoords(nodes) {
1040
- return nodes.map(node => coordinatesToJson(node.coords));
1041
- }
1042
-
1043
-
1044
- function getModifierFromAngle(_angle) {
1045
-
1046
- const angle = positiveMod(rad2deg(_angle), 360);
1047
-
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
- }
1071
-
1072
-
1073
- function noRouteFoundJson(message) {
1074
- return {
1075
- 'code': 'NoRoute',
1076
- message
1077
- };
1078
- }
1079
-
1080
- /**
1081
- * @param {Itinerary} itinerary
1082
- * @returns {object}
1083
- */
1084
- function itineraryToOsrmJson(itinerary) {
1085
-
1086
- const lastLegId = itinerary.legs.length - 1;
1087
-
1088
- const jsonLegs = itinerary.legs.map(({ distance, duration, coords, steps }, idLeg) => {
1089
-
1090
- const lastStepId = steps.length - 1;
1091
-
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
1119
- }
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;
1126
- }
1127
-
1128
- return jsonStep;
1129
- })
1130
- };
1131
- });
1132
-
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
- }
1147
- ],
1148
- 'waypoints': []
1149
- };
1150
- }
1151
-
1152
-
1153
- /**
1154
- * @param {object} jsonSteps
1155
- * @param {Coordinates[]} legCoords
1156
- * @returns {Step[]}
1157
- */
1158
- function parseJsonSteps$1(jsonSteps, legCoords) {
1159
-
1160
- if (!jsonSteps) {
1161
- return [];
1162
- }
1163
-
1164
- return jsonSteps.map(jsonStep => {
1165
-
1166
- const step = new Step();
1167
- step.coords = jsonToCoordinates$2(jsonStep.maneuver.location);
1168
-
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;
1178
-
1179
- step.name = jsonStep.name;
1180
- step.levelChange = jsonStep.levelChange ? LevelChange.fromJson(jsonStep.levelChange) : null;
1181
-
1182
- step.distance = jsonStep.distance;
1183
- step.duration = jsonStep.duration;
1184
-
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
- }
1190
- }
1191
-
1192
- return step;
1193
- });
1194
- }
1195
-
1196
-
1197
- /**
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}
1204
- */
1205
- function createRouterResponseFromJson$3(json, from, to, routingMode = 'walking') {
1206
-
1207
- const { routes: jsonRoutes } = json;
1208
-
1209
- if (!jsonRoutes) {
1210
- return null;
1211
- }
1212
-
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;
1218
-
1219
- const routerResponse = new RouterResponse();
1220
- routerResponse.routerName = 'osrm';
1221
-
1222
- routerResponse.from = from;
1223
- routerResponse.to = to;
1224
-
1225
- for (const jsonItinerary of jsonRoutes) {
1226
-
1227
- const itinerary = new Itinerary();
1228
-
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;
1234
-
1235
- routerResponse.itineraries.push(itinerary);
1236
-
1237
- for (const jsonLeg of jsonItinerary.legs) {
1238
-
1239
- const leg = new Leg();
1240
-
1241
- leg.mode = mode;
1242
- leg.distance = jsonLeg.distance;
1243
- leg.duration = jsonLeg.duration;
1244
-
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));
1250
-
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
- };
1259
-
1260
- leg.steps = parseJsonSteps$1(jsonLeg.steps, leg.coords);
1261
-
1262
- itinerary.legs.push(leg);
1263
- }
1264
-
1265
- // All legs have to be parsed before computing steps metadata
1266
- generateStepsMetadata(itinerary);
1267
-
1268
- }
1269
-
1270
- return routerResponse;
1271
- }
1272
-
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
- });
1283
-
1284
- /* eslint-disable max-statements */
1285
-
1286
- /**
1287
- * @param {object} json
1288
- * @returns {Coordinates}
1289
- */
1290
- function jsonToCoordinates$1(json) {
1291
- return new Coordinates(json.lat, json.lon);
1292
- }
1293
-
1294
- /**
1295
- * @param {object} jsonSteps
1296
- * @param {Coordinates[]} legCoords
1297
- * @returns {Step[]}
1298
- */
1299
- function parseJsonSteps(jsonSteps, legCoords) {
1300
-
1301
- if (!jsonSteps) {
1302
- return [];
1303
- }
1304
-
1305
- return jsonSteps.map(jsonStep => {
1306
-
1307
- const step = new Step();
1308
- const stepCoords = jsonToCoordinates$1(jsonStep);
1309
-
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;
1319
-
1320
- step.name = jsonStep.streetName;
1321
- step.levelChange = null;
1322
-
1323
- step.distance = jsonStep.distance;
1324
-
1325
- return step;
1326
- });
1327
- }
1328
-
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) {
1335
-
1336
- const { plan: jsonPlan } = json;
1337
-
1338
- if (!jsonPlan) {
1339
- return null;
1340
- }
1341
-
1342
- const routerResponse = new RouterResponse();
1343
- routerResponse.routerName = 'otp';
1344
-
1345
- routerResponse.from = jsonToCoordinates$1(jsonPlan.from);
1346
- routerResponse.to = jsonToCoordinates$1(jsonPlan.to);
1347
-
1348
- for (const jsonItinerary of jsonPlan.itineraries) {
1349
-
1350
- const itinerary = new Itinerary();
1351
-
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;
1357
-
1358
- routerResponse.itineraries.push(itinerary);
1359
-
1360
- for (const jsonLeg of jsonItinerary.legs) {
1361
-
1362
- const leg = new Leg();
1363
-
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));
1377
-
1378
- leg.steps = parseJsonSteps(jsonLeg.steps, leg.coords);
1379
-
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
- };
1387
-
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
- }
1396
-
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);
1405
-
1406
- itinerary.legs.push(leg);
1407
-
1408
- }
1409
-
1410
- itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
1411
- if (idx === 0) {
1412
- return acc;
1413
- }
1414
- return acc + arr[idx - 1].distanceTo(coords);
1415
- }, 0);
1416
-
1417
- // All legs have to be parsed before computing steps metadata
1418
- generateStepsMetadata(itinerary);
1419
- }
1420
-
1421
- return routerResponse;
1422
- }
1423
-
1424
- var OtpUtils = /*#__PURE__*/Object.freeze({
1425
- __proto__: null,
1426
- jsonToCoordinates: jsonToCoordinates$1,
1427
- createRouterResponseFromJson: createRouterResponseFromJson$2
1428
- });
1429
-
1430
- /* eslint-disable max-depth */
1431
-
1432
- /**
1433
- * @param {object} json
1434
- * @returns {Coordinates}
1435
- */
1436
- function jsonToCoordinates(json) {
1437
- return new Coordinates(json.Lat, json.Long);
1438
- }
1439
-
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
- }
1454
-
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
- }
1469
-
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
- }
1496
-
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) {
1504
-
1505
- if (json.StatusCode !== 200 || !json.Data || !json.Data.length) {
1506
- return null;
1507
- }
1508
-
1509
- const routerResponse = new RouterResponse();
1510
- routerResponse.routerName = 'cityway';
1511
-
1512
-
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
- }, []);
1518
-
1519
- for (const trip of allJsonTrips) {
1520
-
1521
- const itinerary = new Itinerary();
1522
-
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);
1529
-
1530
- for (const jsonSection of trip.sections.Section) {
1531
-
1532
- const jsonLeg = jsonSection.Leg ? jsonSection.Leg : jsonSection.PTRide;
1533
-
1534
- const leg = new Leg();
1535
-
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 = [];
1541
-
1542
- if (leg.mode === 'WALK' || leg.mode === 'BICYCLE') {
1543
-
1544
- leg.from = {
1545
- name: jsonLeg.Departure.Site.Name,
1546
- coords: jsonToCoordinates(jsonLeg.Departure.Site.Position)
1547
- };
1548
- leg.to = {
1549
- name: jsonLeg.Arrival.Site.Name,
1550
- coords: jsonToCoordinates(jsonLeg.Arrival.Site.Position)
1551
- };
1552
-
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];
1561
- }
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
-
1574
-
1575
- step.name = jsonPathLink.Departure.Site.Name;
1576
- step.levelChange = null;
1577
-
1578
- step.distance = jsonPathLink.Distance;
1579
-
1580
- leg.steps.push(step);
1581
- }
1582
-
1583
- } else if (leg.mode === 'BUS' || leg.mode === 'TRAMWAY' || leg.mode === 'FUNICULAR' || leg.mode === 'TRAIN') {
1584
-
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
- };
1593
-
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);
1599
- }
1600
-
1601
- leg.transportInfo = {
1602
- name: transportName,
1603
- routeColor: jsonLeg.Line.Color,
1604
- routeTextColor: jsonLeg.Line.TextColor,
1605
- directionName: jsonLeg.Destination
1606
- };
1607
-
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
- }
1620
-
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
- }
1631
-
1632
- leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
1633
- if (idx === 0) {
1634
- return acc;
1635
- }
1636
- return acc + arr[idx - 1].distanceTo(coords);
1637
- }, 0);
1638
-
1639
- itinerary.legs.push(leg);
1640
-
1641
- }
1642
-
1643
- itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
1644
- if (idx === 0) {
1645
- return acc;
1646
- }
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
-
1654
- routerResponse.from = routerResponse.itineraries[0].from;
1655
- routerResponse.to = routerResponse.itineraries[routerResponse.itineraries.length - 1].to;
1656
-
1657
- return routerResponse;
1658
- }
1659
-
1660
- var CitywayUtils = /*#__PURE__*/Object.freeze({
1661
- __proto__: null,
1662
- jsonToCoordinates: jsonToCoordinates,
1663
- createRouterResponseFromJson: createRouterResponseFromJson$1
1664
- });
1665
-
1666
- /* eslint-disable max-statements */
1667
-
1668
- /**
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}
1674
- */
1675
- function createRouterResponseFromJson(json, from, to) {
1676
-
1677
- const { segments: jsonSegments } = json;
1678
-
1679
- if (!jsonSegments) {
1680
- return null;
1681
- }
1682
-
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);
1706
-
1707
- if (nodes.length !== 0
1708
- && nodes[nodes.length - 1].coords.equalsTo(coord)) {
1709
- continue;
1710
- }
1711
-
1712
- const osmNode = new OsmNode(id++, coord);
1713
- const node = new GraphNode(osmNode.coords, osmNode);
1714
-
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));
1720
- }
1721
-
1722
- nodes.push(node);
1723
-
1724
- }
1725
- }
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
- }
1747
-
1748
- var DeutscheBahnRouterUtils = /*#__PURE__*/Object.freeze({
1749
- __proto__: null,
1750
- createRouterResponseFromJson: createRouterResponseFromJson
1751
- });
1752
-
1753
- /* eslint-disable max-statements */
1754
-
1755
- class ItineraryInfoManager {
1756
-
1757
- /** @type {Itinerary} */
1758
- _itinerary;
1759
-
1760
- /** @type {Network} */
1761
- _network;
1762
-
1763
- /** @type {Network} */
1764
- _mapMatching;
1765
-
1766
- /** @type {Step[]} */
1767
- _steps;
1768
-
1769
- /** @type {Step[]} */
1770
- _coordsNextStep;
1771
-
1772
- /** @type {Step[]} */
1773
- _coordsPreviousStep;
1774
-
1775
- /** @type {number[]} */
1776
- _coordsDistanceTraveled;
1777
-
1778
- /** @type {Leg[]} */
1779
- _coordsLeg;
1780
-
1781
- /** @type {Itinerary} */
1782
- set itinerary(itinerary) {
1783
-
1784
- if (itinerary === null) {
1785
- this._itinerary = null;
1786
- return;
1787
- }
1788
-
1789
- this._itinerary = itinerary;
1790
- this._steps = itinerary.steps;
1791
- this._network = itinerary.toNetwork();
1792
- this._mapMatching = new MapMatching(this._network);
1793
-
1794
- this._coordsNextStep = new Array(itinerary.coords.length);
1795
- this._coordsPreviousStep = new Array(itinerary.coords.length);
1796
- this._coordsDistanceTraveled = new Array(itinerary.coords.length);
1797
- this._coordsLeg = new Array(itinerary.coords.length);
1798
-
1799
- let stepId = 0;
1800
- let previousStep = null;
1801
- let nextStep = this._steps[0];
1802
- let distanceTraveled = 0;
1803
-
1804
- itinerary.coords.forEach((coords, idx, arr) => {
1805
- if (stepId < this._steps.length && this._steps[stepId].coords.equalsTo(coords)) {
1806
- previousStep = this._steps[stepId];
1807
- nextStep = stepId === this._steps.length - 1 ? null : this._steps[stepId + 1];
1808
- stepId++;
1809
- }
1810
- if (idx !== 0) {
1811
- distanceTraveled += arr[idx - 1].distanceTo(coords);
1812
- }
1813
-
1814
- this._coordsNextStep[idx] = nextStep;
1815
- this._coordsPreviousStep[idx] = previousStep;
1816
- this._coordsDistanceTraveled[idx] = distanceTraveled;
1817
- this._coordsLeg[idx] = itinerary.legs.find(leg => leg.coords.includes(coords));
1818
- });
1819
- }
1820
-
1821
- /**
1822
- * @param {Coordinates} position
1823
- * @returns {ItineraryInfo}
1824
- */
1825
- getInfo(position) {
1826
-
1827
- if (!this._itinerary) {
1828
- return null;
1829
- }
1830
-
1831
- if (!(position instanceof Coordinates)) {
1832
- return null;
1833
- }
1834
-
1835
- const projection = this._mapMatching.getProjection(position);
1836
- if (!projection) {
1837
- return null;
1838
- }
1839
-
1840
- let itineraryInfo = null;
1841
-
1842
- if (projection.nearestElement instanceof GraphNode) {
1843
- const idx = this._itinerary.coords.findIndex(
1844
- coords => projection.nearestElement.coords === coords
1845
- );
1846
- if (idx === -1) {
1847
- throw new Error('ItineraryInfoManager: could not find projection in itinerary (Node)');
1848
- }
1849
-
1850
- itineraryInfo = new ItineraryInfo();
1851
- itineraryInfo.nextStep = this._coordsNextStep[idx];
1852
- itineraryInfo.previousStep = this._coordsPreviousStep[idx];
1853
- itineraryInfo.projection = projection;
1854
- itineraryInfo.leg = this._coordsLeg[idx];
1855
- itineraryInfo.traveledDistance = this._coordsDistanceTraveled[idx];
1856
- itineraryInfo.remainingDistance = this._itinerary.distance - itineraryInfo.traveledDistance;
1857
- itineraryInfo.traveledPercentage = itineraryInfo.traveledDistance / this._itinerary.distance;
1858
- itineraryInfo.remainingPercentage = itineraryInfo.remainingDistance / this._itinerary.distance;
1859
-
1860
- } else if (projection.nearestElement instanceof GraphEdge) {
1861
-
1862
- let firstNode = projection.nearestElement.node1.coords;
1863
- let idx = this._itinerary.coords.findIndex(coords => firstNode === coords);
1864
- if (idx === -1) {
1865
- throw new Error('ItineraryInfoManager: could not find projection in itinerary (Edge)');
1866
- }
1867
-
1868
- // graphEdge is not necessarly ordered. We have to look for the first point
1869
- if (idx === this._itinerary.coords.length - 1
1870
- || this._itinerary.coords[idx + 1] !== projection.nearestElement.node2.coords
1871
- ) {
1872
- firstNode = projection.nearestElement.node2.coords;
1873
- idx--;
1874
- }
1875
-
1876
- itineraryInfo = new ItineraryInfo();
1877
- itineraryInfo.nextStep = this._coordsNextStep[idx];
1878
- itineraryInfo.previousStep = this._coordsPreviousStep[idx];
1879
- itineraryInfo.projection = projection;
1880
- itineraryInfo.leg = this._coordsLeg[idx];
1881
- itineraryInfo.traveledDistance = this._coordsDistanceTraveled[idx]
1882
- + projection.projection.distanceTo(firstNode);
1883
- itineraryInfo.remainingDistance = this._itinerary.distance - itineraryInfo.traveledDistance;
1884
- itineraryInfo.traveledPercentage = itineraryInfo.traveledDistance / this._itinerary.distance;
1885
- itineraryInfo.remainingPercentage = itineraryInfo.remainingDistance / this._itinerary.distance;
1886
-
1887
- }
1888
-
1889
- return itineraryInfo;
1890
- }
1891
-
1892
- }
1893
-
1894
- export { CitywayUtils, DeutscheBahnRouterUtils, Itinerary, ItineraryInfo, ItineraryInfoManager, Leg, LevelChange, OsrmUtils, OtpUtils, RouterResponse, Step, WemapNetworkUtils, WemapRouter, WemapRouterOptions, WemapRouterUtils, generateStepsMetadata, getDurationFromLength };
1895
- //# sourceMappingURL=wemap-routers.es.js.map