@wemap/geo 10.10.0 → 10.11.2
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 +1848 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1846 -0
- package/dist/index.mjs.map +1 -0
- package/index.d.ts +24 -6
- package/package.json +3 -3
- package/src/graph/Network.js +1 -0
- package/dist/wemap-geo.es.js +0 -3271
- package/dist/wemap-geo.es.js.map +0 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,1848 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => {
|
|
5
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
return value;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
const maths = require("@wemap/maths");
|
|
10
|
+
const Logger = require("@wemap/logger");
|
|
11
|
+
const _interopDefaultLegacy = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
|
|
12
|
+
const Logger__default = /* @__PURE__ */ _interopDefaultLegacy(Logger);
|
|
13
|
+
const R_MAJOR = 6378137;
|
|
14
|
+
const R_MINOR = 63567523142e-4;
|
|
15
|
+
const EARTH_GRAVITY = 9.80665;
|
|
16
|
+
const EPS_DEG_MM = 1e-8;
|
|
17
|
+
const EPS_MM = 1e-3;
|
|
18
|
+
const ELLIPSOID_FLATNESS = (R_MAJOR - R_MINOR) / R_MAJOR;
|
|
19
|
+
const ECCENTRICITY = Math.sqrt(ELLIPSOID_FLATNESS * (2 - ELLIPSOID_FLATNESS));
|
|
20
|
+
const ECCENTRICITY_2 = ECCENTRICITY * ECCENTRICITY;
|
|
21
|
+
const R_MAJOR_2 = R_MAJOR * R_MAJOR;
|
|
22
|
+
const R_MAJOR_4 = R_MAJOR_2 * R_MAJOR_2;
|
|
23
|
+
const R_MINOR_2 = R_MINOR * R_MINOR;
|
|
24
|
+
const R_MINOR_4 = R_MINOR_2 * R_MINOR_2;
|
|
25
|
+
const CIRCUMFERENCE = R_MAJOR * 2 * Math.PI;
|
|
26
|
+
const Constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
27
|
+
__proto__: null,
|
|
28
|
+
R_MAJOR,
|
|
29
|
+
R_MINOR,
|
|
30
|
+
EARTH_GRAVITY,
|
|
31
|
+
EPS_DEG_MM,
|
|
32
|
+
EPS_MM,
|
|
33
|
+
ELLIPSOID_FLATNESS,
|
|
34
|
+
ECCENTRICITY,
|
|
35
|
+
ECCENTRICITY_2,
|
|
36
|
+
R_MAJOR_2,
|
|
37
|
+
R_MAJOR_4,
|
|
38
|
+
R_MINOR_2,
|
|
39
|
+
R_MINOR_4,
|
|
40
|
+
CIRCUMFERENCE
|
|
41
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
42
|
+
const _Level = class {
|
|
43
|
+
static checkType(level) {
|
|
44
|
+
if (level === null) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (typeof level === "number" && !isNaN(level)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(level) && level.length === 2) {
|
|
51
|
+
const [low, up] = level;
|
|
52
|
+
if (typeof low === "number" && !isNaN(low) && typeof up === "number" && !isNaN(up)) {
|
|
53
|
+
if (low > up || low === up) {
|
|
54
|
+
throw Error(`Invalid level range: [${low}, ${up}]`);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw Error(`Unknown level format: ${level}`);
|
|
60
|
+
}
|
|
61
|
+
static isRange(level) {
|
|
62
|
+
if (_Level.VERIFY_TYPING) {
|
|
63
|
+
this.checkType(level);
|
|
64
|
+
}
|
|
65
|
+
return Array.isArray(level);
|
|
66
|
+
}
|
|
67
|
+
static clone(level) {
|
|
68
|
+
if (_Level.VERIFY_TYPING) {
|
|
69
|
+
this.checkType(level);
|
|
70
|
+
}
|
|
71
|
+
if (level === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (typeof level === "number") {
|
|
75
|
+
return level;
|
|
76
|
+
}
|
|
77
|
+
return [level[0], level[1]];
|
|
78
|
+
}
|
|
79
|
+
static fromString(str) {
|
|
80
|
+
if (str === null) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (typeof str !== "string") {
|
|
84
|
+
throw Error(`argument must be a string, got ${typeof str}`);
|
|
85
|
+
}
|
|
86
|
+
if (!isNaN(Number(str))) {
|
|
87
|
+
return parseFloat(str);
|
|
88
|
+
}
|
|
89
|
+
const splited = str.split(";");
|
|
90
|
+
if (splited.length === 2) {
|
|
91
|
+
const low = Number(splited[0]);
|
|
92
|
+
const up = Number(splited[1]);
|
|
93
|
+
this.checkType([low, up]);
|
|
94
|
+
return [parseFloat(splited[0]), parseFloat(splited[1])];
|
|
95
|
+
}
|
|
96
|
+
throw Error(`Cannot parse following level: ${str}`);
|
|
97
|
+
}
|
|
98
|
+
static contains(container, targeted) {
|
|
99
|
+
if (_Level.VERIFY_TYPING) {
|
|
100
|
+
this.checkType(container);
|
|
101
|
+
this.checkType(targeted);
|
|
102
|
+
}
|
|
103
|
+
if (container === targeted) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(container)) {
|
|
107
|
+
if (Array.isArray(targeted)) {
|
|
108
|
+
return container[0] <= targeted[0] && container[1] >= targeted[1];
|
|
109
|
+
}
|
|
110
|
+
if (targeted === null) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return container[0] <= targeted && container[1] >= targeted;
|
|
114
|
+
}
|
|
115
|
+
if (container === null || targeted === null) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return container <= targeted[0] && container >= targeted[1];
|
|
119
|
+
}
|
|
120
|
+
static intersection(first, second) {
|
|
121
|
+
if (_Level.VERIFY_TYPING) {
|
|
122
|
+
this.checkType(first);
|
|
123
|
+
this.checkType(second);
|
|
124
|
+
}
|
|
125
|
+
if (first === null || second === null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
if (this.equals(first, second)) {
|
|
129
|
+
return this.clone(first);
|
|
130
|
+
}
|
|
131
|
+
if (typeof first === "number" && typeof second === "number") {
|
|
132
|
+
return first === second ? first : null;
|
|
133
|
+
}
|
|
134
|
+
if (Array.isArray(first) && !Array.isArray(second)) {
|
|
135
|
+
if (this.contains(first, second)) {
|
|
136
|
+
return second;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(first) && Array.isArray(second)) {
|
|
141
|
+
if (this.contains(second, first)) {
|
|
142
|
+
return first;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const low = Math.max(first[0], second[0]);
|
|
147
|
+
const up = Math.min(first[1], second[1]);
|
|
148
|
+
if (up === low) {
|
|
149
|
+
return up;
|
|
150
|
+
}
|
|
151
|
+
return up < low ? null : [low, up];
|
|
152
|
+
}
|
|
153
|
+
static intersect(first, second) {
|
|
154
|
+
if (_Level.VERIFY_TYPING) {
|
|
155
|
+
this.checkType(first);
|
|
156
|
+
this.checkType(second);
|
|
157
|
+
}
|
|
158
|
+
if (first === null && second === null) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return this.intersection(first, second) !== null;
|
|
162
|
+
}
|
|
163
|
+
static union(first, second) {
|
|
164
|
+
if (_Level.VERIFY_TYPING) {
|
|
165
|
+
this.checkType(first);
|
|
166
|
+
this.checkType(second);
|
|
167
|
+
}
|
|
168
|
+
if (first === second) {
|
|
169
|
+
return this.clone(first);
|
|
170
|
+
}
|
|
171
|
+
if (second === null) {
|
|
172
|
+
return this.clone(first);
|
|
173
|
+
}
|
|
174
|
+
if (first === null) {
|
|
175
|
+
return this.clone(second);
|
|
176
|
+
}
|
|
177
|
+
let low, up;
|
|
178
|
+
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
179
|
+
low = Math.min(first, second);
|
|
180
|
+
up = Math.max(first, second);
|
|
181
|
+
} else if (Array.isArray(first) && !Array.isArray(second)) {
|
|
182
|
+
low = Math.min(first[0], second);
|
|
183
|
+
up = Math.max(first[1], second);
|
|
184
|
+
} else if (!Array.isArray(first) && Array.isArray(second)) {
|
|
185
|
+
low = Math.min(second[0], first);
|
|
186
|
+
up = Math.max(second[1], first);
|
|
187
|
+
} else {
|
|
188
|
+
low = Math.min(first[0], second[0]);
|
|
189
|
+
up = Math.max(first[1], second[1]);
|
|
190
|
+
}
|
|
191
|
+
if (low === up) {
|
|
192
|
+
return low;
|
|
193
|
+
}
|
|
194
|
+
return [low, up];
|
|
195
|
+
}
|
|
196
|
+
static multiplyBy(level, factor) {
|
|
197
|
+
if (_Level.VERIFY_TYPING) {
|
|
198
|
+
this.checkType(level);
|
|
199
|
+
}
|
|
200
|
+
if (level === null) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
return Array.isArray(level) ? [level[0] * factor, level[1] * factor] : level * factor;
|
|
204
|
+
}
|
|
205
|
+
static toString(level) {
|
|
206
|
+
if (_Level.VERIFY_TYPING) {
|
|
207
|
+
this.checkType(level);
|
|
208
|
+
}
|
|
209
|
+
if (level === null) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
return Array.isArray(level) ? level[0] + ";" + level[1] : String(level);
|
|
213
|
+
}
|
|
214
|
+
static equals(first, second) {
|
|
215
|
+
if (_Level.VERIFY_TYPING) {
|
|
216
|
+
this.checkType(first);
|
|
217
|
+
this.checkType(second);
|
|
218
|
+
}
|
|
219
|
+
if (first === second) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (Array.isArray(first) && Array.isArray(second)) {
|
|
223
|
+
return first[0] === second[0] && first[1] === second[1];
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
static diff(first, second) {
|
|
228
|
+
if (_Level.VERIFY_TYPING) {
|
|
229
|
+
this.checkType(first);
|
|
230
|
+
this.checkType(second);
|
|
231
|
+
}
|
|
232
|
+
if (first === null || second === null) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
236
|
+
return second - first;
|
|
237
|
+
}
|
|
238
|
+
if (Array.isArray(first) && !Array.isArray(second)) {
|
|
239
|
+
if (first[0] === second) {
|
|
240
|
+
return second - first[1];
|
|
241
|
+
}
|
|
242
|
+
if (first[1] === second) {
|
|
243
|
+
return second - first[0];
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
if (Array.isArray(second) && !Array.isArray(first)) {
|
|
248
|
+
if (first === second[0]) {
|
|
249
|
+
return second[1] - first;
|
|
250
|
+
}
|
|
251
|
+
if (first === second[1]) {
|
|
252
|
+
return second[0] - first;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
if (_Level.equals(first, second)) {
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
let Level = _Level;
|
|
263
|
+
__publicField(Level, "VERIFY_TYPING", false);
|
|
264
|
+
class Coordinates {
|
|
265
|
+
constructor(lat, lng, alt = null, level = null) {
|
|
266
|
+
__publicField(this, "_lat");
|
|
267
|
+
__publicField(this, "_lng");
|
|
268
|
+
__publicField(this, "_alt", null);
|
|
269
|
+
__publicField(this, "_level", null);
|
|
270
|
+
__publicField(this, "_ecef");
|
|
271
|
+
__publicField(this, "autoWrap", true);
|
|
272
|
+
this.lat = lat;
|
|
273
|
+
this.lng = lng;
|
|
274
|
+
this.alt = alt;
|
|
275
|
+
this.level = level;
|
|
276
|
+
this._ecef = null;
|
|
277
|
+
}
|
|
278
|
+
get lat() {
|
|
279
|
+
return this._lat;
|
|
280
|
+
}
|
|
281
|
+
set lat(lat) {
|
|
282
|
+
if (Math.abs(lat) <= 90) {
|
|
283
|
+
this._lat = lat;
|
|
284
|
+
} else {
|
|
285
|
+
throw new Error("lat argument is not in [-90; 90]");
|
|
286
|
+
}
|
|
287
|
+
this._ecef = null;
|
|
288
|
+
}
|
|
289
|
+
get latitude() {
|
|
290
|
+
return this._lat;
|
|
291
|
+
}
|
|
292
|
+
set latitude(_) {
|
|
293
|
+
throw new Error("Please use Coordinates#lat setter instead of Coordinate#latitude");
|
|
294
|
+
}
|
|
295
|
+
get lng() {
|
|
296
|
+
return this._lng;
|
|
297
|
+
}
|
|
298
|
+
set lng(lng) {
|
|
299
|
+
this._lng = lng;
|
|
300
|
+
if (this.autoWrap) {
|
|
301
|
+
this.wrap();
|
|
302
|
+
}
|
|
303
|
+
this._ecef = null;
|
|
304
|
+
}
|
|
305
|
+
get longitude() {
|
|
306
|
+
return this._lng;
|
|
307
|
+
}
|
|
308
|
+
set longitude(_) {
|
|
309
|
+
throw new Error("Please use Coordinates#lng setter instead of Coordinate#longitude");
|
|
310
|
+
}
|
|
311
|
+
get alt() {
|
|
312
|
+
return this._alt;
|
|
313
|
+
}
|
|
314
|
+
set alt(alt) {
|
|
315
|
+
this._alt = alt;
|
|
316
|
+
this._ecef = null;
|
|
317
|
+
}
|
|
318
|
+
get level() {
|
|
319
|
+
return this._level;
|
|
320
|
+
}
|
|
321
|
+
set level(level) {
|
|
322
|
+
Level.checkType(level);
|
|
323
|
+
this._level = level;
|
|
324
|
+
}
|
|
325
|
+
clone() {
|
|
326
|
+
const output = new Coordinates(this.lat, this.lng, this.alt);
|
|
327
|
+
if (this.level !== null) {
|
|
328
|
+
output.level = Level.clone(this.level);
|
|
329
|
+
}
|
|
330
|
+
return output;
|
|
331
|
+
}
|
|
332
|
+
wrap() {
|
|
333
|
+
if (this._lng <= -180 || this._lng > 180) {
|
|
334
|
+
this._lng = maths.wrap(this._lng, -180, 180);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
static equals(pos1, pos2, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
338
|
+
if (pos1 === null && pos1 === pos2) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
if (!(pos1 instanceof Coordinates) || !(pos2 instanceof Coordinates)) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
return Math.abs(pos2.lat - pos1.lat) < eps && Math.abs(pos2.lng - pos1.lng) < eps && (pos1.alt === pos2.alt || pos1.alt !== null && pos2.alt !== null && Math.abs(pos2.alt - pos1.alt) < epsAlt) && Level.equals(pos1.level, pos2.level);
|
|
345
|
+
}
|
|
346
|
+
equals(other) {
|
|
347
|
+
return Coordinates.equals(this, other);
|
|
348
|
+
}
|
|
349
|
+
destinationPoint(distance, bearing, elevation = null) {
|
|
350
|
+
const newPoint = this.clone();
|
|
351
|
+
newPoint.move(distance, bearing, elevation);
|
|
352
|
+
return newPoint;
|
|
353
|
+
}
|
|
354
|
+
move(distance, bearing, elevation = null) {
|
|
355
|
+
const dR = distance / R_MAJOR;
|
|
356
|
+
const cosDr = Math.cos(dR);
|
|
357
|
+
const sinDr = Math.sin(dR);
|
|
358
|
+
const phi1 = maths.deg2rad(this.lat);
|
|
359
|
+
const lambda1 = maths.deg2rad(this.lng);
|
|
360
|
+
const phi2 = Math.asin(
|
|
361
|
+
Math.sin(phi1) * cosDr + Math.cos(phi1) * sinDr * Math.cos(bearing)
|
|
362
|
+
);
|
|
363
|
+
const lambda2 = lambda1 + Math.atan2(
|
|
364
|
+
Math.sin(bearing) * sinDr * Math.cos(phi1),
|
|
365
|
+
cosDr - Math.sin(phi1) * Math.sin(phi2)
|
|
366
|
+
);
|
|
367
|
+
this.lat = maths.rad2deg(phi2);
|
|
368
|
+
this.lng = maths.rad2deg(lambda2);
|
|
369
|
+
if (elevation !== null) {
|
|
370
|
+
if (this.alt === null) {
|
|
371
|
+
throw new Error("Point altitude is not defined");
|
|
372
|
+
}
|
|
373
|
+
this.alt += elevation;
|
|
374
|
+
}
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
377
|
+
distanceTo(location2) {
|
|
378
|
+
const lat1 = this.lat;
|
|
379
|
+
const lng1 = this.lng;
|
|
380
|
+
const lat2 = location2.lat;
|
|
381
|
+
const lng2 = location2.lng;
|
|
382
|
+
const dlat = maths.deg2rad(lat2 - lat1);
|
|
383
|
+
const dlng = maths.deg2rad(lng2 - lng1);
|
|
384
|
+
const dlngsin = Math.sin(dlng / 2);
|
|
385
|
+
const dlatsin = Math.sin(dlat / 2);
|
|
386
|
+
const lat1rad = maths.deg2rad(lat1);
|
|
387
|
+
const lat1cos = Math.cos(lat1rad);
|
|
388
|
+
const lat2rad = maths.deg2rad(lat2);
|
|
389
|
+
const lat2cos = Math.cos(lat2rad);
|
|
390
|
+
const angle = dlatsin * dlatsin + lat1cos * lat2cos * dlngsin * dlngsin;
|
|
391
|
+
const tangy = Math.sqrt(angle);
|
|
392
|
+
const tangx = Math.sqrt(1 - angle);
|
|
393
|
+
const cosn = 2 * Math.atan2(tangy, tangx);
|
|
394
|
+
return R_MAJOR * cosn;
|
|
395
|
+
}
|
|
396
|
+
static distanceBetween(point1, point2) {
|
|
397
|
+
return point1.distanceTo(point2);
|
|
398
|
+
}
|
|
399
|
+
bearingTo(location2) {
|
|
400
|
+
const lat1 = maths.deg2rad(this.lat);
|
|
401
|
+
const lat2 = maths.deg2rad(location2.lat);
|
|
402
|
+
const diffLng = maths.deg2rad(location2.lng - this.lng);
|
|
403
|
+
return Math.atan2(
|
|
404
|
+
Math.sin(diffLng) * Math.cos(lat2),
|
|
405
|
+
Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLng)
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
static bearingTo(point1, point2) {
|
|
409
|
+
return point1.bearingTo(point2);
|
|
410
|
+
}
|
|
411
|
+
get enuToEcefRotation() {
|
|
412
|
+
const rot1 = maths.Quaternion.fromAxisAngle([0, 0, 1], Math.PI / 2 + maths.deg2rad(this.lng));
|
|
413
|
+
const rot2 = maths.Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 - maths.deg2rad(this.lat));
|
|
414
|
+
return maths.Quaternion.multiply(rot1, rot2);
|
|
415
|
+
}
|
|
416
|
+
get ecefToEnuRotation() {
|
|
417
|
+
const rot1 = maths.Quaternion.fromAxisAngle([1, 0, 0], maths.deg2rad(this.lat) - Math.PI / 2);
|
|
418
|
+
const rot2 = maths.Quaternion.fromAxisAngle([0, 0, 1], -maths.deg2rad(this.lng) - Math.PI / 2);
|
|
419
|
+
return maths.Quaternion.multiply(rot1, rot2);
|
|
420
|
+
}
|
|
421
|
+
get ecef() {
|
|
422
|
+
if (!this._ecef) {
|
|
423
|
+
const lat = maths.deg2rad(this.lat);
|
|
424
|
+
const lng = maths.deg2rad(this.lng);
|
|
425
|
+
const alt = this.alt || 0;
|
|
426
|
+
const x = (R_MAJOR + alt) * Math.cos(lat) * Math.cos(lng);
|
|
427
|
+
const y = (R_MAJOR + alt) * Math.cos(lat) * Math.sin(lng);
|
|
428
|
+
const z = (R_MAJOR + alt) * Math.sin(lat);
|
|
429
|
+
this._ecef = [x, y, z];
|
|
430
|
+
}
|
|
431
|
+
return this._ecef;
|
|
432
|
+
}
|
|
433
|
+
static fromECEF(ecef) {
|
|
434
|
+
const x = ecef[0];
|
|
435
|
+
const y = ecef[1];
|
|
436
|
+
const z = ecef[2];
|
|
437
|
+
const p = Math.sqrt(x ** 2 + y ** 2);
|
|
438
|
+
let lng = Math.atan2(y, x);
|
|
439
|
+
const lat = Math.atan2(z, p);
|
|
440
|
+
const alt = p / Math.cos(lat) - R_MAJOR;
|
|
441
|
+
lng = lng % (2 * Math.PI);
|
|
442
|
+
const newPoint = new Coordinates(maths.rad2deg(lat), maths.rad2deg(lng), alt);
|
|
443
|
+
newPoint._ecef = ecef;
|
|
444
|
+
return newPoint;
|
|
445
|
+
}
|
|
446
|
+
getSegmentProjection(p1, p2) {
|
|
447
|
+
const a = maths.Vector3.normalize(p1.ecef);
|
|
448
|
+
const b = maths.Vector3.normalize(p2.ecef);
|
|
449
|
+
const c = maths.Vector3.normalize(this.ecef);
|
|
450
|
+
const G = maths.Vector3.cross(a, b);
|
|
451
|
+
if (maths.Vector3.norm(G) === 0) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
const F = maths.Vector3.cross(c, G);
|
|
455
|
+
const t = maths.Vector3.normalize(maths.Vector3.cross(G, F));
|
|
456
|
+
const posECEF = maths.Vector3.multiplyScalar(t, R_MAJOR);
|
|
457
|
+
const poseCoordinates = Coordinates.fromECEF(posECEF);
|
|
458
|
+
let alt;
|
|
459
|
+
if (p1.alt !== null && p2.alt !== null) {
|
|
460
|
+
alt = (p1.alt + p2.alt) / 2;
|
|
461
|
+
}
|
|
462
|
+
const projection = new Coordinates(
|
|
463
|
+
poseCoordinates.lat,
|
|
464
|
+
poseCoordinates.lng,
|
|
465
|
+
alt,
|
|
466
|
+
Level.intersection(p1.level, p2.level)
|
|
467
|
+
);
|
|
468
|
+
if (Math.abs(p1.distanceTo(p2) - p1.distanceTo(projection) - p2.distanceTo(projection)) > EPS_MM) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
return projection;
|
|
472
|
+
}
|
|
473
|
+
toString() {
|
|
474
|
+
let str = "[" + this._lat.toFixed(7) + ", " + this._lng.toFixed(7);
|
|
475
|
+
if (this._alt !== null) {
|
|
476
|
+
str += ", " + this._alt.toFixed(2);
|
|
477
|
+
}
|
|
478
|
+
if (this._level !== null) {
|
|
479
|
+
str += ", [" + Level.toString(this._level) + "]";
|
|
480
|
+
}
|
|
481
|
+
str += "]";
|
|
482
|
+
return str;
|
|
483
|
+
}
|
|
484
|
+
toJson() {
|
|
485
|
+
return {
|
|
486
|
+
lat: this.lat,
|
|
487
|
+
lng: this.lng,
|
|
488
|
+
...this.alt !== null && { alt: this.alt },
|
|
489
|
+
...this.level !== null && { level: this.level }
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
static fromJson(json) {
|
|
493
|
+
return new Coordinates(json.lat, json.lng, json.alt, json.level);
|
|
494
|
+
}
|
|
495
|
+
toCompressedJson() {
|
|
496
|
+
if (this.level !== null) {
|
|
497
|
+
return [this.lat, this.lng, this.alt, this.level];
|
|
498
|
+
}
|
|
499
|
+
if (this.alt !== null) {
|
|
500
|
+
return [this.lat, this.lng, this.alt];
|
|
501
|
+
}
|
|
502
|
+
return [this.lat, this.lng];
|
|
503
|
+
}
|
|
504
|
+
static fromCompressedJson(json) {
|
|
505
|
+
const coords = new Coordinates(json[0], json[1]);
|
|
506
|
+
if (json.length > 2) {
|
|
507
|
+
coords.alt = json[2];
|
|
508
|
+
}
|
|
509
|
+
if (json.length > 3) {
|
|
510
|
+
coords.level = json[3];
|
|
511
|
+
}
|
|
512
|
+
return coords;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
class UserPosition extends Coordinates {
|
|
516
|
+
constructor(lat, lng, alt = null, level = null, time = null, accuracy = null, bearing = null) {
|
|
517
|
+
super(lat, lng, alt, level);
|
|
518
|
+
__publicField(this, "_time", null);
|
|
519
|
+
__publicField(this, "_accuracy", null);
|
|
520
|
+
__publicField(this, "_bearing", null);
|
|
521
|
+
this.time = time;
|
|
522
|
+
this.accuracy = accuracy;
|
|
523
|
+
this.bearing = bearing;
|
|
524
|
+
}
|
|
525
|
+
get time() {
|
|
526
|
+
return this._time;
|
|
527
|
+
}
|
|
528
|
+
set time(time) {
|
|
529
|
+
this._time = time;
|
|
530
|
+
}
|
|
531
|
+
get accuracy() {
|
|
532
|
+
return this._accuracy;
|
|
533
|
+
}
|
|
534
|
+
set accuracy(accuracy) {
|
|
535
|
+
if (accuracy !== null && accuracy < 0) {
|
|
536
|
+
throw new Error("accuracy argument is not a positive number");
|
|
537
|
+
}
|
|
538
|
+
this._accuracy = accuracy;
|
|
539
|
+
}
|
|
540
|
+
get bearing() {
|
|
541
|
+
return this._bearing;
|
|
542
|
+
}
|
|
543
|
+
set bearing(bearing) {
|
|
544
|
+
this._bearing = bearing !== null ? bearing % (2 * Math.PI) : null;
|
|
545
|
+
}
|
|
546
|
+
move(distance, bearing, elevation = null) {
|
|
547
|
+
super.move(distance, bearing, elevation);
|
|
548
|
+
return this;
|
|
549
|
+
}
|
|
550
|
+
destinationPoint(distance, bearing, elevation = null) {
|
|
551
|
+
const newPoint = this.clone();
|
|
552
|
+
newPoint.move(distance, bearing, elevation);
|
|
553
|
+
return newPoint;
|
|
554
|
+
}
|
|
555
|
+
static fromCoordinates(coordinates) {
|
|
556
|
+
return new UserPosition(
|
|
557
|
+
coordinates.lat,
|
|
558
|
+
coordinates.lng,
|
|
559
|
+
coordinates.alt,
|
|
560
|
+
coordinates.level
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
clone() {
|
|
564
|
+
const cloned = UserPosition.fromCoordinates(super.clone());
|
|
565
|
+
cloned.time = this.time;
|
|
566
|
+
cloned.accuracy = this.accuracy;
|
|
567
|
+
cloned.bearing = this.bearing;
|
|
568
|
+
return cloned;
|
|
569
|
+
}
|
|
570
|
+
static equals(pos1, pos2, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
571
|
+
if (pos1 === null && pos1 === pos2) {
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
if (!(pos1 instanceof UserPosition) || !(pos2 instanceof UserPosition)) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
if (!super.equals(pos1, pos2, eps, epsAlt)) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
return pos1.time === pos2.time && pos1.accuracy === pos2.accuracy && pos1.bearing === pos2.bearing;
|
|
581
|
+
}
|
|
582
|
+
equals(other, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
583
|
+
return UserPosition.equals(this, other, eps, epsAlt);
|
|
584
|
+
}
|
|
585
|
+
toJson() {
|
|
586
|
+
return {
|
|
587
|
+
...super.toJson(),
|
|
588
|
+
...this.time !== null && { time: this.time },
|
|
589
|
+
...this.accuracy !== null && { accuracy: this.accuracy },
|
|
590
|
+
...this.bearing !== null && { bearing: this.bearing }
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
static fromJson(json) {
|
|
594
|
+
const position = UserPosition.fromCoordinates(Coordinates.fromJson(json));
|
|
595
|
+
if (typeof json.time !== "undefined") {
|
|
596
|
+
position.time = json.time;
|
|
597
|
+
}
|
|
598
|
+
if (typeof json.accuracy !== "undefined") {
|
|
599
|
+
position.accuracy = json.accuracy;
|
|
600
|
+
}
|
|
601
|
+
if (typeof json.bearing !== "undefined") {
|
|
602
|
+
position.bearing = json.bearing;
|
|
603
|
+
}
|
|
604
|
+
return position;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function sampleRoute(route, stepSize = 0.7, startSampling = 0, length = Number.MAX_VALUE) {
|
|
608
|
+
const endSampling = startSampling + length;
|
|
609
|
+
const sampledRoute = [];
|
|
610
|
+
let lastSample;
|
|
611
|
+
let totalDistanceTraveled = 0;
|
|
612
|
+
let distanceToNextSample = 0;
|
|
613
|
+
let startFound = false;
|
|
614
|
+
for (let segmentIndex = 0; segmentIndex < route.length - 1; segmentIndex++) {
|
|
615
|
+
const p1 = route[segmentIndex];
|
|
616
|
+
const p2 = route[segmentIndex + 1];
|
|
617
|
+
const segmentSize = p1.distanceTo(p2);
|
|
618
|
+
const segmentBearing = p1.bearingTo(p2);
|
|
619
|
+
let distanceTraveledOnSegment = 0;
|
|
620
|
+
if (!startFound) {
|
|
621
|
+
if (startSampling < totalDistanceTraveled + segmentSize) {
|
|
622
|
+
startFound = true;
|
|
623
|
+
distanceToNextSample = startSampling - totalDistanceTraveled;
|
|
624
|
+
} else {
|
|
625
|
+
totalDistanceTraveled += segmentSize;
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
lastSample = Object.assign(p1.clone(), { bearing: segmentBearing });
|
|
630
|
+
while (distanceTraveledOnSegment + distanceToNextSample < segmentSize && totalDistanceTraveled + distanceToNextSample <= endSampling) {
|
|
631
|
+
const newPoint = lastSample.destinationPoint(distanceToNextSample, segmentBearing);
|
|
632
|
+
newPoint.bearing = segmentBearing;
|
|
633
|
+
sampledRoute.push(newPoint);
|
|
634
|
+
lastSample = newPoint;
|
|
635
|
+
distanceTraveledOnSegment += distanceToNextSample;
|
|
636
|
+
totalDistanceTraveled += distanceToNextSample;
|
|
637
|
+
distanceToNextSample = stepSize;
|
|
638
|
+
}
|
|
639
|
+
if (totalDistanceTraveled + distanceToNextSample > endSampling) {
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
const rest = segmentSize - distanceTraveledOnSegment;
|
|
643
|
+
totalDistanceTraveled += rest;
|
|
644
|
+
distanceToNextSample -= rest;
|
|
645
|
+
}
|
|
646
|
+
return sampledRoute;
|
|
647
|
+
}
|
|
648
|
+
function trimRoute(route, startPosition = route[0], length = Number.MAX_VALUE) {
|
|
649
|
+
const newRoute = [];
|
|
650
|
+
let previousPoint = null;
|
|
651
|
+
let currentPointIndex;
|
|
652
|
+
let cumulativeDistance = 0;
|
|
653
|
+
if (route.length <= 1) {
|
|
654
|
+
throw new Error("Route must have at least 2 points");
|
|
655
|
+
}
|
|
656
|
+
for (currentPointIndex = 1; currentPointIndex < route.length; currentPointIndex++) {
|
|
657
|
+
const p1 = route[currentPointIndex - 1];
|
|
658
|
+
const p2 = route[currentPointIndex];
|
|
659
|
+
if (Coordinates.equals(startPosition, p1)) {
|
|
660
|
+
newRoute.push(p1);
|
|
661
|
+
previousPoint = p1;
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
const proj = startPosition.getSegmentProjection(p1, p2);
|
|
665
|
+
if (proj && Coordinates.equals(startPosition, proj) && !proj.equals(p2)) {
|
|
666
|
+
newRoute.push(proj);
|
|
667
|
+
previousPoint = proj;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (!newRoute.length) {
|
|
672
|
+
throw new Error("startPosition is not on the route");
|
|
673
|
+
}
|
|
674
|
+
while (previousPoint && currentPointIndex < route.length) {
|
|
675
|
+
const currentPoint = route[currentPointIndex];
|
|
676
|
+
const dist = previousPoint.distanceTo(currentPoint);
|
|
677
|
+
if (cumulativeDistance + dist >= length || Math.abs(cumulativeDistance + dist - length) <= EPS_MM) {
|
|
678
|
+
const bearing = previousPoint.bearingTo(currentPoint);
|
|
679
|
+
const remainingLength = length - cumulativeDistance;
|
|
680
|
+
const end = previousPoint.destinationPoint(remainingLength, bearing);
|
|
681
|
+
newRoute.push(end);
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
newRoute.push(currentPoint);
|
|
685
|
+
previousPoint = currentPoint;
|
|
686
|
+
cumulativeDistance += dist;
|
|
687
|
+
currentPointIndex++;
|
|
688
|
+
}
|
|
689
|
+
return newRoute;
|
|
690
|
+
}
|
|
691
|
+
function simplifyRoute(coords, precisionAngle = maths.deg2rad(5)) {
|
|
692
|
+
const isClosed = coords[0].equals(coords[coords.length - 1]);
|
|
693
|
+
let newRoute = coords.slice(0, coords.length - (isClosed ? 1 : 0));
|
|
694
|
+
const len = newRoute.length;
|
|
695
|
+
for (let i = isClosed ? 0 : 1; i < len; i++) {
|
|
696
|
+
const p0 = coords[maths.positiveMod(i - 1, len)];
|
|
697
|
+
const p1 = coords[i];
|
|
698
|
+
const p2 = coords[maths.positiveMod(i + 1, len)];
|
|
699
|
+
const seg1Dir = p0.bearingTo(p1);
|
|
700
|
+
const seg2Dir = p1.bearingTo(p2);
|
|
701
|
+
if (Math.abs(seg2Dir - seg1Dir) < precisionAngle) {
|
|
702
|
+
newRoute = newRoute.filter((coord) => coord !== p1);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (isClosed) {
|
|
706
|
+
newRoute.push(newRoute[0]);
|
|
707
|
+
}
|
|
708
|
+
return newRoute;
|
|
709
|
+
}
|
|
710
|
+
function geolocationPositionToUserPosition(geolocationPosition) {
|
|
711
|
+
if (geolocationPosition === null) {
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
const { latitude, longitude, accuracy, heading } = geolocationPosition.coords;
|
|
715
|
+
const userPosition = new UserPosition(latitude, longitude);
|
|
716
|
+
userPosition.time = geolocationPosition.timestamp;
|
|
717
|
+
userPosition.accuracy = accuracy;
|
|
718
|
+
userPosition.bearing = heading ? maths.deg2rad(heading) : null;
|
|
719
|
+
return userPosition;
|
|
720
|
+
}
|
|
721
|
+
function calcDistance(coords) {
|
|
722
|
+
return coords.reduce((acc, coords2, idx, arr) => acc + (idx ? arr[idx - 1].distanceTo(coords2) : 0), 0);
|
|
723
|
+
}
|
|
724
|
+
const Utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
725
|
+
__proto__: null,
|
|
726
|
+
sampleRoute,
|
|
727
|
+
trimRoute,
|
|
728
|
+
simplifyRoute,
|
|
729
|
+
geolocationPositionToUserPosition,
|
|
730
|
+
calcDistance
|
|
731
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
732
|
+
class BoundingBox {
|
|
733
|
+
constructor(northEast, southWest) {
|
|
734
|
+
__publicField(this, "northEast");
|
|
735
|
+
__publicField(this, "southWest");
|
|
736
|
+
this.northEast = northEast;
|
|
737
|
+
this.southWest = southWest;
|
|
738
|
+
if (this.northEast && this.southWest && this.getNorth() < this.getSouth()) {
|
|
739
|
+
throw new Error("Incorrect bounding box");
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
get center() {
|
|
743
|
+
const latCenter = (this.southWest.lat + this.northEast.lat) / 2;
|
|
744
|
+
const lngCenter = (this.northEast.lng + this.southWest.lng) / 2;
|
|
745
|
+
return new Coordinates(latCenter, lngCenter);
|
|
746
|
+
}
|
|
747
|
+
contains(point) {
|
|
748
|
+
return point.lat <= this.northEast.lat && point.lat >= this.southWest.lat && point.lng <= this.northEast.lng && point.lng >= this.southWest.lng;
|
|
749
|
+
}
|
|
750
|
+
extend(obj) {
|
|
751
|
+
const sw = this.southWest, ne = this.northEast;
|
|
752
|
+
let sw2, ne2;
|
|
753
|
+
if (obj instanceof Coordinates) {
|
|
754
|
+
sw2 = obj;
|
|
755
|
+
ne2 = obj;
|
|
756
|
+
} else if (obj instanceof BoundingBox) {
|
|
757
|
+
sw2 = obj.southWest;
|
|
758
|
+
ne2 = obj.northEast;
|
|
759
|
+
} else {
|
|
760
|
+
throw new Error("Unknown parameter");
|
|
761
|
+
}
|
|
762
|
+
this.southWest = new Coordinates(
|
|
763
|
+
Math.min(sw2.lat, sw.lat),
|
|
764
|
+
Math.min(sw2.lng, sw.lng)
|
|
765
|
+
);
|
|
766
|
+
this.northEast = new Coordinates(
|
|
767
|
+
Math.max(ne2.lat, ne.lat),
|
|
768
|
+
Math.max(ne2.lng, ne.lng)
|
|
769
|
+
);
|
|
770
|
+
return this;
|
|
771
|
+
}
|
|
772
|
+
extendsWithMeasure(measure) {
|
|
773
|
+
if (typeof measure !== "number") {
|
|
774
|
+
throw new Error("measure is not a number");
|
|
775
|
+
}
|
|
776
|
+
this.northEast = this.northEast.destinationPoint(measure, 0).move(measure, Math.PI / 2);
|
|
777
|
+
this.southWest = this.southWest.clone().destinationPoint(measure, -Math.PI / 2).destinationPoint(measure, Math.PI);
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
780
|
+
pad(bufferRatio) {
|
|
781
|
+
const sw = this.southWest;
|
|
782
|
+
const ne = this.northEast;
|
|
783
|
+
const heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio;
|
|
784
|
+
const widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
|
|
785
|
+
this.southWest = new Coordinates(sw.lat - heightBuffer, sw.lng - widthBuffer);
|
|
786
|
+
this.northEast = new Coordinates(ne.lat + heightBuffer, ne.lng + widthBuffer);
|
|
787
|
+
return this;
|
|
788
|
+
}
|
|
789
|
+
getSouthWest() {
|
|
790
|
+
return this.southWest;
|
|
791
|
+
}
|
|
792
|
+
getNorthEast() {
|
|
793
|
+
return this.northEast;
|
|
794
|
+
}
|
|
795
|
+
getNorthWest() {
|
|
796
|
+
return new Coordinates(this.getNorth(), this.getWest());
|
|
797
|
+
}
|
|
798
|
+
getSouthEast() {
|
|
799
|
+
return new Coordinates(this.getSouth(), this.getEast());
|
|
800
|
+
}
|
|
801
|
+
getWest() {
|
|
802
|
+
return this.southWest.lng;
|
|
803
|
+
}
|
|
804
|
+
getSouth() {
|
|
805
|
+
return this.southWest.lat;
|
|
806
|
+
}
|
|
807
|
+
getEast() {
|
|
808
|
+
return this.northEast.lng;
|
|
809
|
+
}
|
|
810
|
+
getNorth() {
|
|
811
|
+
return this.northEast.lat;
|
|
812
|
+
}
|
|
813
|
+
static equals(bb1, bb2) {
|
|
814
|
+
return Coordinates.equals(bb1.northEast, bb2.northEast) && Coordinates.equals(bb1.southWest, bb2.southWest);
|
|
815
|
+
}
|
|
816
|
+
equals(other) {
|
|
817
|
+
return BoundingBox.equals(this, other);
|
|
818
|
+
}
|
|
819
|
+
static fromArray(bounds) {
|
|
820
|
+
return new BoundingBox(
|
|
821
|
+
new Coordinates(bounds[3], bounds[2]),
|
|
822
|
+
new Coordinates(bounds[1], bounds[0])
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
static fromCoordinates(coords) {
|
|
826
|
+
if (coords.length === 0) {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
return coords.reduce(
|
|
830
|
+
(acc, _coords) => acc.extend(_coords),
|
|
831
|
+
new BoundingBox(coords[0], coords[0])
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
toArray() {
|
|
835
|
+
return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()];
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
class RelativePosition {
|
|
839
|
+
constructor(x, y, z, time = null, accuracy = null, bearing = null) {
|
|
840
|
+
__publicField(this, "x");
|
|
841
|
+
__publicField(this, "y");
|
|
842
|
+
__publicField(this, "z");
|
|
843
|
+
__publicField(this, "time", null);
|
|
844
|
+
__publicField(this, "_accuracy", null);
|
|
845
|
+
__publicField(this, "_bearing", null);
|
|
846
|
+
this.x = x;
|
|
847
|
+
this.y = y;
|
|
848
|
+
this.z = z;
|
|
849
|
+
this.time = time;
|
|
850
|
+
this.accuracy = accuracy;
|
|
851
|
+
this.bearing = bearing;
|
|
852
|
+
}
|
|
853
|
+
get accuracy() {
|
|
854
|
+
return this._accuracy;
|
|
855
|
+
}
|
|
856
|
+
set accuracy(accuracy) {
|
|
857
|
+
if (accuracy !== null && accuracy < 0) {
|
|
858
|
+
throw new Error("accuracy argument is not a positive number");
|
|
859
|
+
}
|
|
860
|
+
this._accuracy = accuracy;
|
|
861
|
+
}
|
|
862
|
+
get bearing() {
|
|
863
|
+
return this._bearing;
|
|
864
|
+
}
|
|
865
|
+
set bearing(bearing) {
|
|
866
|
+
this._bearing = bearing !== null ? bearing % (2 * Math.PI) : null;
|
|
867
|
+
}
|
|
868
|
+
clone() {
|
|
869
|
+
return new RelativePosition(this.x, this.y, this.z, this.time, this.accuracy, this.bearing);
|
|
870
|
+
}
|
|
871
|
+
static equals(pos1, pos2, eps = EPS_MM) {
|
|
872
|
+
if (pos1 === null && pos1 === pos2) {
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
if (!(pos1 instanceof RelativePosition) || !(pos2 instanceof RelativePosition)) {
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
return Math.abs(pos2.x - pos1.x) < eps && Math.abs(pos2.y - pos1.y) < eps && Math.abs(pos2.z - pos1.z) < eps && pos1.time === pos2.time && pos1.accuracy === pos2.accuracy && pos1.bearing === pos2.bearing;
|
|
879
|
+
}
|
|
880
|
+
equals(other) {
|
|
881
|
+
return RelativePosition.equals(this, other);
|
|
882
|
+
}
|
|
883
|
+
toJson() {
|
|
884
|
+
return {
|
|
885
|
+
x: this.x,
|
|
886
|
+
y: this.y,
|
|
887
|
+
z: this.z,
|
|
888
|
+
...this.time !== null && { time: this.time },
|
|
889
|
+
...this.accuracy !== null && { accuracy: this.accuracy },
|
|
890
|
+
...this.bearing !== null && { bearing: this.bearing }
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
static fromJson(json) {
|
|
894
|
+
return new RelativePosition(json.x, json.y, json.z, json.time, json.accuracy, json.bearing);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
class GeoRelativePosition extends RelativePosition {
|
|
898
|
+
}
|
|
899
|
+
class GeoRef {
|
|
900
|
+
constructor(origin) {
|
|
901
|
+
__publicField(this, "origin");
|
|
902
|
+
__publicField(this, "scale", 1);
|
|
903
|
+
__publicField(this, "heading", 0);
|
|
904
|
+
this.origin = origin;
|
|
905
|
+
}
|
|
906
|
+
localToWorld(localPosition) {
|
|
907
|
+
const enuTranslationScaled = maths.Vector3.multiplyScalar(localPosition, this.scale);
|
|
908
|
+
const rotationOffset = maths.Quaternion.fromAxisAngle([0, 0, 1], this.heading);
|
|
909
|
+
const enuToEcefRotationOrigin = maths.Quaternion.multiply(rotationOffset, this.origin.enuToEcefRotation);
|
|
910
|
+
const ecefTranslation = maths.Quaternion.rotate(enuToEcefRotationOrigin, enuTranslationScaled);
|
|
911
|
+
const ecef = maths.Vector3.sum(this.origin.ecef, ecefTranslation);
|
|
912
|
+
return Coordinates.fromECEF(ecef);
|
|
913
|
+
}
|
|
914
|
+
worldToLocal(coords) {
|
|
915
|
+
const rotationOffset = maths.Quaternion.fromAxisAngle([0, 0, 1], -this.heading);
|
|
916
|
+
const ecefToEnuRotationOrigin = maths.Quaternion.multiply(this.origin.ecefToEnuRotation, rotationOffset);
|
|
917
|
+
const ecefTranslation = maths.Vector3.subtract(coords.ecef, this.origin.ecef);
|
|
918
|
+
const ecefTranslationScaled = maths.Vector3.multiplyScalar(ecefTranslation, 1 / this.scale);
|
|
919
|
+
return maths.Quaternion.rotate(ecefToEnuRotationOrigin, ecefTranslationScaled);
|
|
920
|
+
}
|
|
921
|
+
toJson() {
|
|
922
|
+
return {
|
|
923
|
+
origin: this.origin.toJson(),
|
|
924
|
+
...this.scale !== 1 && { scale: this.scale },
|
|
925
|
+
...this.heading !== 0 && { heading: this.heading }
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
static fromJson(json) {
|
|
929
|
+
const geoRef = new GeoRef(Coordinates.fromJson(json.origin));
|
|
930
|
+
geoRef.scale = typeof json.scale !== "undefined" ? json.scale : 1;
|
|
931
|
+
geoRef.heading = typeof json.heading !== "undefined" ? json.heading : 0;
|
|
932
|
+
return geoRef;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
class Attitude {
|
|
936
|
+
constructor(quaternion, time = null, accuracy = null) {
|
|
937
|
+
__publicField(this, "_quaternion", [1, 0, 0, 0]);
|
|
938
|
+
__publicField(this, "_heading", null);
|
|
939
|
+
__publicField(this, "_eulerAngles", null);
|
|
940
|
+
__publicField(this, "_time", null);
|
|
941
|
+
__publicField(this, "_accuracy", null);
|
|
942
|
+
this.quaternion = quaternion;
|
|
943
|
+
this.time = time;
|
|
944
|
+
this.accuracy = accuracy;
|
|
945
|
+
}
|
|
946
|
+
static unitary() {
|
|
947
|
+
return new Attitude([1, 0, 0, 0]);
|
|
948
|
+
}
|
|
949
|
+
get quaternion() {
|
|
950
|
+
return this._quaternion;
|
|
951
|
+
}
|
|
952
|
+
set quaternion(quaternion) {
|
|
953
|
+
if (Math.abs(1 - maths.Quaternion.norm(quaternion)) > 1e-4) {
|
|
954
|
+
throw new Error("quaternion is not a unit quaternion");
|
|
955
|
+
}
|
|
956
|
+
this._quaternion = quaternion;
|
|
957
|
+
this._heading = null;
|
|
958
|
+
this._eulerAngles = null;
|
|
959
|
+
}
|
|
960
|
+
get time() {
|
|
961
|
+
return this._time;
|
|
962
|
+
}
|
|
963
|
+
set time(time) {
|
|
964
|
+
this._time = time;
|
|
965
|
+
}
|
|
966
|
+
get accuracy() {
|
|
967
|
+
return this._accuracy;
|
|
968
|
+
}
|
|
969
|
+
set accuracy(accuracy) {
|
|
970
|
+
if (accuracy !== null && (accuracy < 0 || accuracy > Math.PI)) {
|
|
971
|
+
throw new Error("accuracy argument (" + accuracy + ") is not in range [0; PI]");
|
|
972
|
+
}
|
|
973
|
+
this._accuracy = accuracy;
|
|
974
|
+
}
|
|
975
|
+
get eulerAngles() {
|
|
976
|
+
if (this._eulerAngles === null) {
|
|
977
|
+
this._eulerAngles = maths.Rotations.quaternionToEulerZXY(this.quaternion);
|
|
978
|
+
}
|
|
979
|
+
return this._eulerAngles;
|
|
980
|
+
}
|
|
981
|
+
get eulerAnglesDegrees() {
|
|
982
|
+
return this.eulerAngles.map((x) => maths.rad2deg(x));
|
|
983
|
+
}
|
|
984
|
+
get heading() {
|
|
985
|
+
if (this._heading === null) {
|
|
986
|
+
let offset = 0;
|
|
987
|
+
if (typeof window !== "undefined" && window && window.orientation) {
|
|
988
|
+
offset = maths.deg2rad(window.orientation);
|
|
989
|
+
}
|
|
990
|
+
this._heading = maths.Rotations.getHeadingFromQuaternion(this.quaternion) + offset;
|
|
991
|
+
}
|
|
992
|
+
return this._heading;
|
|
993
|
+
}
|
|
994
|
+
get headingDegrees() {
|
|
995
|
+
return maths.rad2deg(this.heading);
|
|
996
|
+
}
|
|
997
|
+
static equals(att1, att2) {
|
|
998
|
+
if (att1 === null && att1 === att2) {
|
|
999
|
+
return true;
|
|
1000
|
+
}
|
|
1001
|
+
if (!(att1 instanceof Attitude) || !(att2 instanceof Attitude)) {
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
if (att1 === att2) {
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
return maths.Quaternion.equals(att1.quaternion, att2.quaternion);
|
|
1008
|
+
}
|
|
1009
|
+
equals(other) {
|
|
1010
|
+
return Attitude.equals(this, other);
|
|
1011
|
+
}
|
|
1012
|
+
toJson() {
|
|
1013
|
+
if (this.time === null && this.accuracy === null) {
|
|
1014
|
+
return this.quaternion;
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
q: this.quaternion,
|
|
1018
|
+
...this.time !== null && { time: this.time },
|
|
1019
|
+
...this.accuracy !== null && { accuracy: this.accuracy }
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
static fromJson(json) {
|
|
1023
|
+
if (Array.isArray(json)) {
|
|
1024
|
+
return new Attitude(json, null, null);
|
|
1025
|
+
}
|
|
1026
|
+
return new Attitude(json.q, json.time, json.accuracy);
|
|
1027
|
+
}
|
|
1028
|
+
clone() {
|
|
1029
|
+
return new Attitude(this.quaternion.slice(0), this.time, this.accuracy);
|
|
1030
|
+
}
|
|
1031
|
+
static diff(attitudeStart, attitudeEnd) {
|
|
1032
|
+
const quaternionDiff = maths.Quaternion.multiply(
|
|
1033
|
+
maths.Quaternion.inverse(attitudeStart.quaternion),
|
|
1034
|
+
attitudeEnd.quaternion
|
|
1035
|
+
);
|
|
1036
|
+
let timeDiff = null;
|
|
1037
|
+
if (attitudeEnd.time !== null && attitudeStart.time !== null) {
|
|
1038
|
+
timeDiff = attitudeEnd.time - attitudeStart.time;
|
|
1039
|
+
}
|
|
1040
|
+
let accuracyDiff = null;
|
|
1041
|
+
if (attitudeStart.accuracy !== null && attitudeEnd.accuracy !== null) {
|
|
1042
|
+
accuracyDiff = Math.max(attitudeEnd.accuracy - attitudeStart.accuracy);
|
|
1043
|
+
}
|
|
1044
|
+
return new Attitude(quaternionDiff, timeDiff, accuracyDiff);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
class AbsoluteHeading {
|
|
1048
|
+
constructor(heading, time = null, accuracy = null) {
|
|
1049
|
+
__publicField(this, "heading");
|
|
1050
|
+
__publicField(this, "time", null);
|
|
1051
|
+
__publicField(this, "_accuracy", null);
|
|
1052
|
+
this.heading = heading;
|
|
1053
|
+
this.time = time;
|
|
1054
|
+
this.accuracy = accuracy;
|
|
1055
|
+
}
|
|
1056
|
+
get accuracy() {
|
|
1057
|
+
return this._accuracy;
|
|
1058
|
+
}
|
|
1059
|
+
set accuracy(accuracy) {
|
|
1060
|
+
if (accuracy !== null && (accuracy < 0 || accuracy > Math.PI)) {
|
|
1061
|
+
throw new Error("accuracy argument (" + accuracy + ") is not in range [0; PI]");
|
|
1062
|
+
}
|
|
1063
|
+
this._accuracy = accuracy;
|
|
1064
|
+
}
|
|
1065
|
+
toAttitude() {
|
|
1066
|
+
return new Attitude(
|
|
1067
|
+
maths.Quaternion.fromAxisAngle([0, 0, 1], -this.heading),
|
|
1068
|
+
this.time,
|
|
1069
|
+
this.accuracy
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
static equals(heading1, heading2) {
|
|
1073
|
+
if (heading1 === null && heading1 === heading2) {
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
if (!(heading1 instanceof AbsoluteHeading) || !(heading2 instanceof AbsoluteHeading)) {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
return Math.abs(heading1.heading - heading2.heading) < 1e-8;
|
|
1080
|
+
}
|
|
1081
|
+
equals(other) {
|
|
1082
|
+
return AbsoluteHeading.equals(this, other);
|
|
1083
|
+
}
|
|
1084
|
+
toJson() {
|
|
1085
|
+
return {
|
|
1086
|
+
heading: this.heading,
|
|
1087
|
+
...this.time !== null && { time: this.time },
|
|
1088
|
+
...this.accuracy !== null && { accuracy: this.accuracy }
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
static fromJson(json) {
|
|
1092
|
+
return new AbsoluteHeading(json.heading, json.time, json.accuracy);
|
|
1093
|
+
}
|
|
1094
|
+
clone() {
|
|
1095
|
+
return new AbsoluteHeading(this.heading, this.time, this.accuracy);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
class GraphNode {
|
|
1099
|
+
constructor(coords, builtFrom = null) {
|
|
1100
|
+
__publicField(this, "coords");
|
|
1101
|
+
__publicField(this, "edges", []);
|
|
1102
|
+
__publicField(this, "builtFrom");
|
|
1103
|
+
__publicField(this, "io", false);
|
|
1104
|
+
this.coords = coords;
|
|
1105
|
+
this.builtFrom = builtFrom;
|
|
1106
|
+
}
|
|
1107
|
+
distanceTo(other) {
|
|
1108
|
+
return this.coords.distanceTo(other.coords);
|
|
1109
|
+
}
|
|
1110
|
+
bearingTo(other) {
|
|
1111
|
+
return this.coords.bearingTo(other.coords);
|
|
1112
|
+
}
|
|
1113
|
+
equals(other) {
|
|
1114
|
+
return this.coords.equals(other.coords) && this.builtFrom === other.builtFrom;
|
|
1115
|
+
}
|
|
1116
|
+
clone() {
|
|
1117
|
+
const node = new GraphNode(this.coords, this.builtFrom);
|
|
1118
|
+
node.edges = this.edges.slice(0);
|
|
1119
|
+
node.io = this.io;
|
|
1120
|
+
return node;
|
|
1121
|
+
}
|
|
1122
|
+
toJson() {
|
|
1123
|
+
return this.coords.toCompressedJson();
|
|
1124
|
+
}
|
|
1125
|
+
static fromJson(json, builtFrom = null) {
|
|
1126
|
+
return new GraphNode(Coordinates.fromCompressedJson(json), builtFrom);
|
|
1127
|
+
}
|
|
1128
|
+
_generateLevelFromEdges() {
|
|
1129
|
+
let tmpLevel = null;
|
|
1130
|
+
for (let i = 0; i < this.edges.length; i++) {
|
|
1131
|
+
const edge = this.edges[i];
|
|
1132
|
+
if (edge.level !== null) {
|
|
1133
|
+
if (tmpLevel === null) {
|
|
1134
|
+
tmpLevel = Level.clone(edge.level);
|
|
1135
|
+
} else {
|
|
1136
|
+
tmpLevel = Level.intersection(tmpLevel, edge.level);
|
|
1137
|
+
if (tmpLevel === null) {
|
|
1138
|
+
throw Error("Something bad happend during parsing: We cannot retrieve node level from adjacent ways: " + this.coords);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
this.coords.level = tmpLevel;
|
|
1144
|
+
}
|
|
1145
|
+
_inferNodeLevelByRecursion() {
|
|
1146
|
+
const { level } = this.coords;
|
|
1147
|
+
if (level === null || !Level.isRange(level)) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
if (this.edges.length > 1) {
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
const lookForLevel = (node, visitedNodes) => {
|
|
1154
|
+
visitedNodes.push(node);
|
|
1155
|
+
if (node.coords.level === null) {
|
|
1156
|
+
return null;
|
|
1157
|
+
}
|
|
1158
|
+
if (!Level.isRange(node.coords.level)) {
|
|
1159
|
+
return node.coords.level;
|
|
1160
|
+
}
|
|
1161
|
+
let tmpLevel = null;
|
|
1162
|
+
for (let i = 0; i < node.edges.length; i++) {
|
|
1163
|
+
const edge = node.edges[i];
|
|
1164
|
+
const otherNode = edge.node1 === node ? edge.node2 : edge.node1;
|
|
1165
|
+
if (!visitedNodes.includes(otherNode)) {
|
|
1166
|
+
tmpLevel = Level.union(lookForLevel(otherNode, visitedNodes), tmpLevel);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return tmpLevel;
|
|
1170
|
+
};
|
|
1171
|
+
const othersLevels = lookForLevel(this, []);
|
|
1172
|
+
if (othersLevels !== null) {
|
|
1173
|
+
if (!Level.isRange(othersLevels)) {
|
|
1174
|
+
this.coords.level = othersLevels === level[0] ? level[1] : level[0];
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
throw Error("Level of: " + this.coords.toString() + " cannot be decided");
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
_inferNodeLevelByNeighboors() {
|
|
1181
|
+
const { level } = this.coords;
|
|
1182
|
+
if (level === null || !Level.isRange(level)) {
|
|
1183
|
+
return true;
|
|
1184
|
+
}
|
|
1185
|
+
let tmpLevel = null;
|
|
1186
|
+
for (let i = 0; i < this.edges.length; i++) {
|
|
1187
|
+
const edge = this.edges[i];
|
|
1188
|
+
const otherNode = edge.node1 === this ? edge.node2 : edge.node1;
|
|
1189
|
+
tmpLevel = Level.union(otherNode.coords.level, tmpLevel);
|
|
1190
|
+
}
|
|
1191
|
+
if (tmpLevel === null || !Level.isRange(tmpLevel)) {
|
|
1192
|
+
this.coords.level = tmpLevel === level[0] ? level[1] : level[0];
|
|
1193
|
+
}
|
|
1194
|
+
return true;
|
|
1195
|
+
}
|
|
1196
|
+
_checkIO() {
|
|
1197
|
+
this.io = this.coords.level !== null && this.edges.some((edge) => edge.level === null);
|
|
1198
|
+
return true;
|
|
1199
|
+
}
|
|
1200
|
+
static generateNodesLevels(nodes) {
|
|
1201
|
+
nodes.forEach((node) => node._generateLevelFromEdges());
|
|
1202
|
+
nodes.forEach((node) => node._inferNodeLevelByNeighboors());
|
|
1203
|
+
nodes.forEach((node) => node._inferNodeLevelByRecursion());
|
|
1204
|
+
nodes.forEach((node) => node._checkIO());
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
class GraphEdge {
|
|
1208
|
+
constructor(node1, node2, level = null, builtFrom = null) {
|
|
1209
|
+
__publicField(this, "_node1");
|
|
1210
|
+
__publicField(this, "_node2");
|
|
1211
|
+
__publicField(this, "_level", null);
|
|
1212
|
+
__publicField(this, "_bearing", null);
|
|
1213
|
+
__publicField(this, "_length", null);
|
|
1214
|
+
__publicField(this, "_computedSizeAndBearing", false);
|
|
1215
|
+
__publicField(this, "builtFrom");
|
|
1216
|
+
__publicField(this, "isOneway", false);
|
|
1217
|
+
this.node1 = node1;
|
|
1218
|
+
this.node2 = node2;
|
|
1219
|
+
this.level = level;
|
|
1220
|
+
this.builtFrom = builtFrom;
|
|
1221
|
+
}
|
|
1222
|
+
get node1() {
|
|
1223
|
+
return this._node1;
|
|
1224
|
+
}
|
|
1225
|
+
set node1(node) {
|
|
1226
|
+
if (!(node instanceof GraphNode)) {
|
|
1227
|
+
throw new TypeError("node1 is not a GraphNode");
|
|
1228
|
+
}
|
|
1229
|
+
if (this._node1 instanceof GraphNode && this._node2 !== this._node1) {
|
|
1230
|
+
this._node1.edges = this._node1.edges.filter((edge) => edge !== this);
|
|
1231
|
+
}
|
|
1232
|
+
node.edges.push(this);
|
|
1233
|
+
this._node1 = node;
|
|
1234
|
+
this._computedSizeAndBearing = false;
|
|
1235
|
+
}
|
|
1236
|
+
get node2() {
|
|
1237
|
+
return this._node2;
|
|
1238
|
+
}
|
|
1239
|
+
set node2(node) {
|
|
1240
|
+
if (!(node instanceof GraphNode)) {
|
|
1241
|
+
throw new TypeError("node2 is not a GraphNode");
|
|
1242
|
+
}
|
|
1243
|
+
if (this._node2 instanceof GraphNode && this._node2 !== this._node1) {
|
|
1244
|
+
this._node2.edges = this._node2.edges.filter((edge) => edge !== this);
|
|
1245
|
+
}
|
|
1246
|
+
node.edges.push(this);
|
|
1247
|
+
this._node2 = node;
|
|
1248
|
+
this._computedSizeAndBearing = false;
|
|
1249
|
+
}
|
|
1250
|
+
get level() {
|
|
1251
|
+
return this._level;
|
|
1252
|
+
}
|
|
1253
|
+
set level(level) {
|
|
1254
|
+
Level.checkType(level);
|
|
1255
|
+
this._level = level;
|
|
1256
|
+
}
|
|
1257
|
+
get bearing() {
|
|
1258
|
+
if (!this._computedSizeAndBearing) {
|
|
1259
|
+
this._computeSizeAndBearing();
|
|
1260
|
+
}
|
|
1261
|
+
return this._bearing;
|
|
1262
|
+
}
|
|
1263
|
+
get length() {
|
|
1264
|
+
if (!this._computedSizeAndBearing) {
|
|
1265
|
+
this._computeSizeAndBearing();
|
|
1266
|
+
}
|
|
1267
|
+
return this._length;
|
|
1268
|
+
}
|
|
1269
|
+
_computeSizeAndBearing() {
|
|
1270
|
+
this._length = this.node1.distanceTo(this.node2);
|
|
1271
|
+
this._bearing = this.node1.bearingTo(this.node2);
|
|
1272
|
+
this._computedSizeAndBearing = true;
|
|
1273
|
+
}
|
|
1274
|
+
equals(other) {
|
|
1275
|
+
if (this === other) {
|
|
1276
|
+
return true;
|
|
1277
|
+
}
|
|
1278
|
+
if (!(other instanceof GraphEdge)) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
return other.node1.equals(this.node1) && other.node2.equals(this.node2) && Level.equals(other.level, this.level) && other.isOneway === this.isOneway && other.builtFrom === this.builtFrom;
|
|
1282
|
+
}
|
|
1283
|
+
clone() {
|
|
1284
|
+
const edge = new GraphEdge(this.node1, this.node2, this.level, this.builtFrom);
|
|
1285
|
+
edge.isOneway = this.isOneway;
|
|
1286
|
+
return edge;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function getEdgeByNodes(edges, node1, node2) {
|
|
1290
|
+
return edges.find(
|
|
1291
|
+
(edge) => node1 === edge.node1 && node2 === edge.node2 || node2 === edge.node1 && node1 === edge.node2
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
function getNodeByCoords(nodes, coords) {
|
|
1295
|
+
return nodes.find((node) => node.coords.equals(coords));
|
|
1296
|
+
}
|
|
1297
|
+
const GraphUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1298
|
+
__proto__: null,
|
|
1299
|
+
getEdgeByNodes,
|
|
1300
|
+
getNodeByCoords
|
|
1301
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1302
|
+
class Network {
|
|
1303
|
+
constructor(nodes, edges) {
|
|
1304
|
+
__publicField(this, "nodes");
|
|
1305
|
+
__publicField(this, "edges");
|
|
1306
|
+
this.nodes = Array.isArray(nodes) ? nodes : [];
|
|
1307
|
+
this.edges = Array.isArray(edges) ? edges : [];
|
|
1308
|
+
}
|
|
1309
|
+
getNodeByCoords(coords) {
|
|
1310
|
+
return this.nodes.find((node) => node.coords.equals(coords));
|
|
1311
|
+
}
|
|
1312
|
+
getEdgeByNodes(node1, node2) {
|
|
1313
|
+
return getEdgeByNodes(this.edges, node1, node2);
|
|
1314
|
+
}
|
|
1315
|
+
getBoundingBox(extendedMeasure) {
|
|
1316
|
+
if (!this.nodes.length) {
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
const boundingBox = BoundingBox.fromCoordinates(this.nodes.map((node) => node.coords));
|
|
1320
|
+
if (extendedMeasure) {
|
|
1321
|
+
boundingBox.extendsWithMeasure(extendedMeasure);
|
|
1322
|
+
}
|
|
1323
|
+
return boundingBox;
|
|
1324
|
+
}
|
|
1325
|
+
toDetailedString(_nodeToStringFn, _edgeToStringFn) {
|
|
1326
|
+
let nodeToStringFn = _nodeToStringFn;
|
|
1327
|
+
if (!nodeToStringFn) {
|
|
1328
|
+
nodeToStringFn = (node) => `${node.builtFrom}`;
|
|
1329
|
+
}
|
|
1330
|
+
let edgeToStringFn = _edgeToStringFn;
|
|
1331
|
+
if (!_edgeToStringFn) {
|
|
1332
|
+
edgeToStringFn = (edge) => `${edge.builtFrom}`;
|
|
1333
|
+
}
|
|
1334
|
+
let output = `--- Network ---
|
|
1335
|
+
Nodes: ${this.nodes.length}
|
|
1336
|
+
Edges: ${this.edges.length}
|
|
1337
|
+
---
|
|
1338
|
+
Nodes
|
|
1339
|
+
`;
|
|
1340
|
+
this.nodes.forEach((node) => {
|
|
1341
|
+
output += `${nodeToStringFn(node)} [edges: ${node.edges.length}]
|
|
1342
|
+
`;
|
|
1343
|
+
});
|
|
1344
|
+
output += "---\nEdges\n";
|
|
1345
|
+
this.edges.forEach((edge) => {
|
|
1346
|
+
output += `${edgeToStringFn(edge)} `;
|
|
1347
|
+
output += `[${nodeToStringFn(edge.node1)} -- ${nodeToStringFn(edge.node2)}]
|
|
1348
|
+
`;
|
|
1349
|
+
});
|
|
1350
|
+
output += "---";
|
|
1351
|
+
return output;
|
|
1352
|
+
}
|
|
1353
|
+
toCompressedJson() {
|
|
1354
|
+
return {
|
|
1355
|
+
nodes: this.nodes.map((node) => node.toJson()),
|
|
1356
|
+
edges: this.edges.map((edge) => {
|
|
1357
|
+
const node1Idx = this.nodes.indexOf(edge.node1);
|
|
1358
|
+
const node2Idx = this.nodes.indexOf(edge.node2);
|
|
1359
|
+
if (edge.isOneway) {
|
|
1360
|
+
return [node1Idx, node2Idx, edge.level, true];
|
|
1361
|
+
}
|
|
1362
|
+
if (edge.level) {
|
|
1363
|
+
return [node1Idx, node2Idx, edge.level];
|
|
1364
|
+
}
|
|
1365
|
+
return [node1Idx, node2Idx];
|
|
1366
|
+
})
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
static fromCompressedJson(json) {
|
|
1370
|
+
const network = new Network();
|
|
1371
|
+
network.nodes = json.nodes.map((node) => GraphNode.fromJson(node, null));
|
|
1372
|
+
network.edges = json.edges.map((jsonEdge) => {
|
|
1373
|
+
const edge = new GraphEdge(
|
|
1374
|
+
network.nodes[jsonEdge[0]],
|
|
1375
|
+
network.nodes[jsonEdge[1]],
|
|
1376
|
+
jsonEdge[2],
|
|
1377
|
+
null
|
|
1378
|
+
);
|
|
1379
|
+
if (jsonEdge.length > 3 && jsonEdge[3]) {
|
|
1380
|
+
edge.isOneway = true;
|
|
1381
|
+
}
|
|
1382
|
+
return edge;
|
|
1383
|
+
});
|
|
1384
|
+
return network;
|
|
1385
|
+
}
|
|
1386
|
+
static fromCoordinates(segments) {
|
|
1387
|
+
const network = new Network();
|
|
1388
|
+
const getOrCreateNode = (coords) => {
|
|
1389
|
+
const node = network.nodes.find((otherNode) => otherNode.coords.equals(coords));
|
|
1390
|
+
if (node) {
|
|
1391
|
+
return node;
|
|
1392
|
+
}
|
|
1393
|
+
const newNode = new GraphNode(coords, null);
|
|
1394
|
+
network.nodes.push(newNode);
|
|
1395
|
+
return newNode;
|
|
1396
|
+
};
|
|
1397
|
+
const createEdgeFromNodes = (node1, node2) => new GraphEdge(
|
|
1398
|
+
node1,
|
|
1399
|
+
node2,
|
|
1400
|
+
Level.union(node1.coords.level, node2.coords.level),
|
|
1401
|
+
null
|
|
1402
|
+
);
|
|
1403
|
+
for (const segment of segments) {
|
|
1404
|
+
let previousNode = null;
|
|
1405
|
+
for (const coords of segment) {
|
|
1406
|
+
const currentNode = getOrCreateNode(coords);
|
|
1407
|
+
if (previousNode) {
|
|
1408
|
+
const edge = createEdgeFromNodes(currentNode, previousNode);
|
|
1409
|
+
network.edges.push(edge);
|
|
1410
|
+
}
|
|
1411
|
+
previousNode = currentNode;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return network;
|
|
1415
|
+
}
|
|
1416
|
+
getEdgesAtLevel(targetLevel, useMultiLevelEdges = true) {
|
|
1417
|
+
return this.edges.filter(
|
|
1418
|
+
({ level }) => useMultiLevelEdges ? Level.intersect(targetLevel, level) : Level.contains(targetLevel, level)
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
class GraphProjection {
|
|
1423
|
+
constructor(origin, distanceFromNearestElement, coords, nearestElement) {
|
|
1424
|
+
__publicField(this, "origin");
|
|
1425
|
+
__publicField(this, "distanceFromNearestElement");
|
|
1426
|
+
__publicField(this, "coords");
|
|
1427
|
+
__publicField(this, "nearestElement");
|
|
1428
|
+
this.origin = origin;
|
|
1429
|
+
this.distanceFromNearestElement = distanceFromNearestElement;
|
|
1430
|
+
this.coords = coords;
|
|
1431
|
+
this.nearestElement = nearestElement;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
const _MapMatching = class {
|
|
1435
|
+
constructor(network = null) {
|
|
1436
|
+
__publicField(this, "network", null);
|
|
1437
|
+
__publicField(this, "_maxDistance", Number.MAX_VALUE);
|
|
1438
|
+
__publicField(this, "_maxAngleBearing", Math.PI);
|
|
1439
|
+
this.network = network;
|
|
1440
|
+
}
|
|
1441
|
+
set maxAngleBearing(maxAngleBearing) {
|
|
1442
|
+
this._maxAngleBearing = maxAngleBearing;
|
|
1443
|
+
}
|
|
1444
|
+
get maxAngleBearing() {
|
|
1445
|
+
return this._maxAngleBearing;
|
|
1446
|
+
}
|
|
1447
|
+
set maxDistance(maxDistance) {
|
|
1448
|
+
this._maxDistance = maxDistance;
|
|
1449
|
+
}
|
|
1450
|
+
get maxDistance() {
|
|
1451
|
+
return this._maxDistance;
|
|
1452
|
+
}
|
|
1453
|
+
_shouldProjectOnEdgeAndNodes(edge, location, useBearing, useMultiLevelSegments, acceptEdgeFn) {
|
|
1454
|
+
if (!acceptEdgeFn(edge)) {
|
|
1455
|
+
return [false, false, false];
|
|
1456
|
+
}
|
|
1457
|
+
let checkEdge = Level.intersect(location.level, edge.level);
|
|
1458
|
+
let checkNode1 = Level.intersect(location.level, edge.node1.coords.level);
|
|
1459
|
+
let checkNode2 = Level.intersect(location.level, edge.node2.coords.level);
|
|
1460
|
+
checkNode1 = checkNode1 || edge.node1.io && location.level === null;
|
|
1461
|
+
checkNode2 = checkNode2 || edge.node2.io && location.level === null;
|
|
1462
|
+
if (!useMultiLevelSegments) {
|
|
1463
|
+
checkEdge = checkEdge && !Level.isRange(edge.level);
|
|
1464
|
+
checkNode1 = checkNode1 && !Level.isRange(edge.node1.coords.level);
|
|
1465
|
+
checkNode2 = checkNode2 && !Level.isRange(edge.node2.coords.level);
|
|
1466
|
+
}
|
|
1467
|
+
if (useBearing) {
|
|
1468
|
+
if (checkEdge) {
|
|
1469
|
+
const diffAngle = maths.diffAngleLines(edge.bearing, location.bearing);
|
|
1470
|
+
if (diffAngle > this._maxAngleBearing) {
|
|
1471
|
+
checkEdge = false;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
checkNode1 = false;
|
|
1475
|
+
checkNode2 = false;
|
|
1476
|
+
}
|
|
1477
|
+
return [checkEdge, checkNode1, checkNode2];
|
|
1478
|
+
}
|
|
1479
|
+
static _assignLatLngLevel(fromCoordinates, toCoordinates) {
|
|
1480
|
+
toCoordinates.lat = fromCoordinates.lat;
|
|
1481
|
+
toCoordinates.lng = fromCoordinates.lng;
|
|
1482
|
+
toCoordinates.level = Level.clone(fromCoordinates.level);
|
|
1483
|
+
}
|
|
1484
|
+
static _handleLevelsWithIONodes(projection, location, projectionNode) {
|
|
1485
|
+
if (location.level === null && projectionNode.io) {
|
|
1486
|
+
projection.level = null;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
getProjection(location, useDistance = false, useBearing = false, useMultiLevelSegments = true, acceptEdgeFn = () => true) {
|
|
1490
|
+
if (this.network === null) {
|
|
1491
|
+
throw new Error("Network has not been set yet");
|
|
1492
|
+
}
|
|
1493
|
+
if (!(location instanceof Coordinates)) {
|
|
1494
|
+
throw new TypeError("location is not an instance of Coordinates");
|
|
1495
|
+
}
|
|
1496
|
+
if (useBearing && (!("bearing" in location && location.bearing !== null) || !this._maxAngleBearing)) {
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
let distanceFromNearestElement = Number.MAX_VALUE;
|
|
1500
|
+
const projection = location.clone();
|
|
1501
|
+
let nearestElement = null;
|
|
1502
|
+
const isProjectionBetter = (distanceOfNewProjection) => {
|
|
1503
|
+
return distanceOfNewProjection < distanceFromNearestElement && (!useDistance || distanceOfNewProjection <= this._maxDistance);
|
|
1504
|
+
};
|
|
1505
|
+
for (const edge of this.network.edges) {
|
|
1506
|
+
const [checkEdge, checkNode1, checkNode2] = this._shouldProjectOnEdgeAndNodes(
|
|
1507
|
+
edge,
|
|
1508
|
+
location,
|
|
1509
|
+
useBearing,
|
|
1510
|
+
useMultiLevelSegments,
|
|
1511
|
+
acceptEdgeFn
|
|
1512
|
+
);
|
|
1513
|
+
if (checkNode1) {
|
|
1514
|
+
const distNode1 = location.distanceTo(edge.node1.coords);
|
|
1515
|
+
if (isProjectionBetter(distNode1) || distNode1 <= EPS_MM) {
|
|
1516
|
+
distanceFromNearestElement = distNode1;
|
|
1517
|
+
nearestElement = edge.node1;
|
|
1518
|
+
_MapMatching._assignLatLngLevel(edge.node1.coords, projection);
|
|
1519
|
+
_MapMatching._handleLevelsWithIONodes(projection, location, edge.node1);
|
|
1520
|
+
if (distNode1 <= EPS_MM) {
|
|
1521
|
+
break;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if (checkNode2) {
|
|
1526
|
+
const distNode2 = location.distanceTo(edge.node2.coords);
|
|
1527
|
+
if (isProjectionBetter(distNode2) || distNode2 <= EPS_MM) {
|
|
1528
|
+
distanceFromNearestElement = distNode2;
|
|
1529
|
+
nearestElement = edge.node2;
|
|
1530
|
+
_MapMatching._assignLatLngLevel(edge.node2.coords, projection);
|
|
1531
|
+
_MapMatching._handleLevelsWithIONodes(projection, location, edge.node2);
|
|
1532
|
+
if (distNode2 <= EPS_MM) {
|
|
1533
|
+
break;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (checkEdge) {
|
|
1538
|
+
const segmentProjection = location.getSegmentProjection(edge.node1.coords, edge.node2.coords);
|
|
1539
|
+
if (segmentProjection) {
|
|
1540
|
+
const distEdge = location.distanceTo(segmentProjection);
|
|
1541
|
+
if (isProjectionBetter(distEdge)) {
|
|
1542
|
+
distanceFromNearestElement = distEdge;
|
|
1543
|
+
nearestElement = edge;
|
|
1544
|
+
_MapMatching._assignLatLngLevel(segmentProjection, projection);
|
|
1545
|
+
_MapMatching._updateProjectionLevelFromEdge(edge, projection);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (!nearestElement) {
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
if (projection instanceof UserPosition && projection.accuracy !== null) {
|
|
1554
|
+
projection.accuracy += distanceFromNearestElement;
|
|
1555
|
+
}
|
|
1556
|
+
return new GraphProjection(
|
|
1557
|
+
location,
|
|
1558
|
+
distanceFromNearestElement,
|
|
1559
|
+
projection,
|
|
1560
|
+
nearestElement
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
let MapMatching = _MapMatching;
|
|
1565
|
+
__publicField(MapMatching, "_updateProjectionLevelFromEdge", (_edge, _projection) => {
|
|
1566
|
+
_projection.level = Level.clone(_edge.level);
|
|
1567
|
+
});
|
|
1568
|
+
class GraphItinerary {
|
|
1569
|
+
constructor(start, end, nodes, edges, edgesWeights) {
|
|
1570
|
+
__publicField(this, "start");
|
|
1571
|
+
__publicField(this, "end");
|
|
1572
|
+
__publicField(this, "nodes");
|
|
1573
|
+
__publicField(this, "edges");
|
|
1574
|
+
__publicField(this, "edgesWeights");
|
|
1575
|
+
this.start = start;
|
|
1576
|
+
this.end = end;
|
|
1577
|
+
this.nodes = nodes;
|
|
1578
|
+
this.edges = edges;
|
|
1579
|
+
this.edgesWeights = edgesWeights;
|
|
1580
|
+
}
|
|
1581
|
+
static fromNetworkNodes(start, end, networkNodes, edgesWeights) {
|
|
1582
|
+
const nodes = networkNodes.map((node) => {
|
|
1583
|
+
const newNode = node.clone();
|
|
1584
|
+
newNode.edges = [];
|
|
1585
|
+
if (newNode.io) {
|
|
1586
|
+
newNode.coords = newNode.coords.clone();
|
|
1587
|
+
newNode.coords.level = null;
|
|
1588
|
+
}
|
|
1589
|
+
return newNode;
|
|
1590
|
+
});
|
|
1591
|
+
const edges = [];
|
|
1592
|
+
networkNodes.forEach((node, idx, arr) => {
|
|
1593
|
+
if (idx === 0) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
const prevNode = arr[idx - 1];
|
|
1597
|
+
const edge = getEdgeByNodes(prevNode.edges, prevNode, node);
|
|
1598
|
+
if (!edge) {
|
|
1599
|
+
Logger__default.default.error("Cannot retrieve edge to create itinerary");
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
const newEdge = new GraphEdge(
|
|
1603
|
+
nodes[idx - 1],
|
|
1604
|
+
nodes[idx],
|
|
1605
|
+
edge.level,
|
|
1606
|
+
edge.builtFrom
|
|
1607
|
+
);
|
|
1608
|
+
newEdge.isOneway = edge.isOneway;
|
|
1609
|
+
edges.push(newEdge);
|
|
1610
|
+
});
|
|
1611
|
+
return new GraphItinerary(start, end, nodes, edges, edgesWeights);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
class NoRouteFoundError extends Error {
|
|
1615
|
+
constructor(start, end, details) {
|
|
1616
|
+
super();
|
|
1617
|
+
__publicField(this, "details");
|
|
1618
|
+
__publicField(this, "end");
|
|
1619
|
+
__publicField(this, "start");
|
|
1620
|
+
this.start = start;
|
|
1621
|
+
this.end = end;
|
|
1622
|
+
this.details = details;
|
|
1623
|
+
}
|
|
1624
|
+
get startStr() {
|
|
1625
|
+
if (this.start instanceof GraphNode) {
|
|
1626
|
+
return `GraphNode ${this.start.coords.toString()}`;
|
|
1627
|
+
}
|
|
1628
|
+
return this.start.toString();
|
|
1629
|
+
}
|
|
1630
|
+
get endStr() {
|
|
1631
|
+
if (this.end instanceof GraphNode) {
|
|
1632
|
+
return `GraphNode ${this.end.coords.toString()}`;
|
|
1633
|
+
}
|
|
1634
|
+
return this.end.toString();
|
|
1635
|
+
}
|
|
1636
|
+
get message() {
|
|
1637
|
+
let message = `No route found from ${this.startStr} to ${this.endStr}.`;
|
|
1638
|
+
if (this.details) {
|
|
1639
|
+
message += ` Details: ${this.details}`;
|
|
1640
|
+
}
|
|
1641
|
+
return message;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
class GraphRouterOptions {
|
|
1645
|
+
constructor() {
|
|
1646
|
+
__publicField(this, "projectionMaxDistance", 50);
|
|
1647
|
+
__publicField(this, "weightEdgeFn", (edge) => edge.length);
|
|
1648
|
+
__publicField(this, "acceptEdgeFn", () => true);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
class GraphRouter {
|
|
1652
|
+
constructor(network) {
|
|
1653
|
+
__publicField(this, "_mapMatching");
|
|
1654
|
+
__publicField(this, "_network");
|
|
1655
|
+
__publicField(this, "disabledEdges", /* @__PURE__ */ new Set());
|
|
1656
|
+
this._network = network;
|
|
1657
|
+
this._mapMatching = new MapMatching(network);
|
|
1658
|
+
}
|
|
1659
|
+
getShortestPath(start, end, options = new GraphRouterOptions()) {
|
|
1660
|
+
if (!(start instanceof GraphNode) && !(start instanceof Coordinates)) {
|
|
1661
|
+
throw new Error("Unknown start type");
|
|
1662
|
+
}
|
|
1663
|
+
if (!(end instanceof GraphNode) && !(end instanceof Coordinates)) {
|
|
1664
|
+
throw new Error("Unknown end type");
|
|
1665
|
+
}
|
|
1666
|
+
const { acceptEdgeFn, weightEdgeFn, projectionMaxDistance } = options;
|
|
1667
|
+
this._mapMatching.maxDistance = projectionMaxDistance;
|
|
1668
|
+
const createdNodes = [];
|
|
1669
|
+
const retrieveOrCreateNearestNode = (point) => {
|
|
1670
|
+
if (point instanceof GraphNode) {
|
|
1671
|
+
return point;
|
|
1672
|
+
}
|
|
1673
|
+
const closeNode = this._network.getNodeByCoords(point);
|
|
1674
|
+
if (closeNode) {
|
|
1675
|
+
return closeNode;
|
|
1676
|
+
}
|
|
1677
|
+
const proj = this._mapMatching.getProjection(point, true, false, false, acceptEdgeFn);
|
|
1678
|
+
if (!proj) {
|
|
1679
|
+
let message = `Point ${point.toString()} is too far from the network > ${this._mapMatching.maxDistance.toFixed(0)} meters.`;
|
|
1680
|
+
if (point.level !== null) {
|
|
1681
|
+
message += ` If it is a multi-level map, please verify if you have a network at level ${Level.toString(point.level)}.`;
|
|
1682
|
+
}
|
|
1683
|
+
throw new NoRouteFoundError(start, end, message);
|
|
1684
|
+
}
|
|
1685
|
+
if (proj.nearestElement instanceof GraphNode) {
|
|
1686
|
+
return proj.nearestElement;
|
|
1687
|
+
}
|
|
1688
|
+
const nodeCreated = this.createNodeInsideEdge(
|
|
1689
|
+
proj.nearestElement,
|
|
1690
|
+
proj.coords
|
|
1691
|
+
);
|
|
1692
|
+
createdNodes.push(nodeCreated);
|
|
1693
|
+
return nodeCreated;
|
|
1694
|
+
};
|
|
1695
|
+
const removeCreatedNodes = () => {
|
|
1696
|
+
while (createdNodes.length) {
|
|
1697
|
+
this.removeNodeFromPreviouslyCreatedEdge(createdNodes.pop());
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
const startNode = retrieveOrCreateNearestNode(start);
|
|
1701
|
+
const endNode = retrieveOrCreateNearestNode(end);
|
|
1702
|
+
const startCoords = start instanceof GraphNode ? start.coords : start;
|
|
1703
|
+
const endCoords = end instanceof GraphNode ? end.coords : end;
|
|
1704
|
+
let graphItinerary;
|
|
1705
|
+
if (startNode === endNode) {
|
|
1706
|
+
graphItinerary = GraphItinerary.fromNetworkNodes(
|
|
1707
|
+
startCoords,
|
|
1708
|
+
endCoords,
|
|
1709
|
+
[startNode],
|
|
1710
|
+
[]
|
|
1711
|
+
);
|
|
1712
|
+
} else {
|
|
1713
|
+
graphItinerary = this.getShortestPathBetweenGraphNodes(
|
|
1714
|
+
startNode,
|
|
1715
|
+
endNode,
|
|
1716
|
+
acceptEdgeFn,
|
|
1717
|
+
weightEdgeFn
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
graphItinerary.start = startCoords;
|
|
1721
|
+
graphItinerary.end = endCoords;
|
|
1722
|
+
removeCreatedNodes();
|
|
1723
|
+
if (!graphItinerary.nodes.length) {
|
|
1724
|
+
throw new NoRouteFoundError(start, end);
|
|
1725
|
+
}
|
|
1726
|
+
return graphItinerary;
|
|
1727
|
+
}
|
|
1728
|
+
createNodeInsideEdge(edge, point) {
|
|
1729
|
+
const a = edge.node1;
|
|
1730
|
+
const b = edge.node2;
|
|
1731
|
+
const m = new GraphNode(point, null);
|
|
1732
|
+
m.coords.level = edge.level;
|
|
1733
|
+
const u = edge.clone();
|
|
1734
|
+
u.node1 = a;
|
|
1735
|
+
u.node2 = m;
|
|
1736
|
+
const v = edge.clone();
|
|
1737
|
+
v.node1 = m;
|
|
1738
|
+
v.node2 = b;
|
|
1739
|
+
a.edges = a.edges.filter((_edge) => _edge !== edge);
|
|
1740
|
+
b.edges = b.edges.filter((_edge) => _edge !== edge);
|
|
1741
|
+
this._network.nodes.push(m);
|
|
1742
|
+
this._network.edges.push(u, v);
|
|
1743
|
+
this._network.edges = this._network.edges.filter(
|
|
1744
|
+
(_edge) => _edge !== edge
|
|
1745
|
+
);
|
|
1746
|
+
return m;
|
|
1747
|
+
}
|
|
1748
|
+
removeNodeFromPreviouslyCreatedEdge(_node) {
|
|
1749
|
+
const u = _node.edges[0];
|
|
1750
|
+
const v = _node.edges[1];
|
|
1751
|
+
u.node1.edges = u.node1.edges.filter((edge) => edge !== u);
|
|
1752
|
+
v.node1.edges = v.node1.edges.filter((edge) => edge !== v);
|
|
1753
|
+
const oldEdge = u.clone();
|
|
1754
|
+
oldEdge.node1 = u.node1;
|
|
1755
|
+
oldEdge.node2 = v.node2;
|
|
1756
|
+
this._network.edges.push(oldEdge);
|
|
1757
|
+
this._network.nodes = this._network.nodes.filter((node) => node !== _node);
|
|
1758
|
+
this._network.edges = this._network.edges.filter(
|
|
1759
|
+
(edge) => edge !== u && edge !== v
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
getShortestPathBetweenGraphNodes(start, end, acceptEdgeFn, weightFn) {
|
|
1763
|
+
const distanceMap = {}, checking = {}, vertexList = {}, vertexNodes = {}, parentVertices = {}, path = [];
|
|
1764
|
+
let vertexId = 1;
|
|
1765
|
+
this._network.nodes.forEach((vertex) => {
|
|
1766
|
+
vertex.uniqueRouterId = vertexId;
|
|
1767
|
+
vertexNodes[vertexId] = vertex;
|
|
1768
|
+
distanceMap[vertexId] = Infinity;
|
|
1769
|
+
checking[vertexId] = null;
|
|
1770
|
+
vertexList[vertexId] = true;
|
|
1771
|
+
vertexId++;
|
|
1772
|
+
});
|
|
1773
|
+
const startVertex = Object.values(vertexNodes).find((vertex) => start.equals(vertex));
|
|
1774
|
+
const endVertex = Object.values(vertexNodes).find((vertex) => end.equals(vertex));
|
|
1775
|
+
distanceMap[startVertex.uniqueRouterId] = 0;
|
|
1776
|
+
while (Object.keys(vertexList).length > 0) {
|
|
1777
|
+
const keys = Object.keys(vertexList).map(Number);
|
|
1778
|
+
const current = Number(
|
|
1779
|
+
keys.reduce((_checking, vertexId2) => {
|
|
1780
|
+
return distanceMap[_checking] > distanceMap[vertexId2] ? vertexId2 : _checking;
|
|
1781
|
+
}, keys[0])
|
|
1782
|
+
);
|
|
1783
|
+
this._network.edges.filter((edge) => {
|
|
1784
|
+
if (!acceptEdgeFn(edge) || this.disabledEdges.has(edge)) {
|
|
1785
|
+
return false;
|
|
1786
|
+
}
|
|
1787
|
+
const from = edge.node1.uniqueRouterId, to = edge.node2.uniqueRouterId;
|
|
1788
|
+
return from === current || to === current;
|
|
1789
|
+
}).forEach((edge) => {
|
|
1790
|
+
let to, from, reversed = false;
|
|
1791
|
+
if (edge.node1.uniqueRouterId === current) {
|
|
1792
|
+
to = edge.node2.uniqueRouterId;
|
|
1793
|
+
from = edge.node1.uniqueRouterId;
|
|
1794
|
+
} else {
|
|
1795
|
+
to = edge.node1.uniqueRouterId;
|
|
1796
|
+
from = edge.node2.uniqueRouterId;
|
|
1797
|
+
reversed = true;
|
|
1798
|
+
}
|
|
1799
|
+
if (edge.isOneway && reversed) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
const distance = distanceMap[current] + weightFn(edge);
|
|
1803
|
+
if (distanceMap[to] > distance) {
|
|
1804
|
+
distanceMap[to] = distance;
|
|
1805
|
+
checking[to] = current;
|
|
1806
|
+
parentVertices[to] = from;
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
1809
|
+
delete vertexList[current];
|
|
1810
|
+
}
|
|
1811
|
+
const edgesWeights = [];
|
|
1812
|
+
let endId = endVertex.uniqueRouterId;
|
|
1813
|
+
while (parentVertices[endId]) {
|
|
1814
|
+
path.unshift(vertexNodes[endId]);
|
|
1815
|
+
edgesWeights.unshift(distanceMap[endId] - distanceMap[parentVertices[endId]]);
|
|
1816
|
+
endId = parentVertices[endId];
|
|
1817
|
+
}
|
|
1818
|
+
if (path.length !== 0) {
|
|
1819
|
+
path.unshift(start);
|
|
1820
|
+
}
|
|
1821
|
+
this._network.nodes.forEach((vertex) => {
|
|
1822
|
+
delete vertex.uniqueRouterId;
|
|
1823
|
+
});
|
|
1824
|
+
return GraphItinerary.fromNetworkNodes(start.coords, end.coords, path, edgesWeights);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
exports.AbsoluteHeading = AbsoluteHeading;
|
|
1828
|
+
exports.Attitude = Attitude;
|
|
1829
|
+
exports.BoundingBox = BoundingBox;
|
|
1830
|
+
exports.Constants = Constants;
|
|
1831
|
+
exports.Coordinates = Coordinates;
|
|
1832
|
+
exports.GeoRef = GeoRef;
|
|
1833
|
+
exports.GeoRelativePosition = GeoRelativePosition;
|
|
1834
|
+
exports.GraphEdge = GraphEdge;
|
|
1835
|
+
exports.GraphItinerary = GraphItinerary;
|
|
1836
|
+
exports.GraphNode = GraphNode;
|
|
1837
|
+
exports.GraphProjection = GraphProjection;
|
|
1838
|
+
exports.GraphRouter = GraphRouter;
|
|
1839
|
+
exports.GraphRouterOptions = GraphRouterOptions;
|
|
1840
|
+
exports.GraphUtils = GraphUtils;
|
|
1841
|
+
exports.Level = Level;
|
|
1842
|
+
exports.MapMatching = MapMatching;
|
|
1843
|
+
exports.Network = Network;
|
|
1844
|
+
exports.NoRouteFoundError = NoRouteFoundError;
|
|
1845
|
+
exports.RelativePosition = RelativePosition;
|
|
1846
|
+
exports.UserPosition = UserPosition;
|
|
1847
|
+
exports.Utils = Utils;
|
|
1848
|
+
//# sourceMappingURL=index.js.map
|