@wemap/geo 4.0.13 → 5.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.
@@ -1,529 +0,0 @@
1
- /* eslint-disable max-statements */
2
- import Coordinates from '../coordinates/Coordinates.js';
3
- import Level from '../coordinates/Level.js';
4
-
5
- import Edge from './Edge.js';
6
- import ItineraryInfo from './ItineraryInfo.js';
7
- import MapMatching from './MapMatching.js';
8
- import Network from './Network.js';
9
- import Node from './Node.js';
10
- import Step from './Step.js';
11
- import StepsGeneration from './StepsGeneration.js';
12
- import { getDurationFromLength } from './Utils.js';
13
-
14
- /**
15
- * Main attributes are:
16
- * nodes: the ordered list of Node
17
- * edges: the ordered list of Edge
18
- * start: the start point (Coordinates)
19
- * end: the end point (Coordinates)
20
- * length: the route length
21
- */
22
- class Itinerary extends Network {
23
-
24
- /** @type {?Coordinates} */
25
- start;
26
-
27
- /** @type {?Coordinates} */
28
- end;
29
-
30
- /** @type {?Step[]} */
31
- _steps;
32
-
33
- /** @type {number} */
34
- _length;
35
-
36
- /** @type {boolean[]} */
37
- _edgesDirectionReversed;
38
-
39
- /** @type {number[]} */
40
- _nextStepsIndexes;
41
-
42
- /** @type {MapMatching} */
43
- _mapMatching;
44
-
45
- /**
46
- * @param {Node[]} nodes
47
- * @param {Edge[]} edges
48
- */
49
- constructor(nodes, edges) {
50
- super(nodes, edges);
51
- this.start = null;
52
- this.end = null;
53
- this._steps = null;
54
- this._length = 0;
55
-
56
- // This array is for computational time gain only
57
- // It helps to know if this.edges is in the same direction than the current itinerary
58
- // true = opposite direction
59
- // false = same direction
60
- // if this.edgeDirectionReversed[i] is true, this.edges[i].node2 is before
61
- // this.edges[i].node1 following the current itinerary
62
- this._edgesDirectionReversed = new Array(edges ? edges.length : 0);
63
-
64
- // This array is for computational time gain only
65
- // It helps to find next and previous steps by storing next steps indexes for each node
66
- // The size of this array is the same than this.nodes
67
- this._nextStepsIndexes = null;
68
-
69
- this._mapMatching = new MapMatching(this);
70
- }
71
-
72
- /**
73
- * Get route length
74
- * @type {number}
75
- */
76
- get length() {
77
- if (!this._length) {
78
- this._length = this.edges.reduce((acc, edge) => acc + edge.length, 0);
79
- }
80
- return this._length;
81
- }
82
-
83
- /**
84
- * Get route duration with default speed
85
- * @type {number}
86
- */
87
- get duration() {
88
- return this.getDuration();
89
- }
90
-
91
- /**
92
- * Get route duration
93
- * @param {number} speed in km/h
94
- */
95
- getDuration(speed) {
96
- return getDurationFromLength(this.length, speed);
97
- }
98
-
99
- /** @type {Step[]} */
100
- get steps() {
101
- if (!this._steps) {
102
- const {
103
- steps, nextStepsIndexes
104
- } = StepsGeneration.fromItinerary(this);
105
- this._steps = steps;
106
- this._nextStepsIndexes = nextStepsIndexes;
107
- }
108
- return this._steps;
109
- }
110
-
111
- /**
112
- * Get edge at given distance
113
- * @param {number} distance
114
- * @returns {Edge} the edge or null if not exists
115
- */
116
- getEdgeAt(distance) {
117
-
118
- if (distance === 0 && this.edges.length > 0) {
119
- return this.edges[0];
120
- }
121
-
122
- let sum = 0;
123
- for (let i = 0; i < this.edges.length; i++) {
124
- sum += this.edges[i].length;
125
- if (distance <= sum) {
126
- return this.edges[i];
127
- }
128
- }
129
- return null;
130
- }
131
-
132
- /**
133
- * @param {Edge|Node} graphElement
134
- * @returns {Node}
135
- */
136
- getNextNode(graphElement) {
137
- if (graphElement instanceof Edge) {
138
- const indexOfEdge = this.edges.indexOf(graphElement);
139
- if (indexOfEdge !== -1) {
140
- return this.nodes[indexOfEdge + 1];
141
- }
142
- }
143
-
144
- if (graphElement instanceof Node) {
145
- const indexOfNode = this.nodes.indexOf(graphElement);
146
- if (indexOfNode !== -1 && indexOfNode !== this.nodes.length - 1) {
147
- return this.nodes[indexOfNode + 1];
148
- }
149
- }
150
- return null;
151
- }
152
-
153
- /**
154
- * @param {Edge|Node} graphElement
155
- * @returns {Edge}
156
- */
157
- getNextEdge(graphElement) {
158
- if (graphElement instanceof Edge) {
159
- const indexOfEdge = this.edges.indexOf(graphElement);
160
- if (indexOfEdge !== -1 && indexOfEdge !== this.edges.length - 1) {
161
- return this.edges[indexOfEdge + 1];
162
- }
163
- }
164
-
165
- if (graphElement instanceof Node) {
166
- const indexOfNode = this.nodes.indexOf(graphElement);
167
- if (indexOfNode !== -1 && indexOfNode !== this.edges.length) {
168
- return this.edges[indexOfNode];
169
- }
170
- }
171
- return null;
172
- }
173
-
174
- /**
175
- * @param {Edge|Node} graphElement
176
- * @returns {Step}
177
- */
178
- getNextStep(graphElement) {
179
-
180
- if (graphElement instanceof Edge) {
181
- const indexOfEdge = this.edges.indexOf(graphElement);
182
- if (indexOfEdge !== -1) {
183
- return this._getNextStepFromEdgeId(indexOfEdge);
184
- }
185
- } else if (graphElement instanceof Node) {
186
- const indexOfNode = this.nodes.indexOf(graphElement);
187
- if (indexOfNode !== -1) {
188
- return this._getNextStepFromNodeId(indexOfNode);
189
- }
190
- }
191
- return null;
192
- }
193
-
194
- /**
195
- * @param {number} edgeId
196
- * @returns {Step}
197
- */
198
- _getNextStepFromEdgeId(edgeId) {
199
- return this.steps[this._nextStepsIndexes[edgeId + 1]];
200
- }
201
-
202
- /**
203
- * @param {number} nodeId
204
- * @returns {Step}
205
- */
206
- _getNextStepFromNodeId(nodeId) {
207
- return this.steps[this._nextStepsIndexes[nodeId]];
208
- }
209
-
210
- /**
211
- * @param {Edge|Node} graphElement
212
- * @returns {Step}
213
- */
214
- getPreviousStep(graphElement) {
215
-
216
- if (graphElement instanceof Edge) {
217
- const indexOfEdge = this.edges.indexOf(graphElement);
218
- if (indexOfEdge !== -1) {
219
- return this._getPreviousStepFromEdgeId(indexOfEdge);
220
- }
221
- } else if (graphElement instanceof Node) {
222
- const indexOfNode = this.nodes.indexOf(graphElement);
223
- if (indexOfNode !== -1) {
224
- return this._getPreviousStepFromNodeId(indexOfNode);
225
- }
226
- }
227
- return null;
228
- }
229
-
230
- /**
231
- * @param {number} edgeId
232
- * @returns {Step}
233
- */
234
- _getPreviousStepFromEdgeId(edgeId) {
235
- const previousStepId = this._nextStepsIndexes[edgeId + 1] - 1;
236
- return this.steps[previousStepId];
237
- }
238
-
239
- /**
240
- * @param {number} nodeId
241
- * @returns {Step}
242
- */
243
- _getPreviousStepFromNodeId(nodeId) {
244
- const previousStepId = this._nextStepsIndexes[nodeId] - 1;
245
- if (previousStepId !== -1) {
246
- return this.steps[previousStepId];
247
- }
248
- return null;
249
- }
250
-
251
- /**
252
- * @param {Coordinates} position
253
- * @returns {ItineraryInfo}
254
- */
255
- getInfo(position) {
256
-
257
- if (!(position instanceof Coordinates)) {
258
- return null;
259
- }
260
-
261
- const projection = this._mapMatching.getProjection(position);
262
- if (!projection) {
263
- return null;
264
- }
265
-
266
- const totalDistance = this.length;
267
- let traveledDistance = 0;
268
- let nextStep, previousStep;
269
-
270
- if (projection.nearestElement instanceof Node) {
271
-
272
- let currentNodeIndex = 0;
273
- while (this.nodes[currentNodeIndex] !== projection.nearestElement) {
274
- traveledDistance += this.edges[currentNodeIndex].length;
275
- currentNodeIndex++;
276
- }
277
- nextStep = this._getNextStepFromNodeId(currentNodeIndex);
278
- previousStep = this._getPreviousStepFromNodeId(currentNodeIndex);
279
-
280
- } else {
281
- // if (projection.nearestElement instanceof Edge)
282
- let currentEdgeIndex = 0;
283
- while (this.edges[currentEdgeIndex] !== projection.nearestElement) {
284
- traveledDistance += this.edges[currentEdgeIndex].length;
285
- currentEdgeIndex++;
286
- }
287
- traveledDistance += this.nodes[currentEdgeIndex].coords
288
- .distanceTo(projection.projection);
289
- nextStep = this._getNextStepFromEdgeId(currentEdgeIndex);
290
- previousStep = this._getPreviousStepFromEdgeId(currentEdgeIndex);
291
-
292
- }
293
-
294
- const itineraryInfo = new ItineraryInfo();
295
- return Object.assign(itineraryInfo, {
296
- nextStep,
297
- previousStep,
298
- projection,
299
- traveledDistance,
300
- traveledPercentage: traveledDistance / totalDistance,
301
- remainingDistance: totalDistance - traveledDistance,
302
- remainingPercentage: 1 - traveledDistance / totalDistance
303
- });
304
- }
305
-
306
- static fromNetworkNodes(nodes, start, end) {
307
-
308
- /**
309
- * Itinerary part of network is cloned and ordered
310
- */
311
-
312
- const itinerary = new Itinerary();
313
- itinerary._length = 0;
314
- itinerary.start = start;
315
- itinerary.end = end;
316
-
317
- for (let i = 0; i < nodes.length; i++) {
318
-
319
- const networkNode = nodes[i];
320
-
321
- itinerary.nodes.push(networkNode);
322
-
323
- if (i === nodes.length - 1) {
324
- break;
325
- }
326
-
327
- const nextNetworkNode = nodes[i + 1];
328
- const networkEdge = Network.getEdgeByNodes(networkNode.edges, networkNode, nextNetworkNode);
329
- itinerary._edgesDirectionReversed.push(networkNode !== networkEdge.node1);
330
-
331
- itinerary.edges.push(networkEdge);
332
- itinerary._length += networkEdge.length;
333
- }
334
-
335
- return itinerary;
336
- }
337
-
338
- /**
339
- * Convert lat/lng/level points to Itinerary
340
- * @param {2DArray} points 2D points array of lat/lng/level (level is optional)
341
- */
342
- static fromOrderedPointsArray(points, start, end) {
343
-
344
- const pointToCoordinates = point => new Coordinates(point[0], point[1], null, point[2]);
345
-
346
- return this.fromOrderedCoordinates(
347
- points.map(pointToCoordinates),
348
- pointToCoordinates(start),
349
- pointToCoordinates(end)
350
- );
351
- }
352
-
353
- /**
354
- * Convert lat/lng/level points to Itinerary
355
- * @param {2DArray} points 2D points array of lat/lng/level (level is optional)
356
- */
357
- static fromOrderedCoordinates(points, start, end) {
358
-
359
- const route = new Itinerary();
360
- route._length = 0;
361
- route.start = start;
362
- route.end = end;
363
-
364
- let lastPoint = null;
365
-
366
- points.forEach(point => {
367
- const currentPoint = new Node(point);
368
- if (lastPoint) {
369
- const edge = new Edge(
370
- lastPoint,
371
- currentPoint,
372
- Level.union(lastPoint.coords.level, currentPoint.coords.level)
373
- );
374
- route._edgesDirectionReversed.push(false);
375
- route.edges.push(edge);
376
- route._length += edge.length;
377
- }
378
-
379
- route.nodes.push(currentPoint);
380
- lastPoint = currentPoint;
381
- });
382
-
383
- return route;
384
- }
385
-
386
- /**
387
- * Create edges From MultiLevel Itinerary for a given level
388
- * @param {Level} targetLevel level for selection.
389
- * @param {Boolean} useMultiLevelEdges use segments which intersect both levels (stairs, elevators...)
390
- * @returns {Array[Edge]} Ordered edges
391
- */
392
- getEdgesAtLevel(targetLevel, useMultiLevelEdges = true) {
393
- return this.edges.filter(
394
- ({ level }) => useMultiLevelEdges
395
- ? Level.intersect(targetLevel, level) !== null
396
- : Level.contains(targetLevel, level)
397
- );
398
- }
399
-
400
- /**
401
- * Append another itinerary
402
- * @param {Itinerary} other
403
- * @returns {Itinerary} this
404
- */
405
- append(other) {
406
-
407
- if (!this.end.equalsTo(other.start)) {
408
- throw new Error('end of the first itinerary does not equal to the start of the second one');
409
- }
410
-
411
- this._steps = null;
412
- this._nextStepsIndexes = null;
413
- this._length = this.length + other.length;
414
-
415
- const lastNodeOfThis = this.nodes[this.nodes.length - 1];
416
- const firstNodeOfOther = other.nodes[0];
417
- const firstEdgeOfOther = other.edges[0];
418
-
419
- if (this.end.equalsTo(lastNodeOfThis.coords)
420
- && other.start.equalsTo(firstNodeOfOther.coords)) {
421
- // first itinerary end node equals to the second itinerary first node
422
-
423
- // Add to nodes other.nodes[1..end]
424
- this.nodes = this.nodes.concat(other.nodes.slice(1));
425
-
426
- // Add to edges other.edges[0..end]
427
- this.edges = this.edges.concat(other.edges);
428
- this._edgesDirectionReversed = this._edgesDirectionReversed.concat(other._edgesDirectionReversed);
429
- firstEdgeOfOther.node1 = lastNodeOfThis;
430
-
431
- } else if (this.end.equalsTo(lastNodeOfThis.coords)
432
- || other.start.equalsTo(firstNodeOfOther.coords)) {
433
- // last node and end of the first itinerary are merged
434
- // or
435
- // first node and start of the second itinerary are merged
436
-
437
- // Add to nodes other.nodes[0..end]
438
- this.nodes = this.nodes.concat(other.nodes);
439
-
440
- // Add to edges [newEdge].concat(other.edges[0..end])
441
- const newEdge = new Edge(lastNodeOfThis, firstNodeOfOther,
442
- Level.union(lastNodeOfThis.coords.level, firstNodeOfOther.coords.level)
443
- );
444
- this.edges.push(newEdge);
445
- this.edges = this.edges.concat(other.edges);
446
- this._edgesDirectionReversed.push(false);
447
- this._edgesDirectionReversed = this._edgesDirectionReversed.concat(other._edgesDirectionReversed);
448
-
449
- this._length += newEdge.length;
450
-
451
- } else {
452
- // normal case
453
-
454
- // Add to nodes [newNode].concat(other.nodes[0..end])
455
- const coords = this.end.clone();
456
- coords.level = Level.intersect(this.end.level, other.start.level);
457
- const newNode = new Node(this.end);
458
- this.nodes.push(newNode);
459
- this.nodes = this.nodes.concat(other.nodes);
460
-
461
- // Add to edges [newEdge1, newEdge2].concat(other.edges[0..end])
462
- const newEdge1 = new Edge(lastNodeOfThis, newNode,
463
- Level.union(lastNodeOfThis.coords.level, coords.level)
464
- );
465
- const newEdge2 = new Edge(newNode, firstNodeOfOther,
466
- Level.union(coords.level, firstNodeOfOther.coords.level)
467
- );
468
- this.edges.push(newEdge1);
469
- this.edges.push(newEdge2);
470
- this.edges = this.edges.concat(other.edges);
471
- this._edgesDirectionReversed.push(false);
472
- this._edgesDirectionReversed.push(false);
473
- this._edgesDirectionReversed = this._edgesDirectionReversed.concat(other._edgesDirectionReversed);
474
-
475
- this._length += newEdge1.length + newEdge2.length;
476
-
477
- }
478
-
479
- this.end = other.end;
480
-
481
- return this;
482
- }
483
-
484
- toCompressedJson() {
485
- return {
486
- start: this.start.toCompressedJson(),
487
- end: this.end.toCompressedJson(),
488
- coords: this.nodes.map(node => node.coords.toCompressedJson()),
489
- nodeProperties: this.nodes.reduce((acc, node, idx) => {
490
- const properties = node.extractProperties();
491
- if (Object.keys(properties).length !== 0) {
492
- acc[idx] = properties;
493
- }
494
- return acc;
495
- }, {}),
496
- edgeProperties: this.edges.reduce((acc, edge, idx) => {
497
- const properties = edge.extractProperties();
498
- if (Object.keys(properties).length !== 0) {
499
- acc[idx] = properties;
500
- }
501
- return acc;
502
- }, {})
503
- };
504
- }
505
-
506
- static fromCompressedJson(json) {
507
- const itinerary = Itinerary.fromOrderedCoordinates(
508
- json.coords.map(coords => Coordinates.fromCompressedJson(coords)),
509
- Coordinates.fromCompressedJson(json.start),
510
- Coordinates.fromCompressedJson(json.end)
511
- );
512
- if ('nodeProperties' in json) {
513
- for (const idx in json.nodeProperties) {
514
- if (json.nodeProperties.hasOwnProperty(idx)) {
515
- itinerary.nodes[idx].applyProperties(json.nodeProperties[idx]);
516
- }
517
- }
518
- }
519
- if ('edgeProperties' in json) {
520
- for (const idx in json.edgeProperties) {
521
- if (json.edgeProperties.hasOwnProperty(idx)) {
522
- itinerary.edges[idx].applyProperties(json.edgeProperties[idx]);
523
- }
524
- }
525
- }
526
- return itinerary;
527
- }
528
- }
529
- export default Itinerary;