@wemap/osm 5.1.0 → 5.1.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.
- package/dist/wemap-osm.es.js +1762 -0
- package/dist/wemap-osm.es.js.map +1 -0
- package/package.json +4 -4
- package/src/routers/Leg.js +16 -4
|
@@ -0,0 +1,1762 @@
|
|
|
1
|
+
import { Level, Coordinates, GraphRouterOptions, GraphRouter, GraphUtils, Network, GraphEdge, GraphNode, MapMatching } from '@wemap/geo';
|
|
2
|
+
import { SaxesParser } from 'saxes';
|
|
3
|
+
import { deg2rad, diffAngle, positiveMod, rad2deg } from '@wemap/maths';
|
|
4
|
+
import Polyline from '@mapbox/polyline';
|
|
5
|
+
|
|
6
|
+
class OsmElement {
|
|
7
|
+
|
|
8
|
+
/** @type {number|null} */
|
|
9
|
+
id;
|
|
10
|
+
|
|
11
|
+
/** @type {object} */
|
|
12
|
+
tags;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} id
|
|
16
|
+
* @param {object} tags
|
|
17
|
+
*/
|
|
18
|
+
constructor(id, tags) {
|
|
19
|
+
this.id = id;
|
|
20
|
+
this.tags = tags || {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class OsmWay extends OsmElement {
|
|
25
|
+
|
|
26
|
+
/** @type {OsmNode[]} */
|
|
27
|
+
nodes = [];
|
|
28
|
+
|
|
29
|
+
/** @type {?Level} */
|
|
30
|
+
level = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {number} id
|
|
34
|
+
* @param {object} tags
|
|
35
|
+
* @param {?Level} level
|
|
36
|
+
*/
|
|
37
|
+
constructor(id, tags, level) {
|
|
38
|
+
super(id, tags);
|
|
39
|
+
this.level = level || null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
get areStairs() {
|
|
46
|
+
return this.tags.highway === 'steps';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
get isConveying() {
|
|
53
|
+
return this.tags.hasOwnProperty('conveying');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
get isEscalator() {
|
|
60
|
+
return this.areStairs && this.isConveying;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
get isMovingWalkway() {
|
|
67
|
+
return !this.areStairs && this.isConveying;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
get isArea() {
|
|
74
|
+
// That is not the real definition for OSM
|
|
75
|
+
return this.nodes[0] === this.nodes[this.nodes.length - 1];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class OsmNode extends OsmElement {
|
|
80
|
+
|
|
81
|
+
/** @type {Coordinates} */
|
|
82
|
+
coords = null;
|
|
83
|
+
|
|
84
|
+
/** @type {OsmWay[]} */
|
|
85
|
+
ways = [];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {number} id
|
|
89
|
+
* @param {Coordinates} coords
|
|
90
|
+
* @param {object} tags
|
|
91
|
+
*/
|
|
92
|
+
constructor(id, coords, tags) {
|
|
93
|
+
super(id, tags);
|
|
94
|
+
this.coords = coords;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @returns {boolean}
|
|
99
|
+
*/
|
|
100
|
+
get isElevator() {
|
|
101
|
+
return this.tags.highway === 'elevator';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
get isConveying() {
|
|
108
|
+
return this.isElevator;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class OsmModel {
|
|
113
|
+
|
|
114
|
+
/** @type {OsmNode[]} */
|
|
115
|
+
nodes;
|
|
116
|
+
|
|
117
|
+
/** @type {OsmWay[]} */
|
|
118
|
+
ways;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {OsmNode[]|undefined} nodes
|
|
122
|
+
* @param {OsmWay[]|undefined} ways
|
|
123
|
+
*/
|
|
124
|
+
constructor(nodes, ways) {
|
|
125
|
+
this.nodes = nodes || [];
|
|
126
|
+
this.ways = ways || [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {number} id
|
|
131
|
+
* @returns {OsmNode|null}
|
|
132
|
+
*/
|
|
133
|
+
getNodeById(id) {
|
|
134
|
+
return this.nodes.find(node => node.id === id) || null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {string} name
|
|
139
|
+
* @returns {OsmNode|null}
|
|
140
|
+
*/
|
|
141
|
+
getNodeByName(name) {
|
|
142
|
+
return this.nodes.find(node => node.tags.name === name) || null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @param {number} id
|
|
148
|
+
* @returns {OsmWay|null}
|
|
149
|
+
*/
|
|
150
|
+
getWayById(id) {
|
|
151
|
+
return this.ways.find(way => way.id === id) || null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {string} name
|
|
156
|
+
* @returns {OsmWay|null}
|
|
157
|
+
*/
|
|
158
|
+
getWayByName(name) {
|
|
159
|
+
return this.ways.find(way => way.tags.name === name) || null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* eslint-disable max-statements */
|
|
165
|
+
|
|
166
|
+
class OsmParser {
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {string} osmXmlString
|
|
170
|
+
* @returns {OsmModel}
|
|
171
|
+
*/
|
|
172
|
+
static parseOsmXmlString(osmXmlString) {
|
|
173
|
+
|
|
174
|
+
const model = new OsmModel();
|
|
175
|
+
const parser = new SaxesParser(true);
|
|
176
|
+
|
|
177
|
+
let buffer;
|
|
178
|
+
|
|
179
|
+
const isDeleted = element => element.attributes.action && element.attributes.action === 'delete';
|
|
180
|
+
|
|
181
|
+
parser.on('opentag', (node) => {
|
|
182
|
+
|
|
183
|
+
switch (node.name) {
|
|
184
|
+
case 'node': {
|
|
185
|
+
if (isDeleted(node)) {
|
|
186
|
+
buffer = null;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const osmNode = this._parseNode(node.attributes);
|
|
190
|
+
buffer = osmNode;
|
|
191
|
+
model.nodes.push(osmNode);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case 'way': {
|
|
195
|
+
if (isDeleted(node)) {
|
|
196
|
+
buffer = null;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
const osmWay = this._parseWay(node.attributes);
|
|
200
|
+
buffer = osmWay;
|
|
201
|
+
model.ways.push(osmWay);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case 'tag': {
|
|
205
|
+
if (!buffer) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const {
|
|
209
|
+
k, v
|
|
210
|
+
} = node.attributes;
|
|
211
|
+
buffer.tags[k] = v;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case 'nd': {
|
|
215
|
+
if (!buffer || !(buffer instanceof OsmWay)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const nodeId = Number(node.attributes.ref);
|
|
219
|
+
const refNode = model.getNodeById(nodeId);
|
|
220
|
+
if (!refNode) {
|
|
221
|
+
throw Error('Node: ' + nodeId + ' in way ' + buffer.id + ' not found');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
buffer.nodes.push(refNode);
|
|
225
|
+
refNode.ways.push(buffer);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
parser.write(osmXmlString);
|
|
232
|
+
|
|
233
|
+
for (let i = 0; i < model.ways.length; i++) {
|
|
234
|
+
const way = model.ways[i];
|
|
235
|
+
if (way.tags.level) {
|
|
236
|
+
way.level = Level.fromString(way.tags.level);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return model;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
static _parseNode(attr) {
|
|
245
|
+
return new OsmNode(
|
|
246
|
+
Number(attr.id),
|
|
247
|
+
new Coordinates(Number(attr.lat), Number(attr.lon)));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static _parseWay(attr) {
|
|
251
|
+
return new OsmWay(Number(attr.id));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
class OsmRouterOptions extends GraphRouterOptions {
|
|
256
|
+
|
|
257
|
+
/** @type {OsmRouterOptions} */
|
|
258
|
+
static DEFAULT = new OsmRouterOptions();
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @returns {OsmRouterOptions}
|
|
262
|
+
*/
|
|
263
|
+
static get WITHOUT_STAIRS() {
|
|
264
|
+
const options = new OsmRouterOptions();
|
|
265
|
+
options.acceptEdgeFn = edge => edge.builtFrom.tags.highway !== 'steps';
|
|
266
|
+
return options;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get route duration
|
|
271
|
+
* @param {Number} speed in km/h
|
|
272
|
+
*/
|
|
273
|
+
static getDurationFromLength(length, speed = 5) {
|
|
274
|
+
return length / (speed * 1000 / 3600);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** @type {function(GraphEdge<OsmElement>):boolean} */
|
|
278
|
+
weightEdgeFn = edge => edge.builtFrom.isElevator ? 30 : OsmRouterOptions.getDurationFromLength(edge.length);
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
class OsmRouter extends GraphRouter {
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @param {!Network<OsmElement>} network
|
|
287
|
+
*/
|
|
288
|
+
constructor(network) {
|
|
289
|
+
super(network);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @param {Coordinates} start
|
|
294
|
+
* @param {Coordinates} end
|
|
295
|
+
* @param {OsmRouterOptions} options
|
|
296
|
+
* @returns {GraphItinerary<OsmElement>}
|
|
297
|
+
*/
|
|
298
|
+
getShortestPath(start, end, options = new OsmRouterOptions()) {
|
|
299
|
+
return super.getShortestPath(start, end, options);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
class LevelChange {
|
|
305
|
+
|
|
306
|
+
/** @type {!string} [up|down] */
|
|
307
|
+
direction;
|
|
308
|
+
|
|
309
|
+
/** @type {!number} [-2, -1, 1, ...] */
|
|
310
|
+
difference;
|
|
311
|
+
|
|
312
|
+
/** @type {?string} [elevator|conveyor|stairs] */
|
|
313
|
+
type = null;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @param {GraphNode<OsmElement>} firstNode
|
|
317
|
+
* @param {GraphNode<OsmElement>} secondNode
|
|
318
|
+
* @returns {LevelChange}
|
|
319
|
+
*/
|
|
320
|
+
static fromTwoNodes(firstNode, secondNode) {
|
|
321
|
+
|
|
322
|
+
const levelChange = new LevelChange();
|
|
323
|
+
|
|
324
|
+
const edge = GraphUtils.getEdgeByNodes(firstNode.edges, firstNode, secondNode);
|
|
325
|
+
|
|
326
|
+
if (edge.builtFrom.isElevator) {
|
|
327
|
+
levelChange.type = 'elevator';
|
|
328
|
+
} else if (edge.builtFrom.isConveying) {
|
|
329
|
+
levelChange.type = 'conveyor';
|
|
330
|
+
} else if (edge.builtFrom.areStairs) {
|
|
331
|
+
levelChange.type = 'stairs';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
levelChange.difference = Level.diff(firstNode.coords.level, secondNode.coords.level);
|
|
335
|
+
levelChange.direction = levelChange.difference > 0 ? 'up' : 'down';
|
|
336
|
+
|
|
337
|
+
return levelChange;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @returns {object}
|
|
342
|
+
*/
|
|
343
|
+
toJson() {
|
|
344
|
+
return {
|
|
345
|
+
direction: this.direction,
|
|
346
|
+
difference: this.difference,
|
|
347
|
+
type: this.type
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @param {object} json
|
|
353
|
+
* @returns {LevelChange}
|
|
354
|
+
*/
|
|
355
|
+
static fromJson(json) {
|
|
356
|
+
const levelChange = new LevelChange();
|
|
357
|
+
levelChange.direction = json.direction;
|
|
358
|
+
levelChange.difference = json.difference;
|
|
359
|
+
levelChange.type = json.type;
|
|
360
|
+
return levelChange;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
class Step {
|
|
365
|
+
|
|
366
|
+
/** @type {!boolean} */
|
|
367
|
+
firstStep = false;
|
|
368
|
+
|
|
369
|
+
/** @type {!boolean} */
|
|
370
|
+
lastStep = false;
|
|
371
|
+
|
|
372
|
+
/** @type {!number} */
|
|
373
|
+
number;
|
|
374
|
+
|
|
375
|
+
/** @type {!Coordinates} */
|
|
376
|
+
coords = [];
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
/** @type {!number} */
|
|
380
|
+
angle;
|
|
381
|
+
|
|
382
|
+
/** @type {!number} */
|
|
383
|
+
previousBearing;
|
|
384
|
+
|
|
385
|
+
/** @type {!number} */
|
|
386
|
+
nextBearing;
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
/** @type {!number} */
|
|
390
|
+
distance;
|
|
391
|
+
|
|
392
|
+
/** @type {?number} */
|
|
393
|
+
duration = null;
|
|
394
|
+
|
|
395
|
+
/** @type {?string} */
|
|
396
|
+
name = null;
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
/** @type {?LevelChange} */
|
|
400
|
+
levelChange = null;
|
|
401
|
+
|
|
402
|
+
/** @type {?{?subwayEntrance: boolean, ?subwayEntranceRef: string}} */
|
|
403
|
+
extras = {};
|
|
404
|
+
|
|
405
|
+
/** @type {!number} */
|
|
406
|
+
_idCoordsInLeg = null;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @returns {object}
|
|
410
|
+
*/
|
|
411
|
+
toJson() {
|
|
412
|
+
const output = {
|
|
413
|
+
number: this.number,
|
|
414
|
+
coords: this.coords.toCompressedJson(),
|
|
415
|
+
angle: this.angle,
|
|
416
|
+
previousBearing: this.previousBearing,
|
|
417
|
+
nextBearing: this.nextBearing,
|
|
418
|
+
distance: this.distance,
|
|
419
|
+
_idCoordsInLeg: this._idCoordsInLeg
|
|
420
|
+
};
|
|
421
|
+
if (this.firstStep) {
|
|
422
|
+
output.firstStep = true;
|
|
423
|
+
}
|
|
424
|
+
if (this.lastStep) {
|
|
425
|
+
output.lastStep = true;
|
|
426
|
+
}
|
|
427
|
+
if (this.duration !== null) {
|
|
428
|
+
output.duration = this.duration;
|
|
429
|
+
}
|
|
430
|
+
if (this.name !== null) {
|
|
431
|
+
output.name = this.name;
|
|
432
|
+
}
|
|
433
|
+
if (this.levelChange !== null) {
|
|
434
|
+
output.levelChange = this.levelChange.toJson();
|
|
435
|
+
}
|
|
436
|
+
if (this.extras !== null) {
|
|
437
|
+
output.extras = this.extras;
|
|
438
|
+
}
|
|
439
|
+
return output;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* @param {object} json
|
|
444
|
+
* @returns {Step}
|
|
445
|
+
*/
|
|
446
|
+
static fromJson(json) {
|
|
447
|
+
const step = new Step();
|
|
448
|
+
step.number = json.number;
|
|
449
|
+
step.coords = Coordinates.fromCompressedJson(json.coords);
|
|
450
|
+
step.angle = json.angle;
|
|
451
|
+
step.previousBearing = json.previousBearing;
|
|
452
|
+
step.nextBearing = json.nextBearing;
|
|
453
|
+
step.distance = json.distance;
|
|
454
|
+
step._idCoordsInLeg = json._idCoordsInLeg;
|
|
455
|
+
if (json.firstStep) {
|
|
456
|
+
step.firstStep = json.firstStep;
|
|
457
|
+
}
|
|
458
|
+
if (json.lastStep) {
|
|
459
|
+
step.lastStep = json.lastStep;
|
|
460
|
+
}
|
|
461
|
+
if (json.duration) {
|
|
462
|
+
step.duration = json.duration;
|
|
463
|
+
}
|
|
464
|
+
if (json.name) {
|
|
465
|
+
step.name = json.name;
|
|
466
|
+
}
|
|
467
|
+
if (json.levelChange) {
|
|
468
|
+
step.levelChange = LevelChange.fromJson(json.levelChange);
|
|
469
|
+
}
|
|
470
|
+
if (json.extras) {
|
|
471
|
+
step.extras = json.extras;
|
|
472
|
+
}
|
|
473
|
+
return step;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
class Leg {
|
|
478
|
+
|
|
479
|
+
/** @type {!string} can be WALK, BIKE, BUS, TRAM, CAR */
|
|
480
|
+
mode;
|
|
481
|
+
|
|
482
|
+
/** @type {!number} */
|
|
483
|
+
distance;
|
|
484
|
+
|
|
485
|
+
/** @type {!number} */
|
|
486
|
+
duration;
|
|
487
|
+
|
|
488
|
+
/** @type {?number} */
|
|
489
|
+
startTime = null;
|
|
490
|
+
|
|
491
|
+
/** @type {?number} */
|
|
492
|
+
endTime = null;
|
|
493
|
+
|
|
494
|
+
/** @type {!{name: ?string, coords: !Coordinates}} */
|
|
495
|
+
from;
|
|
496
|
+
|
|
497
|
+
/** @type {!{name: ?string, coords: !Coordinates}} */
|
|
498
|
+
to;
|
|
499
|
+
|
|
500
|
+
/** @type {!Coordinates[]} */
|
|
501
|
+
coords;
|
|
502
|
+
|
|
503
|
+
/** @type {?{name: !string, routeColor: ?string, routeTextColor: ?string, directionName: ?string}} */
|
|
504
|
+
transportInfo = null;
|
|
505
|
+
|
|
506
|
+
/** @type {?(Step[])} */
|
|
507
|
+
steps = null;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @returns {Network}
|
|
511
|
+
*/
|
|
512
|
+
toNetwork() {
|
|
513
|
+
return Network.fromCoordinates([this.coords]);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @returns {object}
|
|
518
|
+
*/
|
|
519
|
+
toJson() {
|
|
520
|
+
const output = {
|
|
521
|
+
mode: this.mode,
|
|
522
|
+
from: { coords: this.from.coords.toCompressedJson() },
|
|
523
|
+
to: { coords: this.to.coords.toCompressedJson() },
|
|
524
|
+
distance: this.distance,
|
|
525
|
+
duration: this.duration,
|
|
526
|
+
coords: this.coords.map(coords => coords.toCompressedJson())
|
|
527
|
+
};
|
|
528
|
+
if (this.from.name) {
|
|
529
|
+
output.from.name = this.from.name;
|
|
530
|
+
}
|
|
531
|
+
if (this.to.name) {
|
|
532
|
+
output.to.name = this.to.name;
|
|
533
|
+
}
|
|
534
|
+
if (this.startTime !== null) {
|
|
535
|
+
output.startTime = this.startTime;
|
|
536
|
+
}
|
|
537
|
+
if (this.endTime !== null) {
|
|
538
|
+
output.endTime = this.endTime;
|
|
539
|
+
}
|
|
540
|
+
if (this.transportInfo !== null) {
|
|
541
|
+
output.transportInfo = this.transportInfo;
|
|
542
|
+
}
|
|
543
|
+
if (this.steps !== null && this.steps.length > 0) {
|
|
544
|
+
output.steps = this.steps.map(step => step.toJson());
|
|
545
|
+
}
|
|
546
|
+
return output;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* @param {object} json
|
|
552
|
+
* @returns {Leg}
|
|
553
|
+
*/
|
|
554
|
+
static fromJson(json) {
|
|
555
|
+
const leg = new Leg();
|
|
556
|
+
leg.mode = json.mode;
|
|
557
|
+
leg.from = { coords: Coordinates.fromCompressedJson(json.from.coords) };
|
|
558
|
+
leg.to = { coords: Coordinates.fromCompressedJson(json.to.coords) };
|
|
559
|
+
leg.distance = json.distance;
|
|
560
|
+
leg.duration = json.duration;
|
|
561
|
+
leg.coords = json.coords.map(Coordinates.fromCompressedJson);
|
|
562
|
+
if (json.from.name) {
|
|
563
|
+
leg.from.name = json.from.name;
|
|
564
|
+
}
|
|
565
|
+
if (json.to.name) {
|
|
566
|
+
leg.to.name = json.to.name;
|
|
567
|
+
}
|
|
568
|
+
if (json.startTime) {
|
|
569
|
+
leg.startTime = json.startTime;
|
|
570
|
+
}
|
|
571
|
+
if (json.endTime) {
|
|
572
|
+
leg.endTime = json.endTime;
|
|
573
|
+
}
|
|
574
|
+
if (json.transportInfo) {
|
|
575
|
+
leg.transportInfo = json.transportInfo;
|
|
576
|
+
}
|
|
577
|
+
if (json.steps) {
|
|
578
|
+
leg.steps = json.steps.map(Step.fromJson);
|
|
579
|
+
}
|
|
580
|
+
return leg;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* eslint-disable max-statements */
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Main attributes are:
|
|
589
|
+
* nodes: the ordered list of Node
|
|
590
|
+
* edges: the ordered list of Edge
|
|
591
|
+
* start: the start point (Coordinates)
|
|
592
|
+
* end: the end point (Coordinates)
|
|
593
|
+
* length: the route length
|
|
594
|
+
*/
|
|
595
|
+
class Itinerary {
|
|
596
|
+
|
|
597
|
+
/** @type {!Coordinates} */
|
|
598
|
+
from;
|
|
599
|
+
|
|
600
|
+
/** @type {!Coordinates} */
|
|
601
|
+
to;
|
|
602
|
+
|
|
603
|
+
/** @type {!number} */
|
|
604
|
+
distance;
|
|
605
|
+
|
|
606
|
+
/** @type {!number} */
|
|
607
|
+
duration;
|
|
608
|
+
|
|
609
|
+
/** @type {?number} */
|
|
610
|
+
startTime = null;
|
|
611
|
+
|
|
612
|
+
/** @type {?number} */
|
|
613
|
+
endTime = null;
|
|
614
|
+
|
|
615
|
+
/** @type {!(Leg[])} */
|
|
616
|
+
legs = [];
|
|
617
|
+
|
|
618
|
+
/** @type {?Coordinates[]} */
|
|
619
|
+
_coords = null;
|
|
620
|
+
|
|
621
|
+
set coords(_) {
|
|
622
|
+
throw new Error('Itinerary.coords cannot be set. They are calculated from Itinerary.legs.');
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** @type {!(Coordinates[])} */
|
|
626
|
+
get coords() {
|
|
627
|
+
if (!this._coords) {
|
|
628
|
+
// Returns the coordinates contained in all legs and remove duplicates between array
|
|
629
|
+
this._coords = this.legs.reduce((acc, val) => {
|
|
630
|
+
const isDuplicate = acc.length && val.coords.length && acc[acc.length - 1].equalsTo(val.coords[0]);
|
|
631
|
+
acc.push(...val.coords.slice(isDuplicate ? 1 : 0));
|
|
632
|
+
return acc;
|
|
633
|
+
}, []);
|
|
634
|
+
}
|
|
635
|
+
return this._coords;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
set steps(_) {
|
|
639
|
+
throw new Error('Itinerary.step cannot be set. They are calculated from Itinerary.legs.');
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** @type {!(Step[])} */
|
|
643
|
+
get steps() {
|
|
644
|
+
return this.legs.map(leg => leg.steps).flat();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* @returns {Network}
|
|
649
|
+
*/
|
|
650
|
+
toNetwork() {
|
|
651
|
+
return Network.fromCoordinates([this.coords]);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* @param {Itinerary[]} itineraries
|
|
656
|
+
* @returns {Itinerary}
|
|
657
|
+
*/
|
|
658
|
+
static fromItineraries(...itineraries) {
|
|
659
|
+
const itinerary = new Itinerary();
|
|
660
|
+
itinerary.from = itineraries[0].from;
|
|
661
|
+
itinerary.to = itineraries[itineraries.length - 1].to;
|
|
662
|
+
itinerary.distance = 0;
|
|
663
|
+
itinerary.duration = 0;
|
|
664
|
+
itinerary.legs = [];
|
|
665
|
+
|
|
666
|
+
itineraries.forEach(_itinerary => {
|
|
667
|
+
itinerary.distance += _itinerary.distance;
|
|
668
|
+
itinerary.duration += _itinerary.duration;
|
|
669
|
+
itinerary.legs.push(..._itinerary.legs);
|
|
670
|
+
itinerary.legs.forEach(leg => {
|
|
671
|
+
leg.steps[0].firstStep = false;
|
|
672
|
+
leg.steps[leg.steps.length - 1].lastStep = false;
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
itinerary.legs[0].steps[0].firstStep = true;
|
|
677
|
+
const lastLeg = itinerary.legs[itinerary.legs.length - 1];
|
|
678
|
+
lastLeg.steps[lastLeg.steps.length - 1].lastStep = true;
|
|
679
|
+
|
|
680
|
+
return itinerary;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Convert lat/lng/level points to Itinerary
|
|
685
|
+
* @param {number[][]} points 2D points array of lat/lng/level (level is optional)
|
|
686
|
+
* @param {Coordinates} from
|
|
687
|
+
* @param {Coordinates} to
|
|
688
|
+
* @param {string} mode
|
|
689
|
+
* @returns {Itinerary}
|
|
690
|
+
*/
|
|
691
|
+
static fromOrderedPointsArray(points, start, end) {
|
|
692
|
+
|
|
693
|
+
const pointToCoordinates = point => new Coordinates(point[0], point[1], null, point[2]);
|
|
694
|
+
|
|
695
|
+
return this.fromOrderedCoordinates(
|
|
696
|
+
points.map(pointToCoordinates),
|
|
697
|
+
pointToCoordinates(start),
|
|
698
|
+
pointToCoordinates(end)
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Convert ordered Coordinates to Itinerary
|
|
704
|
+
* @param {Coordinates[]} points
|
|
705
|
+
* @param {Coordinates} from
|
|
706
|
+
* @param {Coordinates} to
|
|
707
|
+
* @param {string} mode
|
|
708
|
+
* @returns {Itinerary}
|
|
709
|
+
*/
|
|
710
|
+
static fromOrderedCoordinates(points, from, to, mode = 'WALK') {
|
|
711
|
+
|
|
712
|
+
const itinerary = new Itinerary();
|
|
713
|
+
itinerary.from = from;
|
|
714
|
+
itinerary.to = to;
|
|
715
|
+
|
|
716
|
+
const leg = new Leg();
|
|
717
|
+
leg.mode = mode;
|
|
718
|
+
leg.from = from;
|
|
719
|
+
leg.to = to;
|
|
720
|
+
|
|
721
|
+
leg.coords = points;
|
|
722
|
+
leg.distance = points.reduce((acc, coords, idx, arr) => {
|
|
723
|
+
if (idx !== 0) {
|
|
724
|
+
return acc + arr[idx - 1].distanceTo(coords);
|
|
725
|
+
}
|
|
726
|
+
return acc;
|
|
727
|
+
}, 0);
|
|
728
|
+
leg.duration = leg.distance;
|
|
729
|
+
itinerary.legs.push(leg);
|
|
730
|
+
|
|
731
|
+
itinerary.distance = leg.distance;
|
|
732
|
+
itinerary.duration = leg.duration;
|
|
733
|
+
|
|
734
|
+
return itinerary;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* @returns {object}
|
|
739
|
+
*/
|
|
740
|
+
toJson() {
|
|
741
|
+
const output = {
|
|
742
|
+
from: this.from.toCompressedJson(),
|
|
743
|
+
to: this.to.toCompressedJson(),
|
|
744
|
+
distance: this.distance,
|
|
745
|
+
duration: this.duration,
|
|
746
|
+
legs: this.legs.map(leg => leg.toJson())
|
|
747
|
+
};
|
|
748
|
+
if (this.startTime !== null) {
|
|
749
|
+
output.startTime = this.startTime;
|
|
750
|
+
}
|
|
751
|
+
if (this.endTime !== null) {
|
|
752
|
+
output.endTime = this.endTime;
|
|
753
|
+
}
|
|
754
|
+
return output;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* @param {object} json
|
|
759
|
+
* @returns {Itinerary}
|
|
760
|
+
*/
|
|
761
|
+
static fromJson(json) {
|
|
762
|
+
const itinerary = new Itinerary();
|
|
763
|
+
itinerary.from = Coordinates.fromCompressedJson(json.from);
|
|
764
|
+
itinerary.to = Coordinates.fromCompressedJson(json.to);
|
|
765
|
+
itinerary.distance = json.distance;
|
|
766
|
+
itinerary.duration = json.duration;
|
|
767
|
+
itinerary.legs = json.legs.map(Leg.fromJson);
|
|
768
|
+
if (json.startTime) {
|
|
769
|
+
itinerary.startTime = json.startTime;
|
|
770
|
+
}
|
|
771
|
+
if (json.endTime) {
|
|
772
|
+
itinerary.endTime = json.endTime;
|
|
773
|
+
}
|
|
774
|
+
return itinerary;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/* eslint-disable complexity */
|
|
779
|
+
|
|
780
|
+
const SKIP_STEP_ANGLE_MAX = deg2rad(20);
|
|
781
|
+
|
|
782
|
+
class StepsGeneration {
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* @param {GraphItinerary<OsmElement>} itinerary
|
|
786
|
+
* @returns {Step[]}
|
|
787
|
+
*/
|
|
788
|
+
static fromGraphItinerary(itinerary) {
|
|
789
|
+
|
|
790
|
+
const steps = [];
|
|
791
|
+
|
|
792
|
+
const { start, end, nodes, edges } = itinerary;
|
|
793
|
+
|
|
794
|
+
let currentStep, previousStep;
|
|
795
|
+
let previousBearing = start.bearingTo(nodes[0].coords);
|
|
796
|
+
|
|
797
|
+
for (let i = 0; i < nodes.length - 1; i++) {
|
|
798
|
+
|
|
799
|
+
const isFirstStep = !currentStep;
|
|
800
|
+
|
|
801
|
+
const node = nodes[i];
|
|
802
|
+
const nextNode = nodes[i + 1];
|
|
803
|
+
const edge = edges[i];
|
|
804
|
+
|
|
805
|
+
const bearing = edge.bearing;
|
|
806
|
+
const angle = diffAngle(previousBearing, bearing + Math.PI);
|
|
807
|
+
|
|
808
|
+
let splitByAngle = Math.abs(diffAngle(Math.PI, angle)) >= SKIP_STEP_ANGLE_MAX;
|
|
809
|
+
|
|
810
|
+
const splitByLevel = edge.level && edge.level.isRange
|
|
811
|
+
&& node.coords.level && !node.coords.level.isRange;
|
|
812
|
+
splitByAngle = splitByAngle && !(node.coords.level && node.coords.level.isRange);
|
|
813
|
+
|
|
814
|
+
const splitStepCondition = splitByAngle || splitByLevel;
|
|
815
|
+
|
|
816
|
+
const isSubwayEntrance = node ? node.builtFrom.tags.railway === 'subway_entrance' : false;
|
|
817
|
+
|
|
818
|
+
// New step creation
|
|
819
|
+
if (isFirstStep || splitStepCondition || isSubwayEntrance) {
|
|
820
|
+
|
|
821
|
+
previousStep = currentStep;
|
|
822
|
+
|
|
823
|
+
currentStep = new Step();
|
|
824
|
+
currentStep.coords = node.coords;
|
|
825
|
+
currentStep.number = steps.length + 1;
|
|
826
|
+
currentStep.angle = angle;
|
|
827
|
+
currentStep.previousBearing = previousBearing;
|
|
828
|
+
currentStep.nextBearing = bearing;
|
|
829
|
+
currentStep.name = edge.builtFrom.tags.name || null;
|
|
830
|
+
currentStep.distance = 0;
|
|
831
|
+
currentStep.duration = 0;
|
|
832
|
+
|
|
833
|
+
if (isSubwayEntrance) {
|
|
834
|
+
currentStep.extras.subwayEntrance = true;
|
|
835
|
+
currentStep.name = node.builtFrom.tags.name;
|
|
836
|
+
if (node.builtFrom.tags.ref) {
|
|
837
|
+
currentStep.extras.subwayEntranceRef = node.builtFrom.tags.ref;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (splitByLevel) {
|
|
842
|
+
currentStep.levelChange = LevelChange.fromTwoNodes(node, nextNode);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
steps.push(currentStep);
|
|
846
|
+
|
|
847
|
+
if (!previousStep) {
|
|
848
|
+
currentStep.firstStep = true;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
currentStep.distance += edge.length;
|
|
853
|
+
currentStep.duration += itinerary.edgesWeights[i];
|
|
854
|
+
previousBearing = bearing;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const lastNode = nodes[nodes.length - 1];
|
|
858
|
+
const lastStep = new Step();
|
|
859
|
+
lastStep.coords = lastNode.coords;
|
|
860
|
+
lastStep.number = steps.length + 1;
|
|
861
|
+
lastStep.previousBearing = previousBearing;
|
|
862
|
+
lastStep.distance = lastNode.coords.distanceTo(end);
|
|
863
|
+
if (!Coordinates.equalsTo(lastNode.coords, end)) {
|
|
864
|
+
lastStep.nextBearing = lastNode.coords.bearingTo(end);
|
|
865
|
+
lastStep.angle = diffAngle(lastStep.previousBearing, lastStep.nextBearing + Math.PI);
|
|
866
|
+
}
|
|
867
|
+
lastStep.lastStep = true;
|
|
868
|
+
steps.push(lastStep);
|
|
869
|
+
|
|
870
|
+
return steps;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* @param {GraphItinerary<OsmElement>} graphItinerary
|
|
877
|
+
* @param {string} mode
|
|
878
|
+
* @returns {Leg}
|
|
879
|
+
*/
|
|
880
|
+
function createLegFromGraphItinerary(graphItinerary, mode = 'WALK') {
|
|
881
|
+
|
|
882
|
+
const leg = new Leg();
|
|
883
|
+
|
|
884
|
+
leg.from = { coords: graphItinerary.start };
|
|
885
|
+
leg.to = { coords: graphItinerary.end };
|
|
886
|
+
leg.coords = graphItinerary.nodes.map(node => node.coords);
|
|
887
|
+
leg.distance = graphItinerary.edges.reduce((acc, edge) => acc + edge.length, 0);
|
|
888
|
+
leg.duration = graphItinerary.edgesWeights.reduce((acc, weight) => acc + weight, 0);
|
|
889
|
+
leg.mode = mode;
|
|
890
|
+
leg.steps = StepsGeneration.fromGraphItinerary(graphItinerary);
|
|
891
|
+
|
|
892
|
+
return leg;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* @param {GraphItinerary<OsmElement>} graphItinerary
|
|
897
|
+
* @param {string} mode
|
|
898
|
+
* @returns {Itinerary}
|
|
899
|
+
*/
|
|
900
|
+
function createItineraryFromGraphItinerary(graphItinerary, mode = 'WALK') {
|
|
901
|
+
|
|
902
|
+
const leg = createLegFromGraphItinerary(graphItinerary, mode);
|
|
903
|
+
|
|
904
|
+
const itinerary = new Itinerary();
|
|
905
|
+
|
|
906
|
+
itinerary.from = graphItinerary.start;
|
|
907
|
+
itinerary.to = graphItinerary.end;
|
|
908
|
+
itinerary.distance = leg.distance;
|
|
909
|
+
itinerary.duration = leg.duration;
|
|
910
|
+
itinerary.legs.push(leg);
|
|
911
|
+
|
|
912
|
+
return itinerary;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
var OsmRouterUtils = /*#__PURE__*/Object.freeze({
|
|
916
|
+
__proto__: null,
|
|
917
|
+
createLegFromGraphItinerary: createLegFromGraphItinerary,
|
|
918
|
+
createItineraryFromGraphItinerary: createItineraryFromGraphItinerary
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
const HIGHWAYS_PEDESTRIANS = ['footway', 'steps', 'pedestrian', 'living_street', 'path', 'track', 'sidewalk'];
|
|
922
|
+
|
|
923
|
+
const DEFAULT_WAY_SELECTOR = way => {
|
|
924
|
+
return HIGHWAYS_PEDESTRIANS.includes(way.tags.highway)
|
|
925
|
+
|| way.tags.footway === 'sidewalk'
|
|
926
|
+
|| way.tags.public_transport === 'platform'
|
|
927
|
+
|| way.tags.railway === 'platform';
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* @param {Network<OsmElement>} network
|
|
932
|
+
* @param {string} name
|
|
933
|
+
*/
|
|
934
|
+
function getNodeByName(network, name) {
|
|
935
|
+
return network.nodes.find(({ builtFrom }) => builtFrom.tags.name === name);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* @param {Network<OsmElement>} network
|
|
940
|
+
* @param {string} name
|
|
941
|
+
*/
|
|
942
|
+
function getEdgeByName(network, name) {
|
|
943
|
+
return network.edges.find(({ builtFrom }) => builtFrom.tags.name === name);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* @param {GraphEdge<OsmElement>} edge
|
|
948
|
+
* @param {OsmWay} way
|
|
949
|
+
* @returns {boolean}
|
|
950
|
+
*/
|
|
951
|
+
function manageOneWay(edge, way) {
|
|
952
|
+
|
|
953
|
+
const { highway, oneway, conveying } = way.tags;
|
|
954
|
+
|
|
955
|
+
edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
|
|
956
|
+
|| (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
|
|
957
|
+
|
|
958
|
+
if (edge.isOneway && conveying === 'backward') {
|
|
959
|
+
const tmpNode = edge.node1;
|
|
960
|
+
edge.node1 = edge.node2;
|
|
961
|
+
edge.node2 = tmpNode;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* @param {Network} networkModel
|
|
967
|
+
* @param {GraphNode} node
|
|
968
|
+
*/
|
|
969
|
+
function createNodesAndEdgesFromElevator(networkModel, node) {
|
|
970
|
+
|
|
971
|
+
/** @type {GraphNode[]} */
|
|
972
|
+
const createdNodes = [];
|
|
973
|
+
const getOrCreateLevelNode = level => {
|
|
974
|
+
let levelNode = createdNodes.find(({ coords }) => Level.equalsTo(level, coords.level));
|
|
975
|
+
if (!levelNode) {
|
|
976
|
+
levelNode = new GraphNode(node.coords.clone());
|
|
977
|
+
levelNode.coords.level = level;
|
|
978
|
+
createdNodes.push(levelNode);
|
|
979
|
+
networkModel.nodes.push(levelNode);
|
|
980
|
+
}
|
|
981
|
+
return levelNode;
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
// Create nodes from node.edges
|
|
985
|
+
node.edges.forEach(edge => {
|
|
986
|
+
if (edge.level.isRange) {
|
|
987
|
+
throw new Error('Cannot handle this elevator edge due to ambiguity');
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
const levelNode = getOrCreateLevelNode(edge.level);
|
|
991
|
+
if (edge.node1 === node) {
|
|
992
|
+
edge.node1 = levelNode;
|
|
993
|
+
} else {
|
|
994
|
+
edge.node2 = levelNode;
|
|
995
|
+
}
|
|
996
|
+
levelNode.edges.push(edge);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// Create edges from createdNodes
|
|
1000
|
+
for (let i = 0; i < createdNodes.length; i++) {
|
|
1001
|
+
for (let j = i + 1; j < createdNodes.length; j++) {
|
|
1002
|
+
|
|
1003
|
+
const createdNode1 = createdNodes[i];
|
|
1004
|
+
const createdNode2 = createdNodes[j];
|
|
1005
|
+
|
|
1006
|
+
const newEdge = new GraphEdge(
|
|
1007
|
+
createdNode1,
|
|
1008
|
+
createdNode2,
|
|
1009
|
+
new Level(createdNode1.coords.level.val, createdNode2.coords.level.val),
|
|
1010
|
+
node.builtFrom
|
|
1011
|
+
);
|
|
1012
|
+
networkModel.edges.push(newEdge);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Remove the historical elevator node from the network
|
|
1017
|
+
networkModel.nodes = networkModel.nodes.filter(_node => _node !== node);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* @param {OsmModel} osmModel
|
|
1023
|
+
* @param {function} _waySelectionFilter
|
|
1024
|
+
* @returns {Network<OsmElement>}
|
|
1025
|
+
*/
|
|
1026
|
+
function createNetworkFromOsmModel(
|
|
1027
|
+
osmModel,
|
|
1028
|
+
waySelectionFilter = DEFAULT_WAY_SELECTOR
|
|
1029
|
+
) {
|
|
1030
|
+
|
|
1031
|
+
const networkModel = new Network();
|
|
1032
|
+
|
|
1033
|
+
const nodesCreated = {};
|
|
1034
|
+
const elevatorNodes = [];
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* @param {OsmNode} osmNode
|
|
1038
|
+
*/
|
|
1039
|
+
const getOrCreateNode = osmNode => {
|
|
1040
|
+
let node = nodesCreated[osmNode.id];
|
|
1041
|
+
if (!node) {
|
|
1042
|
+
node = new GraphNode(osmNode.coords, osmNode);
|
|
1043
|
+
nodesCreated[osmNode.id] = node;
|
|
1044
|
+
networkModel.nodes.push(node);
|
|
1045
|
+
|
|
1046
|
+
if (osmNode.tags.highway === 'elevator') {
|
|
1047
|
+
elevatorNodes.push(node);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return node;
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
osmModel.ways.forEach(way => {
|
|
1054
|
+
if (!waySelectionFilter(way)) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
let firstNode = getOrCreateNode(way.nodes[0]);
|
|
1059
|
+
for (let i = 1; i < way.nodes.length; i++) {
|
|
1060
|
+
const secondNode = getOrCreateNode(way.nodes[i]);
|
|
1061
|
+
|
|
1062
|
+
const edge = new GraphEdge(firstNode, secondNode, way.level, way);
|
|
1063
|
+
manageOneWay(edge, way);
|
|
1064
|
+
networkModel.edges.push(edge);
|
|
1065
|
+
firstNode = secondNode;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
elevatorNodes.forEach(node => {
|
|
1071
|
+
// We have to clone this node for each connected edge
|
|
1072
|
+
createNodesAndEdgesFromElevator(networkModel, node);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
GraphNode.generateNodesLevels(networkModel.nodes);
|
|
1076
|
+
|
|
1077
|
+
return networkModel;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
// /**
|
|
1082
|
+
// * @param {GraphNode} node
|
|
1083
|
+
// * @param {object} tags
|
|
1084
|
+
// */
|
|
1085
|
+
// static _applyNodePropertiesFromTags(node, tags) {
|
|
1086
|
+
// node.name = tags.name || null;
|
|
1087
|
+
// node.subwayEntrance = tags.railway === 'subway_entrance';
|
|
1088
|
+
// if (node.subwayEntrance && tags.ref) {
|
|
1089
|
+
// node.subwayEntranceRef = tags.ref;
|
|
1090
|
+
// }
|
|
1091
|
+
// }
|
|
1092
|
+
|
|
1093
|
+
// /**
|
|
1094
|
+
// * @param {GraphEdge} edge
|
|
1095
|
+
// * @param {object} tags
|
|
1096
|
+
// */
|
|
1097
|
+
// static _applyEdgePropertiesFromTags(edge, tags) {
|
|
1098
|
+
// const { highway, oneway, conveying, name } = tags;
|
|
1099
|
+
// edge.name = name || null;
|
|
1100
|
+
// edge.isStairs = highway === 'steps';
|
|
1101
|
+
// edge.isConveying = 'conveying' in tags;
|
|
1102
|
+
// edge.isOneway = Boolean((oneway === 'yes' || oneway === 'true' || oneway === '1')
|
|
1103
|
+
// || (conveying && highway && ['yes', 'forward', 'backward'].includes(conveying)));
|
|
1104
|
+
|
|
1105
|
+
// if (conveying === 'backward') {
|
|
1106
|
+
// const tmpNode = edge.node1;
|
|
1107
|
+
// edge.node1 = edge.node2;
|
|
1108
|
+
// edge.node2 = tmpNode;
|
|
1109
|
+
// }
|
|
1110
|
+
|
|
1111
|
+
// }
|
|
1112
|
+
|
|
1113
|
+
var OsmNetworkUtils = /*#__PURE__*/Object.freeze({
|
|
1114
|
+
__proto__: null,
|
|
1115
|
+
HIGHWAYS_PEDESTRIANS: HIGHWAYS_PEDESTRIANS,
|
|
1116
|
+
DEFAULT_WAY_SELECTOR: DEFAULT_WAY_SELECTOR,
|
|
1117
|
+
getNodeByName: getNodeByName,
|
|
1118
|
+
getEdgeByName: getEdgeByName,
|
|
1119
|
+
createNetworkFromOsmModel: createNetworkFromOsmModel
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
class RouterResponse {
|
|
1123
|
+
|
|
1124
|
+
/** @type {!string} */
|
|
1125
|
+
routerName;
|
|
1126
|
+
|
|
1127
|
+
/** @type {!Coordinates} */
|
|
1128
|
+
from;
|
|
1129
|
+
|
|
1130
|
+
/** @type {!Coordinates} */
|
|
1131
|
+
to;
|
|
1132
|
+
|
|
1133
|
+
/** @type {!(Itinerary[])} */
|
|
1134
|
+
itineraries = [];
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* @param {Itinerary} itinerary
|
|
1139
|
+
*/
|
|
1140
|
+
function generateStepsMetadata(itinerary) {
|
|
1141
|
+
|
|
1142
|
+
let counter = 1;
|
|
1143
|
+
|
|
1144
|
+
itinerary.legs.forEach((leg, legId) => {
|
|
1145
|
+
leg.steps.forEach((step, stepId) => {
|
|
1146
|
+
|
|
1147
|
+
if (counter === 1) {
|
|
1148
|
+
step.firstStep = true;
|
|
1149
|
+
}
|
|
1150
|
+
if (legId === itinerary.legs.length - 1
|
|
1151
|
+
&& stepId === leg.steps.length - 1) {
|
|
1152
|
+
step.lastStep = true;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
step.number = counter++;
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
/*
|
|
1159
|
+
* Generate previousBearing, nextBearing and angle
|
|
1160
|
+
*/
|
|
1161
|
+
|
|
1162
|
+
let coordsBeforeStep;
|
|
1163
|
+
if (step._idCoordsInLeg > 0) {
|
|
1164
|
+
coordsBeforeStep = leg.coords[step._idCoordsInLeg - 1];
|
|
1165
|
+
} else if (legId === 0) {
|
|
1166
|
+
coordsBeforeStep = itinerary.from;
|
|
1167
|
+
} else {
|
|
1168
|
+
coordsBeforeStep = itinerary.legs[legId - 1].to.coords;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
let coordsAfterStep;
|
|
1172
|
+
if (step._idCoordsInLeg !== leg.coords.length - 1) {
|
|
1173
|
+
coordsAfterStep = leg.coords[step._idCoordsInLeg + 1];
|
|
1174
|
+
} else if (legId === itinerary.legs.length - 1) {
|
|
1175
|
+
coordsAfterStep = itinerary.to;
|
|
1176
|
+
} else {
|
|
1177
|
+
coordsAfterStep = itinerary.legs[legId + 1].from.coords;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
step.previousBearing = coordsBeforeStep.bearingTo(step.coords);
|
|
1181
|
+
step.nextBearing = step.coords.bearingTo(coordsAfterStep);
|
|
1182
|
+
step.angle = diffAngle(step.previousBearing, step.nextBearing + Math.PI);
|
|
1183
|
+
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/* eslint-disable max-statements */
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* @param {Coordinates} coordinates
|
|
1192
|
+
* @returns {object}
|
|
1193
|
+
*/
|
|
1194
|
+
function coordinatesToJson(coordinates) {
|
|
1195
|
+
const output = [coordinates.lng, coordinates.lat];
|
|
1196
|
+
if (coordinates.level) {
|
|
1197
|
+
output.push(coordinates.level.toString());
|
|
1198
|
+
}
|
|
1199
|
+
return output;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* @param {object} json
|
|
1204
|
+
* @returns {Coordinates}
|
|
1205
|
+
*/
|
|
1206
|
+
function jsonToCoordinates$1(json) {
|
|
1207
|
+
const output = new Coordinates(json[1], json[0]);
|
|
1208
|
+
if (json.length > 2) {
|
|
1209
|
+
output.level = Level.fromString(json[2]);
|
|
1210
|
+
}
|
|
1211
|
+
return output;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function nodesToJsonCoords(nodes) {
|
|
1215
|
+
return nodes.map(node => coordinatesToJson(node.coords));
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
function getModifierFromAngle(_angle) {
|
|
1220
|
+
|
|
1221
|
+
const angle = positiveMod(rad2deg(_angle), 360);
|
|
1222
|
+
|
|
1223
|
+
if (angle > 0 && angle < 60) {
|
|
1224
|
+
return 'sharp right';
|
|
1225
|
+
}
|
|
1226
|
+
if (angle >= 60 && angle < 140) {
|
|
1227
|
+
return 'right';
|
|
1228
|
+
}
|
|
1229
|
+
if (angle >= 140 && angle < 160) {
|
|
1230
|
+
return 'slight right';
|
|
1231
|
+
}
|
|
1232
|
+
if (angle >= 160 && angle <= 200) {
|
|
1233
|
+
return 'straight';
|
|
1234
|
+
}
|
|
1235
|
+
if (angle > 200 && angle <= 220) {
|
|
1236
|
+
return 'slight left';
|
|
1237
|
+
}
|
|
1238
|
+
if (angle > 220 && angle <= 300) {
|
|
1239
|
+
return 'left';
|
|
1240
|
+
}
|
|
1241
|
+
if (angle > 300 && angle < 360) {
|
|
1242
|
+
return 'sharp left';
|
|
1243
|
+
}
|
|
1244
|
+
return 'u turn';
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
function noRouteFoundJson(message) {
|
|
1249
|
+
return {
|
|
1250
|
+
'code': 'NoRoute',
|
|
1251
|
+
message
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* @param {Itinerary} itinerary
|
|
1257
|
+
* @returns {object}
|
|
1258
|
+
*/
|
|
1259
|
+
function itineraryToOsrmJson(itinerary) {
|
|
1260
|
+
|
|
1261
|
+
const lastLegId = itinerary.legs.length - 1;
|
|
1262
|
+
|
|
1263
|
+
const jsonLegs = itinerary.legs.map(({ distance, duration, coords, steps }, idLeg) => {
|
|
1264
|
+
|
|
1265
|
+
const lastStepId = steps.length - 1;
|
|
1266
|
+
|
|
1267
|
+
return {
|
|
1268
|
+
distance,
|
|
1269
|
+
duration,
|
|
1270
|
+
steps: steps.map((step, idStep, arr) => {
|
|
1271
|
+
|
|
1272
|
+
let type = idStep === 0 && idLeg === 0 ? 'depart' : 'turn';
|
|
1273
|
+
type = idStep === lastStepId && idLeg === lastLegId ? 'arrive' : type;
|
|
1274
|
+
|
|
1275
|
+
const stepCoordsIdx = coords.findIndex(p => p.equalsTo(step.coords));
|
|
1276
|
+
const nextStepCoordsIdx = idStep === lastStepId
|
|
1277
|
+
? stepCoordsIdx
|
|
1278
|
+
: coords.findIndex(p => p.equalsTo(arr[idStep + 1].coords));
|
|
1279
|
+
|
|
1280
|
+
const jsonStep = {
|
|
1281
|
+
geometry: {
|
|
1282
|
+
type: 'LineString',
|
|
1283
|
+
coordinates: coords.slice(stepCoordsIdx, nextStepCoordsIdx + 1).map(coordinatesToJson)
|
|
1284
|
+
},
|
|
1285
|
+
distance: step.distance,
|
|
1286
|
+
duration: step.duration,
|
|
1287
|
+
name: step.name,
|
|
1288
|
+
maneuver: {
|
|
1289
|
+
bearing_before: rad2deg(step.previousBearing),
|
|
1290
|
+
bearing_after: rad2deg(step.nextBearing),
|
|
1291
|
+
location: coordinatesToJson(step.coords),
|
|
1292
|
+
modifier: getModifierFromAngle(step.angle),
|
|
1293
|
+
type
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
if (step.levelChange !== null) {
|
|
1297
|
+
jsonStep.levelChange = step.levelChange;
|
|
1298
|
+
}
|
|
1299
|
+
if (typeof step.extras === 'object' && Object.keys(step.extras).length !== 0) {
|
|
1300
|
+
jsonStep.extras = step.extras;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return jsonStep;
|
|
1304
|
+
})
|
|
1305
|
+
};
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
return {
|
|
1309
|
+
'code': 'Ok',
|
|
1310
|
+
'routes': [
|
|
1311
|
+
{
|
|
1312
|
+
'geometry': {
|
|
1313
|
+
'type': 'LineString',
|
|
1314
|
+
'coordinates': itinerary.coords.map(coordinatesToJson)
|
|
1315
|
+
},
|
|
1316
|
+
'legs': jsonLegs,
|
|
1317
|
+
'distance': itinerary.distance,
|
|
1318
|
+
'duration': itinerary.duration,
|
|
1319
|
+
'weight_name': 'routability',
|
|
1320
|
+
'weight': 0
|
|
1321
|
+
}
|
|
1322
|
+
],
|
|
1323
|
+
'waypoints': []
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* @param {object} jsonSteps
|
|
1330
|
+
* @param {Coordinates[]} legCoords
|
|
1331
|
+
* @returns {Step[]}
|
|
1332
|
+
*/
|
|
1333
|
+
function parseJsonSteps$1(jsonSteps, legCoords) {
|
|
1334
|
+
|
|
1335
|
+
if (!jsonSteps) {
|
|
1336
|
+
return [];
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
return jsonSteps.map(jsonStep => {
|
|
1340
|
+
|
|
1341
|
+
const step = new Step();
|
|
1342
|
+
step.coords = jsonToCoordinates$1(jsonStep.maneuver.location);
|
|
1343
|
+
|
|
1344
|
+
const idStepCoordsInLeg = legCoords.findIndex(coords => step.coords.equalsTo(coords));
|
|
1345
|
+
if (idStepCoordsInLeg < 0) {
|
|
1346
|
+
throw new Error('Osrm Parser: Cannot find step coords in leg coordinates');
|
|
1347
|
+
}
|
|
1348
|
+
step._idCoordsInLeg = idStepCoordsInLeg;
|
|
1349
|
+
|
|
1350
|
+
step.name = jsonStep.name;
|
|
1351
|
+
step.levelChange = jsonStep.levelChange ? jsonStep.levelChange : null;
|
|
1352
|
+
|
|
1353
|
+
step.distance = jsonStep.distance;
|
|
1354
|
+
step.duration = jsonStep.duration;
|
|
1355
|
+
|
|
1356
|
+
if (step.extras.subwayEntrance) {
|
|
1357
|
+
step.extras.subwayEntrance = true;
|
|
1358
|
+
if (jsonStep.extras.subwayEntranceRef) {
|
|
1359
|
+
step.extras.subwayEntranceRef = jsonStep.extras.subwayEntranceRef;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
return step;
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Generate multi itineraries from OSRM JSON
|
|
1370
|
+
* @param {object} json JSON file provided by OSRM.
|
|
1371
|
+
* @param {Coordinates} from itinerary start
|
|
1372
|
+
* @param {Coordinates} to itinerary end
|
|
1373
|
+
* @param {?string} routingMode [walking|driving|bicycle]
|
|
1374
|
+
* @returns {?RouterResponse}
|
|
1375
|
+
*/
|
|
1376
|
+
function createRouterResponseFromJson$1(json, from, to, routingMode = 'walking') {
|
|
1377
|
+
|
|
1378
|
+
const { routes: jsonRoutes } = json;
|
|
1379
|
+
|
|
1380
|
+
if (!jsonRoutes) {
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const routingModeCorrespondance = new Map();
|
|
1385
|
+
routingModeCorrespondance.set('walking', 'WALK');
|
|
1386
|
+
routingModeCorrespondance.set('driving', 'CAR');
|
|
1387
|
+
routingModeCorrespondance.set('bicycle', 'BIKE');
|
|
1388
|
+
const mode = routingModeCorrespondance.get(routingMode) || null;
|
|
1389
|
+
|
|
1390
|
+
const routerResponse = new RouterResponse();
|
|
1391
|
+
routerResponse.routerName = 'osrm';
|
|
1392
|
+
|
|
1393
|
+
routerResponse.from = from;
|
|
1394
|
+
routerResponse.to = to;
|
|
1395
|
+
|
|
1396
|
+
for (const jsonItinerary of jsonRoutes) {
|
|
1397
|
+
|
|
1398
|
+
const itinerary = new Itinerary();
|
|
1399
|
+
|
|
1400
|
+
// itinerary.coords = jsonItinerary.geometry.coordinates.map(jsonToCoordinates);
|
|
1401
|
+
itinerary.distance = jsonItinerary.distance;
|
|
1402
|
+
itinerary.duration = jsonItinerary.duration;
|
|
1403
|
+
itinerary.from = from;
|
|
1404
|
+
itinerary.to = to;
|
|
1405
|
+
|
|
1406
|
+
routerResponse.itineraries.push(itinerary);
|
|
1407
|
+
|
|
1408
|
+
for (const jsonLeg of jsonItinerary.legs) {
|
|
1409
|
+
|
|
1410
|
+
const leg = new Leg();
|
|
1411
|
+
|
|
1412
|
+
leg.mode = mode;
|
|
1413
|
+
leg.distance = jsonLeg.distance;
|
|
1414
|
+
leg.duration = jsonLeg.duration;
|
|
1415
|
+
|
|
1416
|
+
leg.coords = jsonLeg.steps
|
|
1417
|
+
.map(step => step.geometry.coordinates.map(jsonToCoordinates$1))
|
|
1418
|
+
.flat()
|
|
1419
|
+
// Remove duplicates
|
|
1420
|
+
.filter((coords, idx, arr) => idx === 0 || !arr[idx - 1].equalsTo(coords));
|
|
1421
|
+
|
|
1422
|
+
leg.from = {
|
|
1423
|
+
name: null,
|
|
1424
|
+
coords: leg.coords[0]
|
|
1425
|
+
};
|
|
1426
|
+
leg.to = {
|
|
1427
|
+
name: null,
|
|
1428
|
+
coords: leg.coords[leg.coords.length - 1]
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
leg.steps = parseJsonSteps$1(jsonLeg.steps, leg.coords);
|
|
1432
|
+
|
|
1433
|
+
itinerary.legs.push(leg);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// All legs have to be parsed before computing steps metadata
|
|
1437
|
+
generateStepsMetadata(itinerary);
|
|
1438
|
+
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
return routerResponse;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
var OsrmUtils = /*#__PURE__*/Object.freeze({
|
|
1445
|
+
__proto__: null,
|
|
1446
|
+
coordinatesToJson: coordinatesToJson,
|
|
1447
|
+
jsonToCoordinates: jsonToCoordinates$1,
|
|
1448
|
+
nodesToJsonCoords: nodesToJsonCoords,
|
|
1449
|
+
getModifierFromAngle: getModifierFromAngle,
|
|
1450
|
+
noRouteFoundJson: noRouteFoundJson,
|
|
1451
|
+
itineraryToOsrmJson: itineraryToOsrmJson,
|
|
1452
|
+
createRouterResponseFromJson: createRouterResponseFromJson$1
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
/* eslint-disable max-statements */
|
|
1456
|
+
|
|
1457
|
+
/**
|
|
1458
|
+
* @param {object} json
|
|
1459
|
+
* @returns {Coordinates}
|
|
1460
|
+
*/
|
|
1461
|
+
function jsonToCoordinates(json) {
|
|
1462
|
+
return new Coordinates(json.lat, json.lon);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* @param {object} jsonSteps
|
|
1467
|
+
* @param {Coordinates[]} legCoords
|
|
1468
|
+
* @returns {Step[]}
|
|
1469
|
+
*/
|
|
1470
|
+
function parseJsonSteps(jsonSteps, legCoords) {
|
|
1471
|
+
|
|
1472
|
+
if (!jsonSteps) {
|
|
1473
|
+
return [];
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
return jsonSteps.map(jsonStep => {
|
|
1477
|
+
|
|
1478
|
+
const step = new Step();
|
|
1479
|
+
const stepCoords = jsonToCoordinates(jsonStep);
|
|
1480
|
+
|
|
1481
|
+
// OTP step does not have the same coordinates than a point in legCoords.
|
|
1482
|
+
// That is why we look for the closest point.
|
|
1483
|
+
const distances = legCoords.map(coords => coords.distanceTo(stepCoords));
|
|
1484
|
+
const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
|
|
1485
|
+
if (idStepCoordsInLeg < 0) {
|
|
1486
|
+
throw new Error('OTP Parser: Cannot find closest step');
|
|
1487
|
+
}
|
|
1488
|
+
step.coords = legCoords[idStepCoordsInLeg];
|
|
1489
|
+
step._idCoordsInLeg = idStepCoordsInLeg;
|
|
1490
|
+
|
|
1491
|
+
step.name = jsonStep.streetName;
|
|
1492
|
+
step.levelChange = null;
|
|
1493
|
+
|
|
1494
|
+
step.distance = jsonStep.distance;
|
|
1495
|
+
|
|
1496
|
+
return step;
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/**
|
|
1501
|
+
* Generate multi itineraries from OTP JSON
|
|
1502
|
+
* @param {object} json JSON file provided by OTP.
|
|
1503
|
+
* @returns {?RouterResponse}
|
|
1504
|
+
*/
|
|
1505
|
+
function createRouterResponseFromJson(json) {
|
|
1506
|
+
|
|
1507
|
+
const { plan: jsonPlan } = json;
|
|
1508
|
+
|
|
1509
|
+
if (!jsonPlan) {
|
|
1510
|
+
return null;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
const routerResponse = new RouterResponse();
|
|
1514
|
+
routerResponse.routerName = 'otp';
|
|
1515
|
+
|
|
1516
|
+
routerResponse.from = jsonToCoordinates(jsonPlan.from);
|
|
1517
|
+
routerResponse.to = jsonToCoordinates(jsonPlan.to);
|
|
1518
|
+
|
|
1519
|
+
for (const jsonItinerary of jsonPlan.itineraries) {
|
|
1520
|
+
|
|
1521
|
+
const itinerary = new Itinerary();
|
|
1522
|
+
|
|
1523
|
+
itinerary.duration = jsonItinerary.duration;
|
|
1524
|
+
itinerary.startTime = jsonItinerary.startTime;
|
|
1525
|
+
itinerary.endTime = jsonItinerary.endTime;
|
|
1526
|
+
itinerary.from = routerResponse.from;
|
|
1527
|
+
itinerary.to = routerResponse.to;
|
|
1528
|
+
|
|
1529
|
+
routerResponse.itineraries.push(itinerary);
|
|
1530
|
+
|
|
1531
|
+
for (const jsonLeg of jsonItinerary.legs) {
|
|
1532
|
+
|
|
1533
|
+
const leg = new Leg();
|
|
1534
|
+
|
|
1535
|
+
leg.mode = jsonLeg.mode;
|
|
1536
|
+
leg.duration = jsonLeg.duration;
|
|
1537
|
+
leg.startTime = jsonLeg.startTime;
|
|
1538
|
+
leg.endTime = jsonLeg.endTime;
|
|
1539
|
+
leg.from = {
|
|
1540
|
+
name: jsonLeg.from.name,
|
|
1541
|
+
coords: jsonToCoordinates(jsonLeg.from)
|
|
1542
|
+
};
|
|
1543
|
+
leg.to = {
|
|
1544
|
+
name: jsonLeg.to.name,
|
|
1545
|
+
coords: jsonToCoordinates(jsonLeg.to)
|
|
1546
|
+
};
|
|
1547
|
+
leg.coords = Polyline.decode(jsonLeg.legGeometry.points).map(([lat, lon]) => new Coordinates(lat, lon));
|
|
1548
|
+
|
|
1549
|
+
leg.steps = parseJsonSteps(jsonLeg.steps, leg.coords);
|
|
1550
|
+
|
|
1551
|
+
if (leg.mode === 'BUS' || leg.mode === 'TRAM') {
|
|
1552
|
+
leg.transportInfo = {
|
|
1553
|
+
name: jsonLeg.route,
|
|
1554
|
+
routeColor: jsonLeg.routeColor,
|
|
1555
|
+
routeTextColor: jsonLeg.routeTextColor,
|
|
1556
|
+
directionName: jsonLeg.headsign
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
const legStep = new Step();
|
|
1560
|
+
legStep.coords = leg.coords[0];
|
|
1561
|
+
legStep._idCoordsInLeg = 0;
|
|
1562
|
+
legStep.name = jsonLeg.headsign;
|
|
1563
|
+
legStep.levelChange = null;
|
|
1564
|
+
legStep.distance = jsonLeg.distance;
|
|
1565
|
+
leg.steps = [legStep];
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// jsonLeg.distance is not reliable when compared to the array of leg coords.
|
|
1569
|
+
// leg.distance = jsonLeg.distance;
|
|
1570
|
+
leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
|
|
1571
|
+
if (idx === 0) {
|
|
1572
|
+
return acc;
|
|
1573
|
+
}
|
|
1574
|
+
return acc + arr[idx - 1].distanceTo(coords);
|
|
1575
|
+
}, 0);
|
|
1576
|
+
|
|
1577
|
+
itinerary.legs.push(leg);
|
|
1578
|
+
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
|
|
1582
|
+
if (idx === 0) {
|
|
1583
|
+
return acc;
|
|
1584
|
+
}
|
|
1585
|
+
return acc + arr[idx - 1].distanceTo(coords);
|
|
1586
|
+
}, 0);
|
|
1587
|
+
|
|
1588
|
+
// All legs have to be parsed before computing steps metadata
|
|
1589
|
+
generateStepsMetadata(itinerary);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
return routerResponse;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
var OtpUtils = /*#__PURE__*/Object.freeze({
|
|
1596
|
+
__proto__: null,
|
|
1597
|
+
jsonToCoordinates: jsonToCoordinates,
|
|
1598
|
+
createRouterResponseFromJson: createRouterResponseFromJson
|
|
1599
|
+
});
|
|
1600
|
+
|
|
1601
|
+
class ItineraryInfo {
|
|
1602
|
+
|
|
1603
|
+
/** @type {Step} */
|
|
1604
|
+
nextStep;
|
|
1605
|
+
|
|
1606
|
+
/** @type {Step} */
|
|
1607
|
+
previousStep;
|
|
1608
|
+
|
|
1609
|
+
/** @type {GraphProjection} */
|
|
1610
|
+
projection;
|
|
1611
|
+
|
|
1612
|
+
/** @type {Leg} */
|
|
1613
|
+
leg;
|
|
1614
|
+
|
|
1615
|
+
/** @type {number} */
|
|
1616
|
+
traveledDistance;
|
|
1617
|
+
|
|
1618
|
+
/** @type {number} */
|
|
1619
|
+
traveledPercentage;
|
|
1620
|
+
|
|
1621
|
+
/** @type {number} */
|
|
1622
|
+
remainingDistance;
|
|
1623
|
+
|
|
1624
|
+
/** @type {number} */
|
|
1625
|
+
remainingPercentage;
|
|
1626
|
+
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/* eslint-disable max-statements */
|
|
1630
|
+
|
|
1631
|
+
class ItineraryInfoManager {
|
|
1632
|
+
|
|
1633
|
+
/** @type {Itinerary} */
|
|
1634
|
+
_itinerary;
|
|
1635
|
+
|
|
1636
|
+
/** @type {Network} */
|
|
1637
|
+
_network;
|
|
1638
|
+
|
|
1639
|
+
/** @type {Network} */
|
|
1640
|
+
_mapMatching;
|
|
1641
|
+
|
|
1642
|
+
/** @type {Step[]} */
|
|
1643
|
+
_steps;
|
|
1644
|
+
|
|
1645
|
+
/** @type {Step[]} */
|
|
1646
|
+
_coordsNextStep;
|
|
1647
|
+
|
|
1648
|
+
/** @type {Step[]} */
|
|
1649
|
+
_coordsPreviousStep;
|
|
1650
|
+
|
|
1651
|
+
/** @type {number[]} */
|
|
1652
|
+
_coordsDistanceTraveled;
|
|
1653
|
+
|
|
1654
|
+
/** @type {Leg[]} */
|
|
1655
|
+
_coordsLeg;
|
|
1656
|
+
|
|
1657
|
+
/** @type {Itinerary} */
|
|
1658
|
+
set itinerary(itinerary) {
|
|
1659
|
+
|
|
1660
|
+
this._itinerary = itinerary;
|
|
1661
|
+
this._steps = itinerary.steps;
|
|
1662
|
+
this._network = itinerary.toNetwork();
|
|
1663
|
+
this._mapMatching = new MapMatching(this._network);
|
|
1664
|
+
|
|
1665
|
+
this._coordsNextStep = new Array(itinerary.coords.length);
|
|
1666
|
+
this._coordsPreviousStep = new Array(itinerary.coords.length);
|
|
1667
|
+
this._coordsDistanceTraveled = new Array(itinerary.coords.length);
|
|
1668
|
+
this._coordsLeg = new Array(itinerary.coords.length);
|
|
1669
|
+
|
|
1670
|
+
let stepId = 0;
|
|
1671
|
+
let previousStep = null;
|
|
1672
|
+
let nextStep = this._steps[0];
|
|
1673
|
+
let distanceTraveled = 0;
|
|
1674
|
+
|
|
1675
|
+
itinerary.coords.forEach((coords, idx, arr) => {
|
|
1676
|
+
if (stepId < this._steps.length && this._steps[stepId].coords.equalsTo(coords)) {
|
|
1677
|
+
previousStep = this._steps[stepId];
|
|
1678
|
+
nextStep = stepId === this._steps.length - 1 ? null : this._steps[stepId + 1];
|
|
1679
|
+
stepId++;
|
|
1680
|
+
}
|
|
1681
|
+
if (idx !== 0) {
|
|
1682
|
+
distanceTraveled += arr[idx - 1].distanceTo(coords);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
this._coordsNextStep[idx] = nextStep;
|
|
1686
|
+
this._coordsPreviousStep[idx] = previousStep;
|
|
1687
|
+
this._coordsDistanceTraveled[idx] = distanceTraveled;
|
|
1688
|
+
this._coordsLeg[idx] = itinerary.legs.find(leg => leg.coords.includes(coords));
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
/**
|
|
1693
|
+
* @param {Coordinates} position
|
|
1694
|
+
* @returns {ItineraryInfo}
|
|
1695
|
+
*/
|
|
1696
|
+
getInfo(position) {
|
|
1697
|
+
|
|
1698
|
+
if (!(position instanceof Coordinates)) {
|
|
1699
|
+
return null;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
const projection = this._mapMatching.getProjection(position);
|
|
1703
|
+
if (!projection) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
let itineraryInfo = null;
|
|
1708
|
+
|
|
1709
|
+
if (projection.nearestElement instanceof GraphNode) {
|
|
1710
|
+
const idx = this._itinerary.coords.findIndex(
|
|
1711
|
+
coords => projection.nearestElement.coords === coords
|
|
1712
|
+
);
|
|
1713
|
+
if (idx === -1) {
|
|
1714
|
+
throw new Error('ItineraryInfoManager: could not find projection in itinerary (Node)');
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
itineraryInfo = new ItineraryInfo();
|
|
1718
|
+
itineraryInfo.nextStep = this._coordsNextStep[idx];
|
|
1719
|
+
itineraryInfo.previousStep = this._coordsPreviousStep[idx];
|
|
1720
|
+
itineraryInfo.projection = projection;
|
|
1721
|
+
itineraryInfo.leg = this._coordsLeg[idx];
|
|
1722
|
+
itineraryInfo.traveledDistance = this._coordsDistanceTraveled[idx];
|
|
1723
|
+
itineraryInfo.remainingDistance = this._itinerary.distance - itineraryInfo.traveledDistance;
|
|
1724
|
+
itineraryInfo.traveledPercentage = itineraryInfo.traveledDistance / this._itinerary.distance;
|
|
1725
|
+
itineraryInfo.remainingPercentage = itineraryInfo.remainingDistance / this._itinerary.distance;
|
|
1726
|
+
|
|
1727
|
+
} else if (projection.nearestElement instanceof GraphEdge) {
|
|
1728
|
+
|
|
1729
|
+
let firstNode = projection.nearestElement.node1.coords;
|
|
1730
|
+
let idx = this._itinerary.coords.findIndex(coords => firstNode === coords);
|
|
1731
|
+
if (idx === -1) {
|
|
1732
|
+
throw new Error('ItineraryInfoManager: could not find projection in itinerary (Edge)');
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// graphEdge is not necessarly ordered. We have to look for the first point
|
|
1736
|
+
if (idx === this._itinerary.coords.length - 1
|
|
1737
|
+
|| this._itinerary.coords[idx + 1] !== projection.nearestElement.node2.coords
|
|
1738
|
+
) {
|
|
1739
|
+
firstNode = projection.nearestElement.node2.coords;
|
|
1740
|
+
idx--;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
itineraryInfo = new ItineraryInfo();
|
|
1744
|
+
itineraryInfo.nextStep = this._coordsNextStep[idx];
|
|
1745
|
+
itineraryInfo.previousStep = this._coordsPreviousStep[idx];
|
|
1746
|
+
itineraryInfo.projection = projection;
|
|
1747
|
+
itineraryInfo.leg = this._coordsLeg[idx];
|
|
1748
|
+
itineraryInfo.traveledDistance = this._coordsDistanceTraveled[idx]
|
|
1749
|
+
+ projection.projection.distanceTo(firstNode);
|
|
1750
|
+
itineraryInfo.remainingDistance = this._itinerary.distance - itineraryInfo.traveledDistance;
|
|
1751
|
+
itineraryInfo.traveledPercentage = itineraryInfo.traveledDistance / this._itinerary.distance;
|
|
1752
|
+
itineraryInfo.remainingPercentage = itineraryInfo.remainingDistance / this._itinerary.distance;
|
|
1753
|
+
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
return itineraryInfo;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
export { Itinerary, ItineraryInfo, ItineraryInfoManager, Leg, LevelChange, OsmElement, OsmModel, OsmNetworkUtils, OsmNode, OsmParser, OsmRouter, OsmRouterOptions, OsmRouterUtils, OsmWay, OsrmUtils, OtpUtils, RouterResponse, Step };
|
|
1762
|
+
//# sourceMappingURL=wemap-osm.es.js.map
|