@wemap/geo 11.8.2 → 12.0.0-alpha.11
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/index.js +75 -837
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -838
- package/dist/index.mjs.map +1 -1
- package/index.ts +0 -11
- package/package.json +4 -4
- package/src/Utils.ts +32 -0
- package/src/coordinates/Coordinates.spec.ts +3 -3
- package/src/coordinates/Coordinates.ts +2 -13
- package/src/coordinates/Level.spec.ts +2 -2
- package/src/coordinates/Level.ts +50 -26
- package/src/types.ts +2 -16
- package/src/graph/GeoGraph.spec.ts +0 -278
- package/src/graph/GeoGraph.ts +0 -179
- package/src/graph/GeoGraphEdge.spec.ts +0 -51
- package/src/graph/GeoGraphEdge.ts +0 -98
- package/src/graph/GeoGraphItinerary.spec.ts +0 -14
- package/src/graph/GeoGraphItinerary.ts +0 -61
- package/src/graph/GeoGraphProjection.ts +0 -24
- package/src/graph/GeoGraphProjectionHandler.spec.ts +0 -340
- package/src/graph/GeoGraphProjectionHandler.ts +0 -220
- package/src/graph/GeoGraphRouter.spec.ts +0 -322
- package/src/graph/GeoGraphRouter.ts +0 -498
- package/src/graph/GeoGraphVertex.spec.ts +0 -54
- package/src/graph/GeoGraphVertex.ts +0 -139
- package/src/graph/NoRouteFoundError.ts +0 -38
- package/tests/CommonTest.ts +0 -96
package/dist/index.js
CHANGED
|
@@ -7,8 +7,6 @@ var __publicField = (obj, key, value) => {
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
9
|
const maths = require("@wemap/maths");
|
|
10
|
-
const utils = require("@wemap/utils");
|
|
11
|
-
const Logger = require("@wemap/logger");
|
|
12
10
|
const R_MAJOR = 6378137;
|
|
13
11
|
const R_MINOR = 63567523142e-4;
|
|
14
12
|
const EARTH_GRAVITY = 9.80665;
|
|
@@ -62,13 +60,13 @@ const _Level = class _Level {
|
|
|
62
60
|
*/
|
|
63
61
|
static isRange(level) {
|
|
64
62
|
if (_Level.VERIFY_TYPING) {
|
|
65
|
-
|
|
63
|
+
_Level.checkType(level);
|
|
66
64
|
}
|
|
67
65
|
return Array.isArray(level);
|
|
68
66
|
}
|
|
69
67
|
static clone(level) {
|
|
70
68
|
if (_Level.VERIFY_TYPING) {
|
|
71
|
-
|
|
69
|
+
_Level.checkType(level);
|
|
72
70
|
}
|
|
73
71
|
if (level === null) {
|
|
74
72
|
return null;
|
|
@@ -96,14 +94,14 @@ const _Level = class _Level {
|
|
|
96
94
|
const levels = splited.map((str2) => Number(str2));
|
|
97
95
|
const low = Math.min(...levels);
|
|
98
96
|
const up = Math.max(...levels);
|
|
99
|
-
|
|
97
|
+
_Level.checkType([low, up]);
|
|
100
98
|
return [low, up];
|
|
101
99
|
} else {
|
|
102
100
|
const rangeSeparator = str.substring(1).indexOf("-") + 1;
|
|
103
101
|
if (rangeSeparator > 0) {
|
|
104
102
|
const low = Number(str.substring(0, rangeSeparator));
|
|
105
103
|
const up = Number(str.substring(rangeSeparator + 1));
|
|
106
|
-
|
|
104
|
+
_Level.checkType([low, up]);
|
|
107
105
|
return [low, up];
|
|
108
106
|
}
|
|
109
107
|
}
|
|
@@ -116,8 +114,8 @@ const _Level = class _Level {
|
|
|
116
114
|
*/
|
|
117
115
|
static contains(container, targeted) {
|
|
118
116
|
if (_Level.VERIFY_TYPING) {
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
_Level.checkType(container);
|
|
118
|
+
_Level.checkType(targeted);
|
|
121
119
|
}
|
|
122
120
|
if (container === targeted) {
|
|
123
121
|
return true;
|
|
@@ -138,29 +136,40 @@ const _Level = class _Level {
|
|
|
138
136
|
}
|
|
139
137
|
/**
|
|
140
138
|
* Retrieve the intersection of two levels
|
|
139
|
+
* null n null => null
|
|
140
|
+
* null n 1 => null // Conception choice
|
|
141
|
+
* null n [1,2] => null // Conception choice
|
|
142
|
+
* 1 n 1 => 1
|
|
143
|
+
* 1 n 2 => null
|
|
144
|
+
* 1 n [1,2] => 1
|
|
145
|
+
* 1 n [0,2] => 1
|
|
146
|
+
* 1 n [2,3] => null
|
|
147
|
+
* [1,2] n [1,2] => [1,2]
|
|
148
|
+
* [1,2] n [2,3] => 2
|
|
149
|
+
* [1,2] n [3,4] => null
|
|
141
150
|
*/
|
|
142
151
|
static intersection(first, second) {
|
|
143
152
|
if (_Level.VERIFY_TYPING) {
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
_Level.checkType(first);
|
|
154
|
+
_Level.checkType(second);
|
|
146
155
|
}
|
|
147
156
|
if (first === null || second === null) {
|
|
148
157
|
return null;
|
|
149
158
|
}
|
|
150
|
-
if (
|
|
151
|
-
return
|
|
159
|
+
if (_Level.equals(first, second)) {
|
|
160
|
+
return _Level.clone(first);
|
|
152
161
|
}
|
|
153
162
|
if (typeof first === "number" && typeof second === "number") {
|
|
154
163
|
return first === second ? first : null;
|
|
155
164
|
}
|
|
156
165
|
if (Array.isArray(first) && !Array.isArray(second)) {
|
|
157
|
-
if (
|
|
166
|
+
if (_Level.contains(first, second)) {
|
|
158
167
|
return second;
|
|
159
168
|
}
|
|
160
169
|
return null;
|
|
161
170
|
}
|
|
162
171
|
if (!Array.isArray(first) && Array.isArray(second)) {
|
|
163
|
-
if (
|
|
172
|
+
if (_Level.contains(second, first)) {
|
|
164
173
|
return first;
|
|
165
174
|
}
|
|
166
175
|
return null;
|
|
@@ -180,30 +189,41 @@ const _Level = class _Level {
|
|
|
180
189
|
*/
|
|
181
190
|
static intersect(first, second) {
|
|
182
191
|
if (_Level.VERIFY_TYPING) {
|
|
183
|
-
|
|
184
|
-
|
|
192
|
+
_Level.checkType(first);
|
|
193
|
+
_Level.checkType(second);
|
|
185
194
|
}
|
|
186
195
|
if (first === null && second === null) {
|
|
187
196
|
return true;
|
|
188
197
|
}
|
|
189
|
-
return
|
|
198
|
+
return _Level.intersection(first, second) !== null;
|
|
190
199
|
}
|
|
191
200
|
/**
|
|
192
201
|
* Retrieve the union of two levels
|
|
202
|
+
* null u null => null
|
|
203
|
+
* null u 1 => null // Conception choice
|
|
204
|
+
* null u [1,2] => null // Conception choice
|
|
205
|
+
* 1 u 1 => 1
|
|
206
|
+
* 1 u 2 => [1,2]
|
|
207
|
+
* 1 u [1,2] => [1,2]
|
|
208
|
+
* 1 u [0,2] => [0,2]
|
|
209
|
+
* 1 u [2,3] => [1,3]
|
|
210
|
+
* [1,2] u [1,2] => [1,2]
|
|
211
|
+
* [1,2] u [2,3] => [1,3]
|
|
212
|
+
* [1,2] u [3,4] => [1,4]
|
|
193
213
|
*/
|
|
194
214
|
static union(first, second) {
|
|
195
215
|
if (_Level.VERIFY_TYPING) {
|
|
196
|
-
|
|
197
|
-
|
|
216
|
+
_Level.checkType(first);
|
|
217
|
+
_Level.checkType(second);
|
|
198
218
|
}
|
|
199
219
|
if (first === second) {
|
|
200
|
-
return
|
|
220
|
+
return _Level.clone(first);
|
|
201
221
|
}
|
|
202
222
|
if (second === null) {
|
|
203
|
-
return
|
|
223
|
+
return null;
|
|
204
224
|
}
|
|
205
225
|
if (first === null) {
|
|
206
|
-
return
|
|
226
|
+
return null;
|
|
207
227
|
}
|
|
208
228
|
let low, up;
|
|
209
229
|
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
@@ -232,7 +252,7 @@ const _Level = class _Level {
|
|
|
232
252
|
*/
|
|
233
253
|
static multiplyBy(level, factor) {
|
|
234
254
|
if (_Level.VERIFY_TYPING) {
|
|
235
|
-
|
|
255
|
+
_Level.checkType(level);
|
|
236
256
|
}
|
|
237
257
|
if (level === null) {
|
|
238
258
|
return null;
|
|
@@ -241,7 +261,7 @@ const _Level = class _Level {
|
|
|
241
261
|
}
|
|
242
262
|
static toString(level) {
|
|
243
263
|
if (_Level.VERIFY_TYPING) {
|
|
244
|
-
|
|
264
|
+
_Level.checkType(level);
|
|
245
265
|
}
|
|
246
266
|
if (level === null) {
|
|
247
267
|
return null;
|
|
@@ -250,8 +270,8 @@ const _Level = class _Level {
|
|
|
250
270
|
}
|
|
251
271
|
static equals(first, second) {
|
|
252
272
|
if (_Level.VERIFY_TYPING) {
|
|
253
|
-
|
|
254
|
-
|
|
273
|
+
_Level.checkType(first);
|
|
274
|
+
_Level.checkType(second);
|
|
255
275
|
}
|
|
256
276
|
if (first === second) {
|
|
257
277
|
return true;
|
|
@@ -263,8 +283,8 @@ const _Level = class _Level {
|
|
|
263
283
|
}
|
|
264
284
|
static diff(first, second) {
|
|
265
285
|
if (_Level.VERIFY_TYPING) {
|
|
266
|
-
|
|
267
|
-
|
|
286
|
+
_Level.checkType(first);
|
|
287
|
+
_Level.checkType(second);
|
|
268
288
|
}
|
|
269
289
|
if (first === null || second === null) {
|
|
270
290
|
return null;
|
|
@@ -530,7 +550,7 @@ class Coordinates {
|
|
|
530
550
|
poseCoordinates.lat,
|
|
531
551
|
poseCoordinates.lng,
|
|
532
552
|
alt,
|
|
533
|
-
Level.
|
|
553
|
+
Level.union(p1.level, p2.level)
|
|
534
554
|
);
|
|
535
555
|
if (Math.abs(p1.distanceTo(p2) - p1.distanceTo(projection) - p2.distanceTo(projection)) > EPS_MM) {
|
|
536
556
|
return null;
|
|
@@ -567,26 +587,15 @@ class Coordinates {
|
|
|
567
587
|
return [
|
|
568
588
|
Number(this.lat.toFixed(8)),
|
|
569
589
|
Number(this.lng.toFixed(8)),
|
|
570
|
-
this.alt === null ? null : Number(this.alt.toFixed(3)),
|
|
571
590
|
this.level
|
|
572
591
|
];
|
|
573
592
|
}
|
|
574
|
-
if (this.alt !== null) {
|
|
575
|
-
return [
|
|
576
|
-
Number(this.lat.toFixed(8)),
|
|
577
|
-
Number(this.lng.toFixed(8)),
|
|
578
|
-
Number(this.alt.toFixed(3))
|
|
579
|
-
];
|
|
580
|
-
}
|
|
581
593
|
return [Number(this.lat.toFixed(8)), Number(this.lng.toFixed(8))];
|
|
582
594
|
}
|
|
583
595
|
static fromCompressedJson(json) {
|
|
584
596
|
const coords = new Coordinates(json[0], json[1]);
|
|
585
597
|
if (json.length > 2) {
|
|
586
|
-
coords.
|
|
587
|
-
}
|
|
588
|
-
if (json.length > 3) {
|
|
589
|
-
coords.level = json[3];
|
|
598
|
+
coords.level = json[2];
|
|
590
599
|
}
|
|
591
600
|
return coords;
|
|
592
601
|
}
|
|
@@ -802,9 +811,34 @@ function geolocationPositionToUserPosition(geolocationPosition) {
|
|
|
802
811
|
function calcDistance(coords) {
|
|
803
812
|
return coords.reduce((acc, coords2, idx, arr) => acc + (idx ? arr[idx - 1].distanceTo(coords2) : 0), 0);
|
|
804
813
|
}
|
|
814
|
+
function createSegmentsAtLevel(itineraryCoords, segmentsLevel, useMultiLevelSegments = true) {
|
|
815
|
+
const itineraryCoordsLength = itineraryCoords.length;
|
|
816
|
+
let previousLevelCorrespond = false;
|
|
817
|
+
const segments = [];
|
|
818
|
+
let coordinates = [];
|
|
819
|
+
for (let i = 0; i < itineraryCoordsLength; i++) {
|
|
820
|
+
const coords = itineraryCoords[i];
|
|
821
|
+
const levelCorrespond = Level.intersect(segmentsLevel, coords.level);
|
|
822
|
+
if (useMultiLevelSegments && !levelCorrespond && previousLevelCorrespond) {
|
|
823
|
+
coordinates.push(coords);
|
|
824
|
+
} else if (levelCorrespond) {
|
|
825
|
+
if (!previousLevelCorrespond) {
|
|
826
|
+
coordinates = [];
|
|
827
|
+
segments.push(coordinates);
|
|
828
|
+
if (useMultiLevelSegments && i !== 0) {
|
|
829
|
+
coordinates.push(itineraryCoords[i - 1]);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
coordinates.push(coords);
|
|
833
|
+
}
|
|
834
|
+
previousLevelCorrespond = levelCorrespond;
|
|
835
|
+
}
|
|
836
|
+
return segments;
|
|
837
|
+
}
|
|
805
838
|
const Utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
806
839
|
__proto__: null,
|
|
807
840
|
calcDistance,
|
|
841
|
+
createSegmentsAtLevel,
|
|
808
842
|
geolocationPositionToUserPosition,
|
|
809
843
|
sampleRoute,
|
|
810
844
|
simplifyRoute,
|
|
@@ -1244,810 +1278,14 @@ class AbsoluteHeading {
|
|
|
1244
1278
|
return new AbsoluteHeading(this.heading, this.time, this.accuracy);
|
|
1245
1279
|
}
|
|
1246
1280
|
}
|
|
1247
|
-
class GeoGraphEdge extends utils.GraphEdge {
|
|
1248
|
-
constructor(vertex1, vertex2, params) {
|
|
1249
|
-
super(vertex1, vertex2, params);
|
|
1250
|
-
__publicField(this, "_level", null);
|
|
1251
|
-
__publicField(this, "_bearing", null);
|
|
1252
|
-
__publicField(this, "_length", null);
|
|
1253
|
-
__publicField(this, "_computedSizeAndBearing", false);
|
|
1254
|
-
__publicField(this, "isOneway", false);
|
|
1255
|
-
Object.assign(this, params);
|
|
1256
|
-
}
|
|
1257
|
-
set vertex1(vertex) {
|
|
1258
|
-
super.vertex1 = vertex;
|
|
1259
|
-
this._computedSizeAndBearing = false;
|
|
1260
|
-
}
|
|
1261
|
-
get vertex1() {
|
|
1262
|
-
return this._vertex1;
|
|
1263
|
-
}
|
|
1264
|
-
set vertex2(vertex) {
|
|
1265
|
-
super.vertex2 = vertex;
|
|
1266
|
-
this._computedSizeAndBearing = false;
|
|
1267
|
-
}
|
|
1268
|
-
get vertex2() {
|
|
1269
|
-
return this._vertex2;
|
|
1270
|
-
}
|
|
1271
|
-
get level() {
|
|
1272
|
-
return this._level;
|
|
1273
|
-
}
|
|
1274
|
-
set level(level) {
|
|
1275
|
-
Level.checkType(level);
|
|
1276
|
-
this._level = level;
|
|
1277
|
-
}
|
|
1278
|
-
/**
|
|
1279
|
-
* Get edge bearing from vertex1 to vertex2
|
|
1280
|
-
*/
|
|
1281
|
-
get bearing() {
|
|
1282
|
-
if (!this._computedSizeAndBearing) {
|
|
1283
|
-
this._computeSizeAndBearing();
|
|
1284
|
-
}
|
|
1285
|
-
return this._bearing;
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* get edge length
|
|
1289
|
-
*/
|
|
1290
|
-
get length() {
|
|
1291
|
-
if (!this._computedSizeAndBearing) {
|
|
1292
|
-
this._computeSizeAndBearing();
|
|
1293
|
-
}
|
|
1294
|
-
return this._length;
|
|
1295
|
-
}
|
|
1296
|
-
_computeSizeAndBearing() {
|
|
1297
|
-
this._length = this.vertex1.distanceTo(this.vertex2);
|
|
1298
|
-
this._bearing = this.vertex1.bearingTo(this.vertex2);
|
|
1299
|
-
this._computedSizeAndBearing = true;
|
|
1300
|
-
}
|
|
1301
|
-
static getEdgeByVertices(edges, vertex1, vertex2) {
|
|
1302
|
-
return super.getEdgeByVertices(edges, vertex1, vertex2);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
class GeoGraphVertex extends utils.GraphVertex {
|
|
1306
|
-
constructor(coords, params) {
|
|
1307
|
-
super(params);
|
|
1308
|
-
__publicField(this, "coords");
|
|
1309
|
-
__publicField(this, "io", false);
|
|
1310
|
-
this.coords = coords;
|
|
1311
|
-
}
|
|
1312
|
-
distanceTo(other) {
|
|
1313
|
-
return this.coords.distanceTo(other.coords);
|
|
1314
|
-
}
|
|
1315
|
-
bearingTo(other) {
|
|
1316
|
-
return this.coords.bearingTo(other.coords);
|
|
1317
|
-
}
|
|
1318
|
-
/**
|
|
1319
|
-
* Does not include "edges" property
|
|
1320
|
-
*/
|
|
1321
|
-
toJson() {
|
|
1322
|
-
return this.coords.toCompressedJson();
|
|
1323
|
-
}
|
|
1324
|
-
static fromJson(json) {
|
|
1325
|
-
return new GeoGraphVertex(Coordinates.fromCompressedJson(json));
|
|
1326
|
-
}
|
|
1327
|
-
inferVertexLevelFromEdges() {
|
|
1328
|
-
let tmpLevel = null;
|
|
1329
|
-
for (let i = 0; i < this.edges.length; i++) {
|
|
1330
|
-
const edge = this.edges[i];
|
|
1331
|
-
if (edge.level !== null) {
|
|
1332
|
-
if (tmpLevel === null) {
|
|
1333
|
-
tmpLevel = Level.clone(edge.level);
|
|
1334
|
-
} else {
|
|
1335
|
-
tmpLevel = Level.intersection(tmpLevel, edge.level);
|
|
1336
|
-
if (tmpLevel === null) {
|
|
1337
|
-
throw Error("Something bad happend during parsing: We cannot retrieve vertex level from adjacent ways: " + this.coords);
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
this.coords.level = tmpLevel;
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* We suppose inferVertexLevelFromEdges() was called before
|
|
1346
|
-
*/
|
|
1347
|
-
inferVertexLevelByNeighboors() {
|
|
1348
|
-
const { level } = this.coords;
|
|
1349
|
-
if (level === null || !Level.isRange(level)) {
|
|
1350
|
-
return true;
|
|
1351
|
-
}
|
|
1352
|
-
let tmpLevel = null;
|
|
1353
|
-
for (let i = 0; i < this.edges.length; i++) {
|
|
1354
|
-
const edge = this.edges[i];
|
|
1355
|
-
const otherVertex = edge.vertex1 === this ? edge.vertex2 : edge.vertex1;
|
|
1356
|
-
tmpLevel = Level.union(otherVertex.coords.level, tmpLevel);
|
|
1357
|
-
}
|
|
1358
|
-
if (tmpLevel === null || !Level.isRange(tmpLevel)) {
|
|
1359
|
-
this.coords.level = tmpLevel === level[0] ? level[1] : level[0];
|
|
1360
|
-
}
|
|
1361
|
-
return true;
|
|
1362
|
-
}
|
|
1363
|
-
/**
|
|
1364
|
-
* We suppose inferVertexLevelFromEdges() and inferVertexLevelByNeighboors() were called before
|
|
1365
|
-
*/
|
|
1366
|
-
inferVertexLevelByRecursion() {
|
|
1367
|
-
const { level } = this.coords;
|
|
1368
|
-
if (level === null || !Level.isRange(level)) {
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
if (this.edges.length > 1) {
|
|
1372
|
-
return;
|
|
1373
|
-
}
|
|
1374
|
-
const lookForLevel = (vertex, visitedVertices) => {
|
|
1375
|
-
visitedVertices.push(vertex);
|
|
1376
|
-
if (vertex.coords.level === null) {
|
|
1377
|
-
return null;
|
|
1378
|
-
}
|
|
1379
|
-
if (!Level.isRange(vertex.coords.level)) {
|
|
1380
|
-
return vertex.coords.level;
|
|
1381
|
-
}
|
|
1382
|
-
let tmpLevel = null;
|
|
1383
|
-
for (let i = 0; i < vertex.edges.length; i++) {
|
|
1384
|
-
const edge = vertex.edges[i];
|
|
1385
|
-
const otherVertex = edge.vertex1 === vertex ? edge.vertex2 : edge.vertex1;
|
|
1386
|
-
if (!visitedVertices.includes(otherVertex)) {
|
|
1387
|
-
tmpLevel = Level.union(lookForLevel(otherVertex, visitedVertices), tmpLevel);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
return tmpLevel;
|
|
1391
|
-
};
|
|
1392
|
-
const othersLevels = lookForLevel(this, []);
|
|
1393
|
-
if (othersLevels !== null) {
|
|
1394
|
-
if (!Level.isRange(othersLevels)) {
|
|
1395
|
-
this.coords.level = othersLevels === level[0] ? level[1] : level[0];
|
|
1396
|
-
return;
|
|
1397
|
-
}
|
|
1398
|
-
throw Error("Level of: " + this.coords.toString() + " cannot be decided");
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
class GeoGraph extends utils.Graph {
|
|
1403
|
-
constructor(vertices, edges, generateVerticesLevels = false) {
|
|
1404
|
-
super(vertices, edges);
|
|
1405
|
-
generateVerticesLevels && this.generateVerticesLevels();
|
|
1406
|
-
}
|
|
1407
|
-
getVertexByCoords(coords) {
|
|
1408
|
-
return GeoGraph.getVertexByCoords(this.vertices, coords);
|
|
1409
|
-
}
|
|
1410
|
-
static getVertexByCoords(vertices, coords) {
|
|
1411
|
-
return vertices.find((vertex) => vertex.coords.equals(coords));
|
|
1412
|
-
}
|
|
1413
|
-
getVertexByName(name) {
|
|
1414
|
-
return super.getVertexByName(name);
|
|
1415
|
-
}
|
|
1416
|
-
getEdgeByName(name) {
|
|
1417
|
-
return super.getEdgeByName(name);
|
|
1418
|
-
}
|
|
1419
|
-
getBoundingBox(extendedMeasure) {
|
|
1420
|
-
if (!this.vertices.length) {
|
|
1421
|
-
return null;
|
|
1422
|
-
}
|
|
1423
|
-
const boundingBox = BoundingBox.fromCoordinates(this.vertices.map((vertex) => vertex.coords));
|
|
1424
|
-
if (extendedMeasure) {
|
|
1425
|
-
boundingBox.extendsWithMeasure(extendedMeasure);
|
|
1426
|
-
}
|
|
1427
|
-
return boundingBox;
|
|
1428
|
-
}
|
|
1429
|
-
toCompressedJson() {
|
|
1430
|
-
const createEdgeExtras = (edge) => {
|
|
1431
|
-
const extras = {};
|
|
1432
|
-
if (edge.isOneway) {
|
|
1433
|
-
extras.oneway = true;
|
|
1434
|
-
}
|
|
1435
|
-
return extras;
|
|
1436
|
-
};
|
|
1437
|
-
return {
|
|
1438
|
-
vertices: this.vertices.map((vertex) => vertex.toJson()),
|
|
1439
|
-
edges: this.edges.map((edge) => {
|
|
1440
|
-
const vertex1Idx = this.vertices.indexOf(edge.vertex1);
|
|
1441
|
-
const vertex2Idx = this.vertices.indexOf(edge.vertex2);
|
|
1442
|
-
const edgeExtras = createEdgeExtras(edge);
|
|
1443
|
-
if (Object.keys(edgeExtras).length > 0) {
|
|
1444
|
-
return [vertex1Idx, vertex2Idx, edge.level, edgeExtras];
|
|
1445
|
-
}
|
|
1446
|
-
if (edge.level !== null) {
|
|
1447
|
-
return [vertex1Idx, vertex2Idx, edge.level];
|
|
1448
|
-
}
|
|
1449
|
-
return [vertex1Idx, vertex2Idx];
|
|
1450
|
-
})
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
static fromCompressedJson(json) {
|
|
1454
|
-
const geograph = new GeoGraph();
|
|
1455
|
-
geograph.vertices = json.vertices.map((vertex) => GeoGraphVertex.fromJson(vertex));
|
|
1456
|
-
geograph.edges = json.edges.map((jsonEdge) => {
|
|
1457
|
-
const edge = new GeoGraphEdge(
|
|
1458
|
-
geograph.vertices[jsonEdge[0]],
|
|
1459
|
-
geograph.vertices[jsonEdge[1]],
|
|
1460
|
-
{ level: jsonEdge.length > 2 ? jsonEdge[2] : null }
|
|
1461
|
-
);
|
|
1462
|
-
const extras = jsonEdge.length > 3 ? jsonEdge[3] : {};
|
|
1463
|
-
if (typeof extras.oneway !== "undefined") {
|
|
1464
|
-
edge.isOneway = extras.oneway;
|
|
1465
|
-
}
|
|
1466
|
-
return edge;
|
|
1467
|
-
});
|
|
1468
|
-
return geograph;
|
|
1469
|
-
}
|
|
1470
|
-
static fromCoordinates(segments) {
|
|
1471
|
-
const geograph = new GeoGraph();
|
|
1472
|
-
const getOrCreateVertex = (coords) => {
|
|
1473
|
-
const vertex = geograph.vertices.find((otherVertex) => otherVertex.coords.equals(coords));
|
|
1474
|
-
if (vertex) {
|
|
1475
|
-
return vertex;
|
|
1476
|
-
}
|
|
1477
|
-
const newVertex = new GeoGraphVertex(coords);
|
|
1478
|
-
geograph.vertices.push(newVertex);
|
|
1479
|
-
return newVertex;
|
|
1480
|
-
};
|
|
1481
|
-
const createEdgeFromVertices = (vertex1, vertex2) => new GeoGraphEdge(
|
|
1482
|
-
vertex1,
|
|
1483
|
-
vertex2,
|
|
1484
|
-
{ level: Level.union(vertex1.coords.level, vertex2.coords.level) }
|
|
1485
|
-
);
|
|
1486
|
-
for (const segment of segments) {
|
|
1487
|
-
let previousVertex = null;
|
|
1488
|
-
for (const coords of segment) {
|
|
1489
|
-
const currentVertex = getOrCreateVertex(coords);
|
|
1490
|
-
if (previousVertex) {
|
|
1491
|
-
const edge = createEdgeFromVertices(currentVertex, previousVertex);
|
|
1492
|
-
geograph.edges.push(edge);
|
|
1493
|
-
}
|
|
1494
|
-
previousVertex = currentVertex;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
return geograph;
|
|
1498
|
-
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Create edges From MultiLevel Itinerary for a given level
|
|
1501
|
-
* @param useMultiLevelEdges use segments which intersect both levels (stairs, elevators...)
|
|
1502
|
-
*/
|
|
1503
|
-
getEdgesAtLevel(targetLevel, useMultiLevelEdges = true) {
|
|
1504
|
-
return this.edges.filter(
|
|
1505
|
-
({ level }) => useMultiLevelEdges ? Level.intersect(targetLevel, level) : Level.contains(targetLevel, level)
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
generateVerticesLevels() {
|
|
1509
|
-
const { vertices } = this;
|
|
1510
|
-
vertices.forEach((vertex) => vertex.inferVertexLevelFromEdges());
|
|
1511
|
-
vertices.forEach((vertex) => vertex.inferVertexLevelByNeighboors());
|
|
1512
|
-
vertices.forEach((vertex) => vertex.inferVertexLevelByRecursion());
|
|
1513
|
-
vertices.forEach((vertex) => {
|
|
1514
|
-
vertex.io = vertex.coords.level !== null && vertex.edges.some((edge) => edge.level === null);
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
class GeoGraphProjection {
|
|
1519
|
-
constructor(origin, distanceFromNearestElement, coords, nearestElement) {
|
|
1520
|
-
__publicField(this, "origin");
|
|
1521
|
-
__publicField(this, "distanceFromNearestElement");
|
|
1522
|
-
__publicField(this, "coords");
|
|
1523
|
-
__publicField(this, "nearestElement");
|
|
1524
|
-
this.origin = origin;
|
|
1525
|
-
this.distanceFromNearestElement = distanceFromNearestElement;
|
|
1526
|
-
this.coords = coords;
|
|
1527
|
-
this.nearestElement = nearestElement;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
const _GeoGraphProjectionHandler = class _GeoGraphProjectionHandler {
|
|
1531
|
-
constructor(graph = null) {
|
|
1532
|
-
__publicField(this, "graph", null);
|
|
1533
|
-
__publicField(this, "_maxDistance", Number.MAX_VALUE);
|
|
1534
|
-
__publicField(this, "_maxAngleBearing", Math.PI);
|
|
1535
|
-
this.graph = graph;
|
|
1536
|
-
}
|
|
1537
|
-
set maxAngleBearing(maxAngleBearing) {
|
|
1538
|
-
this._maxAngleBearing = maxAngleBearing;
|
|
1539
|
-
}
|
|
1540
|
-
get maxAngleBearing() {
|
|
1541
|
-
return this._maxAngleBearing;
|
|
1542
|
-
}
|
|
1543
|
-
set maxDistance(maxDistance) {
|
|
1544
|
-
this._maxDistance = maxDistance;
|
|
1545
|
-
}
|
|
1546
|
-
get maxDistance() {
|
|
1547
|
-
return this._maxDistance;
|
|
1548
|
-
}
|
|
1549
|
-
/**
|
|
1550
|
-
* Check if the specified edge and its vertices can be used for projection
|
|
1551
|
-
* @returns an array of two elements.
|
|
1552
|
-
* First is true if projection will be used on the specified edge, false otherwise.
|
|
1553
|
-
* Second is true if projection will be used on the vertices of the specified edge, false otherwise.
|
|
1554
|
-
*/
|
|
1555
|
-
_shouldProjectOnEdgeAndVertices(edge, location, useBearing, useMultiLevelSegments, acceptEdgeFn) {
|
|
1556
|
-
if (!acceptEdgeFn(edge)) {
|
|
1557
|
-
return [false, false, false];
|
|
1558
|
-
}
|
|
1559
|
-
let checkEdge = Level.intersect(location.level, edge.level);
|
|
1560
|
-
let checkVertex1 = Level.intersect(location.level, edge.vertex1.coords.level);
|
|
1561
|
-
let checkVertex2 = Level.intersect(location.level, edge.vertex2.coords.level);
|
|
1562
|
-
checkVertex1 = checkVertex1 || edge.vertex1.io && location.level === null;
|
|
1563
|
-
checkVertex2 = checkVertex2 || edge.vertex2.io && location.level === null;
|
|
1564
|
-
if (!useMultiLevelSegments) {
|
|
1565
|
-
checkEdge = checkEdge && !Level.isRange(edge.level);
|
|
1566
|
-
checkVertex1 = checkVertex1 && !Level.isRange(edge.vertex1.coords.level);
|
|
1567
|
-
checkVertex2 = checkVertex2 && !Level.isRange(edge.vertex2.coords.level);
|
|
1568
|
-
}
|
|
1569
|
-
if (useBearing) {
|
|
1570
|
-
if (checkEdge) {
|
|
1571
|
-
const diffAngle = maths.diffAngleLines(edge.bearing, location.bearing);
|
|
1572
|
-
if (diffAngle > this._maxAngleBearing) {
|
|
1573
|
-
checkEdge = false;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
checkVertex1 = false;
|
|
1577
|
-
checkVertex2 = false;
|
|
1578
|
-
}
|
|
1579
|
-
return [checkEdge, checkVertex1, checkVertex2];
|
|
1580
|
-
}
|
|
1581
|
-
static _assignLatLngLevel(fromCoordinates, toCoordinates) {
|
|
1582
|
-
toCoordinates.lat = fromCoordinates.lat;
|
|
1583
|
-
toCoordinates.lng = fromCoordinates.lng;
|
|
1584
|
-
toCoordinates.level = Level.clone(fromCoordinates.level);
|
|
1585
|
-
}
|
|
1586
|
-
/**
|
|
1587
|
-
* IO Vertices are typical because they have a non-null level but projection car works on them.
|
|
1588
|
-
* This function handles the case where the projection is on an IO vertex and a location with
|
|
1589
|
-
* a null level is required.
|
|
1590
|
-
*/
|
|
1591
|
-
static _handleLevelsWithIOVertices(projection, location, projectionVertex) {
|
|
1592
|
-
if (location.level === null && projectionVertex.io) {
|
|
1593
|
-
projection.level = null;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
/**
|
|
1597
|
-
* Main function for map-matching, the networks have to be set before calling this function
|
|
1598
|
-
* The function will returns a GraphProjection object given a coordinates object and a set
|
|
1599
|
-
* of options (useDistance, useBearing, useMultiLevelSegments, acceptEdgeFn).
|
|
1600
|
-
*/
|
|
1601
|
-
getProjection(location, useDistance = false, useBearing = false, useMultiLevelSegments = true, acceptEdgeFn = () => true) {
|
|
1602
|
-
if (this.graph === null) {
|
|
1603
|
-
throw new Error("Graph has not been set yet");
|
|
1604
|
-
}
|
|
1605
|
-
if (useBearing && (!("bearing" in location && location.bearing !== null) || !this._maxAngleBearing)) {
|
|
1606
|
-
return null;
|
|
1607
|
-
}
|
|
1608
|
-
let distanceFromNearestElement = Number.MAX_VALUE;
|
|
1609
|
-
const projection = location.clone();
|
|
1610
|
-
let nearestElement = null;
|
|
1611
|
-
const isProjectionBetter = (distanceOfNewProjection) => {
|
|
1612
|
-
return distanceOfNewProjection < distanceFromNearestElement && (!useDistance || distanceOfNewProjection <= this._maxDistance);
|
|
1613
|
-
};
|
|
1614
|
-
for (const edge of this.graph.edges) {
|
|
1615
|
-
const [checkEdge, checkVertex1, checkVertex2] = this._shouldProjectOnEdgeAndVertices(
|
|
1616
|
-
edge,
|
|
1617
|
-
location,
|
|
1618
|
-
useBearing,
|
|
1619
|
-
useMultiLevelSegments,
|
|
1620
|
-
acceptEdgeFn
|
|
1621
|
-
);
|
|
1622
|
-
if (checkVertex1) {
|
|
1623
|
-
const distVertex1 = location.distanceTo(edge.vertex1.coords);
|
|
1624
|
-
if (isProjectionBetter(distVertex1) || distVertex1 <= EPS_MM) {
|
|
1625
|
-
distanceFromNearestElement = distVertex1;
|
|
1626
|
-
nearestElement = edge.vertex1;
|
|
1627
|
-
_GeoGraphProjectionHandler._assignLatLngLevel(edge.vertex1.coords, projection);
|
|
1628
|
-
_GeoGraphProjectionHandler._handleLevelsWithIOVertices(projection, location, edge.vertex1);
|
|
1629
|
-
if (distVertex1 <= EPS_MM) {
|
|
1630
|
-
break;
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
if (checkVertex2) {
|
|
1635
|
-
const distVertex2 = location.distanceTo(edge.vertex2.coords);
|
|
1636
|
-
if (isProjectionBetter(distVertex2) || distVertex2 <= EPS_MM) {
|
|
1637
|
-
distanceFromNearestElement = distVertex2;
|
|
1638
|
-
nearestElement = edge.vertex2;
|
|
1639
|
-
_GeoGraphProjectionHandler._assignLatLngLevel(edge.vertex2.coords, projection);
|
|
1640
|
-
_GeoGraphProjectionHandler._handleLevelsWithIOVertices(projection, location, edge.vertex2);
|
|
1641
|
-
if (distVertex2 <= EPS_MM) {
|
|
1642
|
-
break;
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
if (checkEdge) {
|
|
1647
|
-
const segmentProjection = location.getSegmentProjection(edge.vertex1.coords, edge.vertex2.coords);
|
|
1648
|
-
if (segmentProjection) {
|
|
1649
|
-
const distEdge = location.distanceTo(segmentProjection);
|
|
1650
|
-
if (isProjectionBetter(distEdge)) {
|
|
1651
|
-
distanceFromNearestElement = distEdge;
|
|
1652
|
-
nearestElement = edge;
|
|
1653
|
-
_GeoGraphProjectionHandler._assignLatLngLevel(segmentProjection, projection);
|
|
1654
|
-
_GeoGraphProjectionHandler._updateProjectionLevelFromEdge(edge, projection);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
if (!nearestElement) {
|
|
1660
|
-
return null;
|
|
1661
|
-
}
|
|
1662
|
-
if (projection instanceof UserPosition && projection.accuracy !== null) {
|
|
1663
|
-
projection.accuracy += distanceFromNearestElement;
|
|
1664
|
-
}
|
|
1665
|
-
return new GeoGraphProjection(
|
|
1666
|
-
location,
|
|
1667
|
-
distanceFromNearestElement,
|
|
1668
|
-
projection,
|
|
1669
|
-
nearestElement
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
|
-
};
|
|
1673
|
-
__publicField(_GeoGraphProjectionHandler, "_updateProjectionLevelFromEdge", (_edge, _projection) => {
|
|
1674
|
-
_projection.level = Level.clone(_edge.level);
|
|
1675
|
-
});
|
|
1676
|
-
let GeoGraphProjectionHandler = _GeoGraphProjectionHandler;
|
|
1677
|
-
class GeoGraphItinerary extends GeoGraph {
|
|
1678
|
-
constructor(start, end, vertices, edges, edgesWeights) {
|
|
1679
|
-
super(vertices, edges);
|
|
1680
|
-
this.start = start;
|
|
1681
|
-
this.end = end;
|
|
1682
|
-
this.edgesWeights = edgesWeights;
|
|
1683
|
-
}
|
|
1684
|
-
static fromGraphVertices(start, end, networkVertices, edgesWeights) {
|
|
1685
|
-
const vertices = networkVertices.map((vertex) => {
|
|
1686
|
-
return new GeoGraphVertex(
|
|
1687
|
-
vertex.coords.clone(),
|
|
1688
|
-
{ name: vertex.name, id: vertex.id, data: vertex.data, io: vertex.io }
|
|
1689
|
-
);
|
|
1690
|
-
});
|
|
1691
|
-
const edges = [];
|
|
1692
|
-
networkVertices.forEach((vertex, idx, arr) => {
|
|
1693
|
-
if (idx === 0) {
|
|
1694
|
-
return;
|
|
1695
|
-
}
|
|
1696
|
-
const prevVertex = arr[idx - 1];
|
|
1697
|
-
const edge = GeoGraphEdge.getEdgeByVertices(prevVertex.edges, prevVertex, vertex);
|
|
1698
|
-
if (!edge) {
|
|
1699
|
-
Logger.error("Cannot retrieve edge to create itinerary");
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
|
-
edges.push(new GeoGraphEdge(
|
|
1703
|
-
vertices[idx - 1],
|
|
1704
|
-
vertices[idx],
|
|
1705
|
-
{ name: edge.name, id: edge.id, data: edge.data, level: edge.level, isOneway: edge.isOneway }
|
|
1706
|
-
));
|
|
1707
|
-
});
|
|
1708
|
-
return new GeoGraphItinerary(start, end, vertices, edges, edgesWeights);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
class NoRouteFoundError extends Error {
|
|
1712
|
-
constructor(start, end, details = null) {
|
|
1713
|
-
super();
|
|
1714
|
-
this.start = start;
|
|
1715
|
-
this.end = end;
|
|
1716
|
-
this.details = details;
|
|
1717
|
-
}
|
|
1718
|
-
get startStr() {
|
|
1719
|
-
if (this.start instanceof GeoGraphVertex) {
|
|
1720
|
-
return `GeoGraphVertex ${this.start.coords.toString()}`;
|
|
1721
|
-
}
|
|
1722
|
-
return this.start.toString();
|
|
1723
|
-
}
|
|
1724
|
-
get endStr() {
|
|
1725
|
-
if (this.end instanceof GeoGraphVertex) {
|
|
1726
|
-
return `GeoGraphVertex ${this.end.coords.toString()}`;
|
|
1727
|
-
}
|
|
1728
|
-
if (Array.isArray(this.end)) {
|
|
1729
|
-
return this.end.map((p) => p instanceof GeoGraphVertex ? p.coords.toString() : p.toString()).join(",");
|
|
1730
|
-
}
|
|
1731
|
-
return this.end.toString();
|
|
1732
|
-
}
|
|
1733
|
-
get message() {
|
|
1734
|
-
let message = `No route found from ${this.startStr} to ${this.endStr}.`;
|
|
1735
|
-
if (this.details) {
|
|
1736
|
-
message += ` Details: ${this.details}`;
|
|
1737
|
-
}
|
|
1738
|
-
return message;
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
const _GeoGraphRouter = class _GeoGraphRouter {
|
|
1742
|
-
constructor(graph) {
|
|
1743
|
-
__publicField(this, "_mapMatching");
|
|
1744
|
-
__publicField(this, "_graph");
|
|
1745
|
-
__publicField(this, "disabledEdges", /* @__PURE__ */ new Set());
|
|
1746
|
-
this._graph = graph;
|
|
1747
|
-
this._mapMatching = new GeoGraphProjectionHandler(graph);
|
|
1748
|
-
}
|
|
1749
|
-
getShortestPath(start, end, options = _GeoGraphRouter.DEFAULT_OPTIONS) {
|
|
1750
|
-
const { acceptEdgeFn, weightEdgeFn, projectionMaxDistance } = options;
|
|
1751
|
-
this._mapMatching.maxDistance = projectionMaxDistance;
|
|
1752
|
-
const createdVertices = [];
|
|
1753
|
-
const retrieveOrCreateNearestVertex = (point) => {
|
|
1754
|
-
if (point instanceof GeoGraphVertex) {
|
|
1755
|
-
return point;
|
|
1756
|
-
}
|
|
1757
|
-
const closeVertex = this._graph.getVertexByCoords(point);
|
|
1758
|
-
if (closeVertex) {
|
|
1759
|
-
return closeVertex;
|
|
1760
|
-
}
|
|
1761
|
-
const proj = this._mapMatching.getProjection(point, true, false, false, options.acceptEdgeFn);
|
|
1762
|
-
if (!proj) {
|
|
1763
|
-
let message = `Point ${point.toString()} is too far from the graph > ${this._mapMatching.maxDistance.toFixed(0)} meters.`;
|
|
1764
|
-
if (point.level !== null) {
|
|
1765
|
-
message += ` If it is a multi-level map, please verify if you have a graph at level ${Level.toString(point.level)}.`;
|
|
1766
|
-
}
|
|
1767
|
-
throw new NoRouteFoundError(start, end, message);
|
|
1768
|
-
}
|
|
1769
|
-
if (proj.nearestElement instanceof GeoGraphVertex) {
|
|
1770
|
-
return proj.nearestElement;
|
|
1771
|
-
}
|
|
1772
|
-
const vertexCreated = this.createVertexInsideEdge(
|
|
1773
|
-
proj.nearestElement,
|
|
1774
|
-
proj.coords
|
|
1775
|
-
);
|
|
1776
|
-
createdVertices.push(vertexCreated);
|
|
1777
|
-
return vertexCreated;
|
|
1778
|
-
};
|
|
1779
|
-
const removeCreatedVertices = () => {
|
|
1780
|
-
while (createdVertices.length) {
|
|
1781
|
-
this.removeVertexFromPreviouslyCreatedEdge(createdVertices.pop());
|
|
1782
|
-
}
|
|
1783
|
-
};
|
|
1784
|
-
const startVertex = retrieveOrCreateNearestVertex(start);
|
|
1785
|
-
const endVertex = retrieveOrCreateNearestVertex(end);
|
|
1786
|
-
const startCoords = start instanceof GeoGraphVertex ? start.coords : start;
|
|
1787
|
-
const endCoords = end instanceof GeoGraphVertex ? end.coords : end;
|
|
1788
|
-
let graphItinerary;
|
|
1789
|
-
if (startVertex === endVertex) {
|
|
1790
|
-
graphItinerary = GeoGraphItinerary.fromGraphVertices(
|
|
1791
|
-
startCoords,
|
|
1792
|
-
endCoords,
|
|
1793
|
-
[startVertex],
|
|
1794
|
-
[]
|
|
1795
|
-
);
|
|
1796
|
-
} else {
|
|
1797
|
-
graphItinerary = this.getShortestPathBetweenGeoGraphVertices(
|
|
1798
|
-
startVertex,
|
|
1799
|
-
endVertex,
|
|
1800
|
-
weightEdgeFn,
|
|
1801
|
-
acceptEdgeFn
|
|
1802
|
-
);
|
|
1803
|
-
}
|
|
1804
|
-
graphItinerary.start = startCoords;
|
|
1805
|
-
graphItinerary.end = endCoords;
|
|
1806
|
-
removeCreatedVertices();
|
|
1807
|
-
if (!graphItinerary.vertices.length) {
|
|
1808
|
-
throw new NoRouteFoundError(start, end);
|
|
1809
|
-
}
|
|
1810
|
-
return graphItinerary;
|
|
1811
|
-
}
|
|
1812
|
-
getShortestPaths(start, ends, options = _GeoGraphRouter.DEFAULT_OPTIONS) {
|
|
1813
|
-
const { acceptEdgeFn, weightEdgeFn, projectionMaxDistance } = options;
|
|
1814
|
-
this._mapMatching.maxDistance = projectionMaxDistance;
|
|
1815
|
-
const createdVertices = [];
|
|
1816
|
-
const retrieveOrCreateNearestVertex = (point, options2 = _GeoGraphRouter.DEFAULT_OPTIONS) => {
|
|
1817
|
-
if (point instanceof GeoGraphVertex) {
|
|
1818
|
-
return point;
|
|
1819
|
-
}
|
|
1820
|
-
const closeVertex = this._graph.getVertexByCoords(point);
|
|
1821
|
-
if (closeVertex) {
|
|
1822
|
-
return closeVertex;
|
|
1823
|
-
}
|
|
1824
|
-
const proj = this._mapMatching.getProjection(point, true, false, false, options2.acceptEdgeFn);
|
|
1825
|
-
if (!proj) {
|
|
1826
|
-
return null;
|
|
1827
|
-
}
|
|
1828
|
-
if (proj.nearestElement instanceof GeoGraphVertex) {
|
|
1829
|
-
return proj.nearestElement;
|
|
1830
|
-
}
|
|
1831
|
-
const vertexCreated = this.createVertexInsideEdge(
|
|
1832
|
-
proj.nearestElement,
|
|
1833
|
-
proj.coords
|
|
1834
|
-
);
|
|
1835
|
-
createdVertices.push(vertexCreated);
|
|
1836
|
-
return vertexCreated;
|
|
1837
|
-
};
|
|
1838
|
-
const removeCreatedVertices = () => {
|
|
1839
|
-
while (createdVertices.length) {
|
|
1840
|
-
this.removeVertexFromPreviouslyCreatedEdge(createdVertices.pop());
|
|
1841
|
-
}
|
|
1842
|
-
};
|
|
1843
|
-
const startVertex = retrieveOrCreateNearestVertex(start);
|
|
1844
|
-
const endsStruct = ends.map((e) => ({
|
|
1845
|
-
input: e,
|
|
1846
|
-
vertex: retrieveOrCreateNearestVertex(e),
|
|
1847
|
-
itinerary: null
|
|
1848
|
-
}));
|
|
1849
|
-
const startCoords = start instanceof GeoGraphVertex ? start.coords : start;
|
|
1850
|
-
if (!startVertex) {
|
|
1851
|
-
let message = `Point ${startCoords.toString()} is too far from the graph > ${this._mapMatching.maxDistance.toFixed(0)} meters.`;
|
|
1852
|
-
if (startCoords.level !== null) {
|
|
1853
|
-
message += ` If it is a multi-level map, please verify if you have a graph at level ${Level.toString(startCoords.level)}.`;
|
|
1854
|
-
}
|
|
1855
|
-
throw new NoRouteFoundError(start, ends, message);
|
|
1856
|
-
}
|
|
1857
|
-
const endsStructFiltered = endsStruct.filter((v) => v.vertex != null);
|
|
1858
|
-
const graphItineraries = this.getShortestPathsBetweenGeoGraphVertices(
|
|
1859
|
-
startVertex,
|
|
1860
|
-
endsStructFiltered.map((v) => v.vertex),
|
|
1861
|
-
weightEdgeFn,
|
|
1862
|
-
acceptEdgeFn
|
|
1863
|
-
);
|
|
1864
|
-
graphItineraries.forEach((itinerary, idx) => {
|
|
1865
|
-
itinerary.start = startCoords;
|
|
1866
|
-
const endStructFiltered = endsStructFiltered[idx];
|
|
1867
|
-
endStructFiltered.itinerary = itinerary;
|
|
1868
|
-
});
|
|
1869
|
-
removeCreatedVertices();
|
|
1870
|
-
endsStructFiltered.forEach((e) => {
|
|
1871
|
-
if (e.itinerary !== null) {
|
|
1872
|
-
e.itinerary.end = e.input instanceof GeoGraphVertex ? e.input.coords : e.input;
|
|
1873
|
-
}
|
|
1874
|
-
});
|
|
1875
|
-
return endsStruct.map((end) => {
|
|
1876
|
-
const endFiltered = endsStructFiltered.find((e) => e.input === end.input);
|
|
1877
|
-
if (endFiltered)
|
|
1878
|
-
return endFiltered.itinerary;
|
|
1879
|
-
const endCoords = end.input instanceof GeoGraphVertex ? end.input.coords : end.input;
|
|
1880
|
-
return GeoGraphItinerary.fromGraphVertices(startCoords, endCoords, [], []);
|
|
1881
|
-
});
|
|
1882
|
-
}
|
|
1883
|
-
createVertexInsideEdge(edge, point) {
|
|
1884
|
-
const a = edge.vertex1;
|
|
1885
|
-
const b = edge.vertex2;
|
|
1886
|
-
const m = new GeoGraphVertex(point, { name: `proj on ${edge.name || null} (tmp)` });
|
|
1887
|
-
const newEdgesParams = { name: `splitted ${edge.name || null} (tmp)`, data: edge.data, isOneway: edge.isOneway, level: edge.level };
|
|
1888
|
-
const u = new GeoGraphEdge(a, m, newEdgesParams);
|
|
1889
|
-
const v = new GeoGraphEdge(m, b, newEdgesParams);
|
|
1890
|
-
a.edges = a.edges.filter((_edge) => _edge !== edge);
|
|
1891
|
-
b.edges = b.edges.filter((_edge) => _edge !== edge);
|
|
1892
|
-
this._graph.vertices.push(m);
|
|
1893
|
-
this._graph.edges.push(u, v);
|
|
1894
|
-
this._graph.edges = this._graph.edges.filter(
|
|
1895
|
-
(_edge) => _edge !== edge
|
|
1896
|
-
);
|
|
1897
|
-
return m;
|
|
1898
|
-
}
|
|
1899
|
-
removeVertexFromPreviouslyCreatedEdge(_vertex) {
|
|
1900
|
-
const isUfirstEdge = _vertex.edges[0].vertex2 === _vertex;
|
|
1901
|
-
const u = _vertex.edges[isUfirstEdge ? 0 : 1];
|
|
1902
|
-
const v = _vertex.edges[isUfirstEdge ? 1 : 0];
|
|
1903
|
-
u.vertex1.edges = u.vertex1.edges.filter((edge) => edge !== u);
|
|
1904
|
-
v.vertex2.edges = v.vertex2.edges.filter((edge) => edge !== v);
|
|
1905
|
-
const oldEdgeName = u.name.substring("splitted ".length, u.name.length - " (tmp)".length);
|
|
1906
|
-
const oldEdgeParams = { name: oldEdgeName, data: u.data, isOneway: u.isOneway, level: u.level };
|
|
1907
|
-
const oldEdge = new GeoGraphEdge(u.vertex1, v.vertex2, oldEdgeParams);
|
|
1908
|
-
this._graph.edges.push(oldEdge);
|
|
1909
|
-
this._graph.vertices = this._graph.vertices.filter((vertex) => vertex !== _vertex);
|
|
1910
|
-
this._graph.edges = this._graph.edges.filter(
|
|
1911
|
-
(edge) => edge !== u && edge !== v
|
|
1912
|
-
);
|
|
1913
|
-
}
|
|
1914
|
-
getShortestPathBetweenGeoGraphVertices(start, end, weightFn, acceptEdgeFn) {
|
|
1915
|
-
const distanceMap = {}, checking = {}, vertexList = {}, vertices = {}, parentVertices = {}, path = [];
|
|
1916
|
-
this._graph.vertices.forEach((vertex) => {
|
|
1917
|
-
vertices[vertex.id] = vertex;
|
|
1918
|
-
distanceMap[vertex.id] = Infinity;
|
|
1919
|
-
checking[vertex.id] = null;
|
|
1920
|
-
vertexList[vertex.id] = true;
|
|
1921
|
-
});
|
|
1922
|
-
distanceMap[start.id] = 0;
|
|
1923
|
-
while (Object.keys(vertexList).length > 0) {
|
|
1924
|
-
const keys = Object.keys(vertexList).map(Number);
|
|
1925
|
-
const current = Number(
|
|
1926
|
-
keys.reduce((_checking, vertexId) => {
|
|
1927
|
-
return distanceMap[_checking] > distanceMap[vertexId] ? vertexId : _checking;
|
|
1928
|
-
}, keys[0])
|
|
1929
|
-
);
|
|
1930
|
-
this._graph.edges.filter((edge) => {
|
|
1931
|
-
if (acceptEdgeFn && !acceptEdgeFn(edge) || this.disabledEdges.has(edge)) {
|
|
1932
|
-
return false;
|
|
1933
|
-
}
|
|
1934
|
-
const from = edge.vertex1.id, to = edge.vertex2.id;
|
|
1935
|
-
return from === current || to === current;
|
|
1936
|
-
}).forEach((edge) => {
|
|
1937
|
-
let to, from, reversed = false;
|
|
1938
|
-
if (edge.vertex1.id === current) {
|
|
1939
|
-
to = edge.vertex2.id;
|
|
1940
|
-
from = edge.vertex1.id;
|
|
1941
|
-
} else {
|
|
1942
|
-
to = edge.vertex1.id;
|
|
1943
|
-
from = edge.vertex2.id;
|
|
1944
|
-
reversed = true;
|
|
1945
|
-
}
|
|
1946
|
-
if (edge.isOneway && reversed) {
|
|
1947
|
-
return;
|
|
1948
|
-
}
|
|
1949
|
-
const distance = distanceMap[current] + weightFn(edge);
|
|
1950
|
-
if (distanceMap[to] > distance) {
|
|
1951
|
-
distanceMap[to] = distance;
|
|
1952
|
-
checking[to] = current;
|
|
1953
|
-
parentVertices[to] = from;
|
|
1954
|
-
}
|
|
1955
|
-
});
|
|
1956
|
-
delete vertexList[current];
|
|
1957
|
-
}
|
|
1958
|
-
const edgesWeights = [];
|
|
1959
|
-
let endId = end.id;
|
|
1960
|
-
while (typeof parentVertices[endId] !== "undefined") {
|
|
1961
|
-
path.unshift(vertices[endId]);
|
|
1962
|
-
edgesWeights.unshift(distanceMap[endId] - distanceMap[parentVertices[endId]]);
|
|
1963
|
-
endId = parentVertices[endId];
|
|
1964
|
-
}
|
|
1965
|
-
if (path.length !== 0) {
|
|
1966
|
-
path.unshift(start);
|
|
1967
|
-
}
|
|
1968
|
-
return GeoGraphItinerary.fromGraphVertices(start.coords, end.coords, path, edgesWeights);
|
|
1969
|
-
}
|
|
1970
|
-
getShortestPathsBetweenGeoGraphVertices(start, ends, weightFn, acceptEdgeFn) {
|
|
1971
|
-
const distanceMap = {}, checking = {}, vertexList = {}, vertices = {}, parentVertices = {};
|
|
1972
|
-
this._graph.vertices.forEach((vertex) => {
|
|
1973
|
-
vertices[vertex.id] = vertex;
|
|
1974
|
-
distanceMap[vertex.id] = Infinity;
|
|
1975
|
-
checking[vertex.id] = null;
|
|
1976
|
-
vertexList[vertex.id] = true;
|
|
1977
|
-
});
|
|
1978
|
-
distanceMap[start.id] = 0;
|
|
1979
|
-
while (Object.keys(vertexList).length > 0) {
|
|
1980
|
-
const keys = Object.keys(vertexList).map(Number);
|
|
1981
|
-
const current = Number(
|
|
1982
|
-
keys.reduce((_checking, vertexId) => {
|
|
1983
|
-
return distanceMap[_checking] > distanceMap[vertexId] ? vertexId : _checking;
|
|
1984
|
-
}, keys[0])
|
|
1985
|
-
);
|
|
1986
|
-
this._graph.edges.filter((edge) => {
|
|
1987
|
-
if (acceptEdgeFn && !acceptEdgeFn(edge) || this.disabledEdges.has(edge)) {
|
|
1988
|
-
return false;
|
|
1989
|
-
}
|
|
1990
|
-
const from = edge.vertex1.id, to = edge.vertex2.id;
|
|
1991
|
-
return from === current || to === current;
|
|
1992
|
-
}).forEach((edge) => {
|
|
1993
|
-
let to, from, reversed = false;
|
|
1994
|
-
if (edge.vertex1.id === current) {
|
|
1995
|
-
to = edge.vertex2.id;
|
|
1996
|
-
from = edge.vertex1.id;
|
|
1997
|
-
} else {
|
|
1998
|
-
to = edge.vertex1.id;
|
|
1999
|
-
from = edge.vertex2.id;
|
|
2000
|
-
reversed = true;
|
|
2001
|
-
}
|
|
2002
|
-
if (edge.isOneway && reversed) {
|
|
2003
|
-
return;
|
|
2004
|
-
}
|
|
2005
|
-
const distance = distanceMap[current] + weightFn(edge);
|
|
2006
|
-
if (distanceMap[to] > distance) {
|
|
2007
|
-
distanceMap[to] = distance;
|
|
2008
|
-
checking[to] = current;
|
|
2009
|
-
parentVertices[to] = from;
|
|
2010
|
-
}
|
|
2011
|
-
});
|
|
2012
|
-
delete vertexList[current];
|
|
2013
|
-
}
|
|
2014
|
-
return ends.map((end) => {
|
|
2015
|
-
const edgesWeights = [];
|
|
2016
|
-
const path = [];
|
|
2017
|
-
let endId = end.id;
|
|
2018
|
-
while (typeof parentVertices[endId] !== "undefined") {
|
|
2019
|
-
path.unshift(vertices[endId]);
|
|
2020
|
-
edgesWeights.unshift(distanceMap[endId] - distanceMap[parentVertices[endId]]);
|
|
2021
|
-
endId = parentVertices[endId];
|
|
2022
|
-
}
|
|
2023
|
-
if (path.length !== 0) {
|
|
2024
|
-
path.unshift(start);
|
|
2025
|
-
}
|
|
2026
|
-
return GeoGraphItinerary.fromGraphVertices(start.coords, end.coords, path, edgesWeights);
|
|
2027
|
-
});
|
|
2028
|
-
}
|
|
2029
|
-
};
|
|
2030
|
-
__publicField(_GeoGraphRouter, "DEFAULT_OPTIONS", {
|
|
2031
|
-
projectionMaxDistance: 50,
|
|
2032
|
-
weightEdgeFn: (edge) => edge.length
|
|
2033
|
-
});
|
|
2034
|
-
let GeoGraphRouter = _GeoGraphRouter;
|
|
2035
1281
|
exports.AbsoluteHeading = AbsoluteHeading;
|
|
2036
1282
|
exports.Attitude = Attitude;
|
|
2037
1283
|
exports.BoundingBox = BoundingBox;
|
|
2038
1284
|
exports.Constants = Constants;
|
|
2039
1285
|
exports.Coordinates = Coordinates;
|
|
2040
|
-
exports.GeoGraph = GeoGraph;
|
|
2041
|
-
exports.GeoGraphEdge = GeoGraphEdge;
|
|
2042
|
-
exports.GeoGraphItinerary = GeoGraphItinerary;
|
|
2043
|
-
exports.GeoGraphProjection = GeoGraphProjection;
|
|
2044
|
-
exports.GeoGraphProjectionHandler = GeoGraphProjectionHandler;
|
|
2045
|
-
exports.GeoGraphRouter = GeoGraphRouter;
|
|
2046
|
-
exports.GeoGraphVertex = GeoGraphVertex;
|
|
2047
1286
|
exports.GeoRef = GeoRef;
|
|
2048
1287
|
exports.GeoRelativePosition = GeoRelativePosition;
|
|
2049
1288
|
exports.Level = Level;
|
|
2050
|
-
exports.NoRouteFoundError = NoRouteFoundError;
|
|
2051
1289
|
exports.RelativePosition = RelativePosition;
|
|
2052
1290
|
exports.UserPosition = UserPosition;
|
|
2053
1291
|
exports.Utils = Utils;
|