@wemap/geo 13.2.3 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.d.ts +14 -13
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1049 -2
- package/dist/src/Constants.d.ts +1 -0
- package/dist/src/Constants.d.ts.map +1 -0
- package/dist/src/Utils.d.ts +4 -3
- package/dist/src/Utils.d.ts.map +1 -0
- package/dist/src/coordinates/BoundingBox.d.ts +2 -1
- package/dist/src/coordinates/BoundingBox.d.ts.map +1 -0
- package/dist/src/coordinates/Coordinates.d.ts +3 -2
- package/dist/src/coordinates/Coordinates.d.ts.map +1 -0
- package/dist/src/coordinates/GeoRef.d.ts +6 -5
- package/dist/src/coordinates/GeoRef.d.ts.map +1 -0
- package/dist/src/coordinates/GeoRelativePosition.d.ts +2 -1
- package/dist/src/coordinates/GeoRelativePosition.d.ts.map +1 -0
- package/dist/src/coordinates/Level.d.ts +1 -0
- package/dist/src/coordinates/Level.d.ts.map +1 -0
- package/dist/src/coordinates/RelativePosition.d.ts +2 -1
- package/dist/src/coordinates/RelativePosition.d.ts.map +1 -0
- package/dist/src/coordinates/UserPosition.d.ts +3 -2
- package/dist/src/coordinates/UserPosition.d.ts.map +1 -0
- package/dist/src/rotations/AbsoluteHeading.d.ts +3 -2
- package/dist/src/rotations/AbsoluteHeading.d.ts.map +1 -0
- package/dist/src/rotations/Attitude.d.ts +2 -1
- package/dist/src/rotations/Attitude.d.ts.map +1 -0
- package/dist/src/types.d.ts +2 -1
- package/dist/src/types.d.ts.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/package.json +28 -53
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1348
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
DELETED
|
@@ -1,1348 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
-
var __publicField = (obj, key, value) => {
|
|
4
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
-
return value;
|
|
6
|
-
};
|
|
7
|
-
import { wrap, deg2rad, rad2deg, Quaternion, Vector3, positiveMod, Rotations } from "@wemap/maths";
|
|
8
|
-
import booleanPointInPolygon from "@turf/boolean-point-in-polygon";
|
|
9
|
-
const R_MAJOR = 6378137;
|
|
10
|
-
const R_MINOR = 63567523142e-4;
|
|
11
|
-
const EARTH_GRAVITY = 9.80665;
|
|
12
|
-
const EPS_DEG_MM = 1e-8;
|
|
13
|
-
const EPS_MM = 1e-3;
|
|
14
|
-
const ELLIPSOID_FLATNESS = (R_MAJOR - R_MINOR) / R_MAJOR;
|
|
15
|
-
const ECCENTRICITY = Math.sqrt(ELLIPSOID_FLATNESS * (2 - ELLIPSOID_FLATNESS));
|
|
16
|
-
const ECCENTRICITY_2 = ECCENTRICITY * ECCENTRICITY;
|
|
17
|
-
const R_MAJOR_2 = R_MAJOR * R_MAJOR;
|
|
18
|
-
const R_MAJOR_4 = R_MAJOR_2 * R_MAJOR_2;
|
|
19
|
-
const R_MINOR_2 = R_MINOR * R_MINOR;
|
|
20
|
-
const R_MINOR_4 = R_MINOR_2 * R_MINOR_2;
|
|
21
|
-
const CIRCUMFERENCE = R_MAJOR * 2 * Math.PI;
|
|
22
|
-
const Constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23
|
-
__proto__: null,
|
|
24
|
-
CIRCUMFERENCE,
|
|
25
|
-
EARTH_GRAVITY,
|
|
26
|
-
ECCENTRICITY,
|
|
27
|
-
ECCENTRICITY_2,
|
|
28
|
-
ELLIPSOID_FLATNESS,
|
|
29
|
-
EPS_DEG_MM,
|
|
30
|
-
EPS_MM,
|
|
31
|
-
R_MAJOR,
|
|
32
|
-
R_MAJOR_2,
|
|
33
|
-
R_MAJOR_4,
|
|
34
|
-
R_MINOR,
|
|
35
|
-
R_MINOR_2,
|
|
36
|
-
R_MINOR_4
|
|
37
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
38
|
-
const _Level = class _Level {
|
|
39
|
-
static checkType(level) {
|
|
40
|
-
if (level === null) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (typeof level === "number" && !isNaN(level)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (Array.isArray(level) && level.length === 2) {
|
|
47
|
-
const [low, up] = level;
|
|
48
|
-
if (typeof low === "number" && !isNaN(low) && typeof up === "number" && !isNaN(up)) {
|
|
49
|
-
if (low > up || low === up) {
|
|
50
|
-
throw Error(`Invalid level range: [${low}, ${up}]`);
|
|
51
|
-
}
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
throw Error(`Unknown level format: ${level}`);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Return true if the level is a range, false otherwise
|
|
59
|
-
*/
|
|
60
|
-
static isRange(level) {
|
|
61
|
-
if (_Level.VERIFY_TYPING) {
|
|
62
|
-
_Level.checkType(level);
|
|
63
|
-
}
|
|
64
|
-
return Array.isArray(level);
|
|
65
|
-
}
|
|
66
|
-
static clone(level) {
|
|
67
|
-
if (_Level.VERIFY_TYPING) {
|
|
68
|
-
_Level.checkType(level);
|
|
69
|
-
}
|
|
70
|
-
if (level === null) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
if (typeof level === "number") {
|
|
74
|
-
return level;
|
|
75
|
-
}
|
|
76
|
-
return [level[0], level[1]];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Create a level from a string (eg. 1, -2, 1;2, -2;3, 2;-1, 0.5;1 ...)
|
|
80
|
-
*/
|
|
81
|
-
static fromString(str) {
|
|
82
|
-
if (str === null) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
if (typeof str !== "string" || !str.length) {
|
|
86
|
-
throw Error(`argument must be a non empty string, got ${typeof str}`);
|
|
87
|
-
}
|
|
88
|
-
if (!isNaN(Number(str))) {
|
|
89
|
-
return parseFloat(str);
|
|
90
|
-
}
|
|
91
|
-
const splited = str.split(";");
|
|
92
|
-
if (splited.length > 1) {
|
|
93
|
-
const levels = splited.map((str2) => Number(str2));
|
|
94
|
-
const low = Math.min(...levels);
|
|
95
|
-
const up = Math.max(...levels);
|
|
96
|
-
_Level.checkType([low, up]);
|
|
97
|
-
return [low, up];
|
|
98
|
-
} else {
|
|
99
|
-
const rangeSeparator = str.substring(1).indexOf("-") + 1;
|
|
100
|
-
if (rangeSeparator > 0) {
|
|
101
|
-
const low = Number(str.substring(0, rangeSeparator));
|
|
102
|
-
const up = Number(str.substring(rangeSeparator + 1));
|
|
103
|
-
_Level.checkType([low, up]);
|
|
104
|
-
return [low, up];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
throw Error(`Cannot parse following level: ${str}`);
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Returns if a level is contained in another
|
|
111
|
-
* @param {null|number|[number, number]} container The container level
|
|
112
|
-
* @param {null|number|[number, number]} targeted The targeted level
|
|
113
|
-
*/
|
|
114
|
-
static contains(container, targeted) {
|
|
115
|
-
if (_Level.VERIFY_TYPING) {
|
|
116
|
-
_Level.checkType(container);
|
|
117
|
-
_Level.checkType(targeted);
|
|
118
|
-
}
|
|
119
|
-
if (container === targeted) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
if (Array.isArray(container)) {
|
|
123
|
-
if (Array.isArray(targeted)) {
|
|
124
|
-
return container[0] <= targeted[0] && container[1] >= targeted[1];
|
|
125
|
-
}
|
|
126
|
-
if (targeted === null) {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
return container[0] <= targeted && container[1] >= targeted;
|
|
130
|
-
}
|
|
131
|
-
if (container === null || targeted === null) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
return container <= targeted[0] && container >= targeted[1];
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Retrieve the intersection of two levels
|
|
138
|
-
* null n null => null
|
|
139
|
-
* null n 1 => null // Conception choice
|
|
140
|
-
* null n [1,2] => null // Conception choice
|
|
141
|
-
* 1 n 1 => 1
|
|
142
|
-
* 1 n 2 => null
|
|
143
|
-
* 1 n [1,2] => 1
|
|
144
|
-
* 1 n [0,2] => 1
|
|
145
|
-
* 1 n [2,3] => null
|
|
146
|
-
* [1,2] n [1,2] => [1,2]
|
|
147
|
-
* [1,2] n [2,3] => 2
|
|
148
|
-
* [1,2] n [3,4] => null
|
|
149
|
-
*/
|
|
150
|
-
static intersection(first, second) {
|
|
151
|
-
if (_Level.VERIFY_TYPING) {
|
|
152
|
-
_Level.checkType(first);
|
|
153
|
-
_Level.checkType(second);
|
|
154
|
-
}
|
|
155
|
-
if (first === null || second === null) {
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
if (_Level.equals(first, second)) {
|
|
159
|
-
return _Level.clone(first);
|
|
160
|
-
}
|
|
161
|
-
if (typeof first === "number" && typeof second === "number") {
|
|
162
|
-
return first === second ? first : null;
|
|
163
|
-
}
|
|
164
|
-
if (Array.isArray(first) && !Array.isArray(second)) {
|
|
165
|
-
if (_Level.contains(first, second)) {
|
|
166
|
-
return second;
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
if (!Array.isArray(first) && Array.isArray(second)) {
|
|
171
|
-
if (_Level.contains(second, first)) {
|
|
172
|
-
return first;
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
const low = Math.max(first[0], second[0]);
|
|
177
|
-
const up = Math.min(first[1], second[1]);
|
|
178
|
-
if (up === low) {
|
|
179
|
-
return up;
|
|
180
|
-
}
|
|
181
|
-
return up < low ? null : [low, up];
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Retrieve the intersection of two levels
|
|
185
|
-
* @param {null|number|[number, number]} first The first level
|
|
186
|
-
* @param {null|number|[number, number]} second The second level
|
|
187
|
-
* @returns {boolean}
|
|
188
|
-
*/
|
|
189
|
-
static intersect(first, second) {
|
|
190
|
-
if (_Level.VERIFY_TYPING) {
|
|
191
|
-
_Level.checkType(first);
|
|
192
|
-
_Level.checkType(second);
|
|
193
|
-
}
|
|
194
|
-
if (first === null && second === null) {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
return _Level.intersection(first, second) !== null;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Retrieve the union of two levels
|
|
201
|
-
* null u null => null
|
|
202
|
-
* null u 1 => null // Conception choice
|
|
203
|
-
* null u [1,2] => null // Conception choice
|
|
204
|
-
* 1 u 1 => 1
|
|
205
|
-
* 1 u 2 => [1,2]
|
|
206
|
-
* 1 u [1,2] => [1,2]
|
|
207
|
-
* 1 u [0,2] => [0,2]
|
|
208
|
-
* 1 u [2,3] => [1,3]
|
|
209
|
-
* [1,2] u [1,2] => [1,2]
|
|
210
|
-
* [1,2] u [2,3] => [1,3]
|
|
211
|
-
* [1,2] u [3,4] => [1,4]
|
|
212
|
-
*/
|
|
213
|
-
static union(first, second) {
|
|
214
|
-
if (_Level.VERIFY_TYPING) {
|
|
215
|
-
_Level.checkType(first);
|
|
216
|
-
_Level.checkType(second);
|
|
217
|
-
}
|
|
218
|
-
if (first === second) {
|
|
219
|
-
return _Level.clone(first);
|
|
220
|
-
}
|
|
221
|
-
if (second === null) {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
if (first === null) {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
let low, up;
|
|
228
|
-
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
229
|
-
low = Math.min(first, second);
|
|
230
|
-
up = Math.max(first, second);
|
|
231
|
-
} else if (Array.isArray(first) && !Array.isArray(second)) {
|
|
232
|
-
low = Math.min(first[0], second);
|
|
233
|
-
up = Math.max(first[1], second);
|
|
234
|
-
} else if (!Array.isArray(first) && Array.isArray(second)) {
|
|
235
|
-
low = Math.min(second[0], first);
|
|
236
|
-
up = Math.max(second[1], first);
|
|
237
|
-
} else {
|
|
238
|
-
low = Math.min(first[0], second[0]);
|
|
239
|
-
up = Math.max(first[1], second[1]);
|
|
240
|
-
}
|
|
241
|
-
if (low === up) {
|
|
242
|
-
return low;
|
|
243
|
-
}
|
|
244
|
-
return [low, up];
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Multiply a level by a factor
|
|
248
|
-
* @param {null|number|[number, number]} level the level to multiply
|
|
249
|
-
* @param {number} factor
|
|
250
|
-
* @returns {null|number|[number, number]}
|
|
251
|
-
*/
|
|
252
|
-
static multiplyBy(level, factor) {
|
|
253
|
-
if (_Level.VERIFY_TYPING) {
|
|
254
|
-
_Level.checkType(level);
|
|
255
|
-
}
|
|
256
|
-
if (level === null) {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
return Array.isArray(level) ? [level[0] * factor, level[1] * factor] : level * factor;
|
|
260
|
-
}
|
|
261
|
-
static toString(level) {
|
|
262
|
-
if (_Level.VERIFY_TYPING) {
|
|
263
|
-
_Level.checkType(level);
|
|
264
|
-
}
|
|
265
|
-
if (level === null) {
|
|
266
|
-
return null;
|
|
267
|
-
}
|
|
268
|
-
return Array.isArray(level) ? level[0] + ";" + level[1] : String(level);
|
|
269
|
-
}
|
|
270
|
-
static equals(first, second) {
|
|
271
|
-
if (_Level.VERIFY_TYPING) {
|
|
272
|
-
_Level.checkType(first);
|
|
273
|
-
_Level.checkType(second);
|
|
274
|
-
}
|
|
275
|
-
if (first === second) {
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
if (Array.isArray(first) && Array.isArray(second)) {
|
|
279
|
-
return first[0] === second[0] && first[1] === second[1];
|
|
280
|
-
}
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
static diff(first, second) {
|
|
284
|
-
if (_Level.VERIFY_TYPING) {
|
|
285
|
-
_Level.checkType(first);
|
|
286
|
-
_Level.checkType(second);
|
|
287
|
-
}
|
|
288
|
-
if (first === null || second === null) {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
if (!Array.isArray(first) && !Array.isArray(second)) {
|
|
292
|
-
return second - first;
|
|
293
|
-
}
|
|
294
|
-
if (Array.isArray(first) && !Array.isArray(second)) {
|
|
295
|
-
if (first[0] === second) {
|
|
296
|
-
return second - first[1];
|
|
297
|
-
}
|
|
298
|
-
if (first[1] === second) {
|
|
299
|
-
return second - first[0];
|
|
300
|
-
}
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
if (Array.isArray(second) && !Array.isArray(first)) {
|
|
304
|
-
if (first === second[0]) {
|
|
305
|
-
return second[1] - first;
|
|
306
|
-
}
|
|
307
|
-
if (first === second[1]) {
|
|
308
|
-
return second[0] - first;
|
|
309
|
-
}
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
if (_Level.equals(first, second)) {
|
|
313
|
-
return 0;
|
|
314
|
-
}
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
__publicField(_Level, "VERIFY_TYPING", false);
|
|
319
|
-
let Level = _Level;
|
|
320
|
-
class Coordinates {
|
|
321
|
-
constructor(lat, lng, alt = null, level = null) {
|
|
322
|
-
__publicField(this, "_lat");
|
|
323
|
-
__publicField(this, "_lng");
|
|
324
|
-
__publicField(this, "_alt", null);
|
|
325
|
-
__publicField(this, "_level", null);
|
|
326
|
-
__publicField(this, "_heightFromFloor", null);
|
|
327
|
-
__publicField(this, "_heightFromGround", null);
|
|
328
|
-
__publicField(this, "_ecef");
|
|
329
|
-
__publicField(this, "autoWrap", true);
|
|
330
|
-
this.lat = lat;
|
|
331
|
-
this.lng = lng;
|
|
332
|
-
this.alt = alt;
|
|
333
|
-
this.level = level;
|
|
334
|
-
this._ecef = null;
|
|
335
|
-
}
|
|
336
|
-
get lat() {
|
|
337
|
-
return this._lat;
|
|
338
|
-
}
|
|
339
|
-
set lat(lat) {
|
|
340
|
-
if (Math.abs(lat) <= 90) {
|
|
341
|
-
this._lat = lat;
|
|
342
|
-
} else {
|
|
343
|
-
throw new Error(`lat argument is not in [-90; 90], value is ${lat}`);
|
|
344
|
-
}
|
|
345
|
-
this._ecef = null;
|
|
346
|
-
}
|
|
347
|
-
get latitude() {
|
|
348
|
-
return this._lat;
|
|
349
|
-
}
|
|
350
|
-
set latitude(_) {
|
|
351
|
-
throw new Error("Please use Coordinates#lat setter instead of Coordinate#latitude");
|
|
352
|
-
}
|
|
353
|
-
get lng() {
|
|
354
|
-
return this._lng;
|
|
355
|
-
}
|
|
356
|
-
set lng(lng) {
|
|
357
|
-
this._lng = lng;
|
|
358
|
-
if (this.autoWrap) {
|
|
359
|
-
this.wrap();
|
|
360
|
-
}
|
|
361
|
-
this._ecef = null;
|
|
362
|
-
}
|
|
363
|
-
get longitude() {
|
|
364
|
-
return this._lng;
|
|
365
|
-
}
|
|
366
|
-
set longitude(_) {
|
|
367
|
-
throw new Error("Please use Coordinates#lng setter instead of Coordinate#longitude");
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* alt does not denote the altitude of a point but its height from
|
|
371
|
-
* the "level" field (if defined) or from the ground
|
|
372
|
-
*/
|
|
373
|
-
get alt() {
|
|
374
|
-
return this._alt;
|
|
375
|
-
}
|
|
376
|
-
set alt(alt) {
|
|
377
|
-
this._alt = alt;
|
|
378
|
-
this._ecef = null;
|
|
379
|
-
}
|
|
380
|
-
get level() {
|
|
381
|
-
return this._level;
|
|
382
|
-
}
|
|
383
|
-
set level(level) {
|
|
384
|
-
Level.checkType(level);
|
|
385
|
-
this._level = level;
|
|
386
|
-
}
|
|
387
|
-
get heightFromFloor() {
|
|
388
|
-
return this._heightFromFloor;
|
|
389
|
-
}
|
|
390
|
-
set heightFromFloor(heightFromFloor) {
|
|
391
|
-
this._heightFromFloor = heightFromFloor;
|
|
392
|
-
}
|
|
393
|
-
get heightFromGround() {
|
|
394
|
-
return this._heightFromGround;
|
|
395
|
-
}
|
|
396
|
-
set heightFromGround(heightFromGround) {
|
|
397
|
-
this._heightFromGround = heightFromGround;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Deep clone coordinates
|
|
401
|
-
*/
|
|
402
|
-
clone() {
|
|
403
|
-
const output = new Coordinates(this.lat, this.lng, this.alt);
|
|
404
|
-
if (this.level !== null) {
|
|
405
|
-
output.level = Level.clone(this.level);
|
|
406
|
-
}
|
|
407
|
-
return output;
|
|
408
|
-
}
|
|
409
|
-
wrap() {
|
|
410
|
-
if (this._lng <= -180 || this._lng > 180) {
|
|
411
|
-
this._lng = wrap(this._lng, -180, 180);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
static equals(pos1, pos2, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
415
|
-
if (!this.equalsWithoutLevel(pos1, pos2, eps, epsAlt)) {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
if (pos1 === null || pos2 === null) {
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
return Level.equals(pos1.level, pos2.level);
|
|
422
|
-
}
|
|
423
|
-
equals(other) {
|
|
424
|
-
return Coordinates.equals(this, other);
|
|
425
|
-
}
|
|
426
|
-
static equalsWithoutLevel(pos1, pos2, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
427
|
-
if (pos1 === null && pos1 === pos2) {
|
|
428
|
-
return true;
|
|
429
|
-
}
|
|
430
|
-
if (!(pos1 instanceof Coordinates) || !(pos2 instanceof Coordinates)) {
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
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);
|
|
434
|
-
}
|
|
435
|
-
equalsWithoutLevel(other, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
436
|
-
return Coordinates.equalsWithoutLevel(this, other, eps, epsAlt);
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* @throws {Error} if elevation is defined and point altitude is not defined
|
|
440
|
-
*/
|
|
441
|
-
destinationPoint(distance, bearing, elevation = null) {
|
|
442
|
-
const newPoint = this.clone();
|
|
443
|
-
newPoint.move(distance, bearing, elevation);
|
|
444
|
-
return newPoint;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Source: http://www.movable-type.co.uk/scripts/latlong.html#destPoint
|
|
448
|
-
* @throws {Error} if elevation is defined and point altitude is not defined
|
|
449
|
-
*/
|
|
450
|
-
move(distance, bearing, elevation = null) {
|
|
451
|
-
const dR = distance / R_MAJOR;
|
|
452
|
-
const cosDr = Math.cos(dR);
|
|
453
|
-
const sinDr = Math.sin(dR);
|
|
454
|
-
const phi1 = deg2rad(this.lat);
|
|
455
|
-
const lambda1 = deg2rad(this.lng);
|
|
456
|
-
const phi2 = Math.asin(
|
|
457
|
-
Math.sin(phi1) * cosDr + Math.cos(phi1) * sinDr * Math.cos(bearing)
|
|
458
|
-
);
|
|
459
|
-
const lambda2 = lambda1 + Math.atan2(
|
|
460
|
-
Math.sin(bearing) * sinDr * Math.cos(phi1),
|
|
461
|
-
cosDr - Math.sin(phi1) * Math.sin(phi2)
|
|
462
|
-
);
|
|
463
|
-
this.lat = rad2deg(phi2);
|
|
464
|
-
this.lng = rad2deg(lambda2);
|
|
465
|
-
if (elevation !== null) {
|
|
466
|
-
if (this.alt === null) {
|
|
467
|
-
throw new Error("Point altitude is not defined");
|
|
468
|
-
}
|
|
469
|
-
this.alt += elevation;
|
|
470
|
-
}
|
|
471
|
-
return this;
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Returns a distance between two points in meters
|
|
475
|
-
*/
|
|
476
|
-
distanceTo(location2) {
|
|
477
|
-
const lat1 = this.lat;
|
|
478
|
-
const lng1 = this.lng;
|
|
479
|
-
const lat2 = location2.lat;
|
|
480
|
-
const lng2 = location2.lng;
|
|
481
|
-
const dlat = deg2rad(lat2 - lat1);
|
|
482
|
-
const dlng = deg2rad(lng2 - lng1);
|
|
483
|
-
const dlngsin = Math.sin(dlng / 2);
|
|
484
|
-
const dlatsin = Math.sin(dlat / 2);
|
|
485
|
-
const lat1rad = deg2rad(lat1);
|
|
486
|
-
const lat1cos = Math.cos(lat1rad);
|
|
487
|
-
const lat2rad = deg2rad(lat2);
|
|
488
|
-
const lat2cos = Math.cos(lat2rad);
|
|
489
|
-
const angle = dlatsin * dlatsin + lat1cos * lat2cos * dlngsin * dlngsin;
|
|
490
|
-
const tangy = Math.sqrt(angle);
|
|
491
|
-
const tangx = Math.sqrt(1 - angle);
|
|
492
|
-
const cosn = 2 * Math.atan2(tangy, tangx);
|
|
493
|
-
return R_MAJOR * cosn;
|
|
494
|
-
}
|
|
495
|
-
static distanceBetween(point1, point2) {
|
|
496
|
-
return point1.distanceTo(point2);
|
|
497
|
-
}
|
|
498
|
-
bearingTo(location2) {
|
|
499
|
-
const lat1 = deg2rad(this.lat);
|
|
500
|
-
const lat2 = deg2rad(location2.lat);
|
|
501
|
-
const diffLng = deg2rad(location2.lng - this.lng);
|
|
502
|
-
return Math.atan2(
|
|
503
|
-
Math.sin(diffLng) * Math.cos(lat2),
|
|
504
|
-
Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLng)
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
static bearingTo(point1, point2) {
|
|
508
|
-
return point1.bearingTo(point2);
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* ECEF Transformations
|
|
512
|
-
* Here we used a light version of ECEF considering earth
|
|
513
|
-
* as a sphere instead of an ellipse
|
|
514
|
-
*/
|
|
515
|
-
get enuToEcefRotation() {
|
|
516
|
-
const rot1 = Quaternion.fromAxisAngle([0, 0, 1], Math.PI / 2 + deg2rad(this.lng));
|
|
517
|
-
const rot2 = Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 - deg2rad(this.lat));
|
|
518
|
-
return Quaternion.multiply(rot1, rot2);
|
|
519
|
-
}
|
|
520
|
-
get ecefToEnuRotation() {
|
|
521
|
-
const rot1 = Quaternion.fromAxisAngle([1, 0, 0], deg2rad(this.lat) - Math.PI / 2);
|
|
522
|
-
const rot2 = Quaternion.fromAxisAngle([0, 0, 1], -deg2rad(this.lng) - Math.PI / 2);
|
|
523
|
-
return Quaternion.multiply(rot1, rot2);
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* https://gist.github.com/klucar/1536194
|
|
527
|
-
* Adapted for spherical formula
|
|
528
|
-
*/
|
|
529
|
-
get ecef() {
|
|
530
|
-
if (!this._ecef) {
|
|
531
|
-
const lat = deg2rad(this.lat);
|
|
532
|
-
const lng = deg2rad(this.lng);
|
|
533
|
-
const alt = this.alt || 0;
|
|
534
|
-
const x = (R_MAJOR + alt) * Math.cos(lat) * Math.cos(lng);
|
|
535
|
-
const y = (R_MAJOR + alt) * Math.cos(lat) * Math.sin(lng);
|
|
536
|
-
const z = (R_MAJOR + alt) * Math.sin(lat);
|
|
537
|
-
this._ecef = [x, y, z];
|
|
538
|
-
}
|
|
539
|
-
return this._ecef;
|
|
540
|
-
}
|
|
541
|
-
static fromECEF(ecef) {
|
|
542
|
-
const x = ecef[0];
|
|
543
|
-
const y = ecef[1];
|
|
544
|
-
const z = ecef[2];
|
|
545
|
-
const p = Math.sqrt(x ** 2 + y ** 2);
|
|
546
|
-
let lng = Math.atan2(y, x);
|
|
547
|
-
const lat = Math.atan2(z, p);
|
|
548
|
-
const alt = p / Math.cos(lat) - R_MAJOR;
|
|
549
|
-
lng = lng % (2 * Math.PI);
|
|
550
|
-
const newPoint = new Coordinates(rad2deg(lat), rad2deg(lng), alt);
|
|
551
|
-
newPoint._ecef = ecef;
|
|
552
|
-
return newPoint;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* https://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
|
|
556
|
-
* Adapted to ECEF
|
|
557
|
-
*/
|
|
558
|
-
getSegmentProjection(p1, p2) {
|
|
559
|
-
const a = Vector3.normalize(p1.ecef);
|
|
560
|
-
const b = Vector3.normalize(p2.ecef);
|
|
561
|
-
const c = Vector3.normalize(this.ecef);
|
|
562
|
-
const G = Vector3.cross(a, b);
|
|
563
|
-
if (Vector3.norm(G) === 0) {
|
|
564
|
-
return null;
|
|
565
|
-
}
|
|
566
|
-
const F = Vector3.cross(c, G);
|
|
567
|
-
const t = Vector3.normalize(Vector3.cross(G, F));
|
|
568
|
-
const posECEF = Vector3.multiplyScalar(t, R_MAJOR);
|
|
569
|
-
const poseCoordinates = Coordinates.fromECEF(posECEF);
|
|
570
|
-
let alt;
|
|
571
|
-
if (p1.alt !== null && p2.alt !== null) {
|
|
572
|
-
alt = (p1.alt + p2.alt) / 2;
|
|
573
|
-
}
|
|
574
|
-
const projection = new Coordinates(
|
|
575
|
-
poseCoordinates.lat,
|
|
576
|
-
poseCoordinates.lng,
|
|
577
|
-
alt,
|
|
578
|
-
Level.union(p1.level, p2.level)
|
|
579
|
-
);
|
|
580
|
-
if (Math.abs(p1.distanceTo(p2) - p1.distanceTo(projection) - p2.distanceTo(projection)) > EPS_MM) {
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
return projection;
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Input / Output
|
|
587
|
-
*/
|
|
588
|
-
toString() {
|
|
589
|
-
let str = "[" + this._lat.toFixed(7) + ", " + this._lng.toFixed(7);
|
|
590
|
-
if (this._alt !== null) {
|
|
591
|
-
str += ", " + this._alt.toFixed(2);
|
|
592
|
-
}
|
|
593
|
-
if (this._level !== null) {
|
|
594
|
-
str += ", [" + Level.toString(this._level) + "]";
|
|
595
|
-
}
|
|
596
|
-
str += "]";
|
|
597
|
-
return str;
|
|
598
|
-
}
|
|
599
|
-
toJson() {
|
|
600
|
-
return {
|
|
601
|
-
lat: Number(this.lat.toFixed(8)),
|
|
602
|
-
lng: Number(this.lng.toFixed(8)),
|
|
603
|
-
...this.alt !== null && { alt: Number(this.alt.toFixed(3)) },
|
|
604
|
-
...this.level !== null && { level: this.level }
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
static fromJson(json) {
|
|
608
|
-
return new Coordinates(json.lat, json.lng, json.alt, json.level);
|
|
609
|
-
}
|
|
610
|
-
toCompressedJson() {
|
|
611
|
-
if (this.level !== null) {
|
|
612
|
-
return [
|
|
613
|
-
Number(this.lat.toFixed(8)),
|
|
614
|
-
Number(this.lng.toFixed(8)),
|
|
615
|
-
this.level
|
|
616
|
-
];
|
|
617
|
-
}
|
|
618
|
-
return [Number(this.lat.toFixed(8)), Number(this.lng.toFixed(8))];
|
|
619
|
-
}
|
|
620
|
-
static fromCompressedJson(json) {
|
|
621
|
-
const coords = new Coordinates(json[0], json[1]);
|
|
622
|
-
if (json.length > 2) {
|
|
623
|
-
coords.level = json[2];
|
|
624
|
-
}
|
|
625
|
-
return coords;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
class UserPosition extends Coordinates {
|
|
629
|
-
constructor(lat, lng, alt = null, level = null, time = null, accuracy = null, bearing = null) {
|
|
630
|
-
super(lat, lng, alt, level);
|
|
631
|
-
__publicField(this, "_time", null);
|
|
632
|
-
__publicField(this, "_accuracy", null);
|
|
633
|
-
__publicField(this, "_bearing", null);
|
|
634
|
-
this.time = time;
|
|
635
|
-
this.accuracy = accuracy;
|
|
636
|
-
this.bearing = bearing;
|
|
637
|
-
}
|
|
638
|
-
get time() {
|
|
639
|
-
return this._time;
|
|
640
|
-
}
|
|
641
|
-
set time(time) {
|
|
642
|
-
this._time = time;
|
|
643
|
-
}
|
|
644
|
-
get accuracy() {
|
|
645
|
-
return this._accuracy;
|
|
646
|
-
}
|
|
647
|
-
set accuracy(accuracy) {
|
|
648
|
-
if (accuracy !== null && accuracy < 0) {
|
|
649
|
-
throw new Error("accuracy argument is not a positive number");
|
|
650
|
-
}
|
|
651
|
-
this._accuracy = accuracy;
|
|
652
|
-
}
|
|
653
|
-
get bearing() {
|
|
654
|
-
return this._bearing;
|
|
655
|
-
}
|
|
656
|
-
set bearing(bearing) {
|
|
657
|
-
this._bearing = bearing !== null ? bearing % (2 * Math.PI) : null;
|
|
658
|
-
}
|
|
659
|
-
move(distance, bearing, elevation = null) {
|
|
660
|
-
super.move(distance, bearing, elevation);
|
|
661
|
-
return this;
|
|
662
|
-
}
|
|
663
|
-
destinationPoint(distance, bearing, elevation = null) {
|
|
664
|
-
const newPoint = this.clone();
|
|
665
|
-
newPoint.move(distance, bearing, elevation);
|
|
666
|
-
return newPoint;
|
|
667
|
-
}
|
|
668
|
-
// Create a UserPosition with lat, lng, alt from Coordinates coordinates and
|
|
669
|
-
// other fields from another UserPosition
|
|
670
|
-
static fromCoordinates(coordinates) {
|
|
671
|
-
return new UserPosition(
|
|
672
|
-
coordinates.lat,
|
|
673
|
-
coordinates.lng,
|
|
674
|
-
coordinates.alt,
|
|
675
|
-
coordinates.level
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
clone() {
|
|
679
|
-
const cloned = UserPosition.fromCoordinates(super.clone());
|
|
680
|
-
cloned.time = this.time;
|
|
681
|
-
cloned.accuracy = this.accuracy;
|
|
682
|
-
cloned.bearing = this.bearing;
|
|
683
|
-
return cloned;
|
|
684
|
-
}
|
|
685
|
-
static equals(pos1, pos2, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
686
|
-
if (pos1 === null && pos1 === pos2) {
|
|
687
|
-
return true;
|
|
688
|
-
}
|
|
689
|
-
if (!(pos1 instanceof UserPosition) || !(pos2 instanceof UserPosition)) {
|
|
690
|
-
return false;
|
|
691
|
-
}
|
|
692
|
-
if (!super.equals(pos1, pos2, eps, epsAlt)) {
|
|
693
|
-
return false;
|
|
694
|
-
}
|
|
695
|
-
return pos1.time === pos2.time && pos1.accuracy === pos2.accuracy && pos1.bearing === pos2.bearing;
|
|
696
|
-
}
|
|
697
|
-
equals(other, eps = EPS_DEG_MM, epsAlt = EPS_MM) {
|
|
698
|
-
return UserPosition.equals(this, other, eps, epsAlt);
|
|
699
|
-
}
|
|
700
|
-
toJson() {
|
|
701
|
-
return {
|
|
702
|
-
...super.toJson(),
|
|
703
|
-
...this.time !== null && { time: this.time },
|
|
704
|
-
...this.accuracy !== null && { accuracy: this.accuracy },
|
|
705
|
-
...this.bearing !== null && { bearing: this.bearing }
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
static fromJson(json) {
|
|
709
|
-
const position = UserPosition.fromCoordinates(Coordinates.fromJson(json));
|
|
710
|
-
if (typeof json.time !== "undefined") {
|
|
711
|
-
position.time = json.time;
|
|
712
|
-
}
|
|
713
|
-
if (typeof json.accuracy !== "undefined") {
|
|
714
|
-
position.accuracy = json.accuracy;
|
|
715
|
-
}
|
|
716
|
-
if (typeof json.bearing !== "undefined") {
|
|
717
|
-
position.bearing = json.bearing;
|
|
718
|
-
}
|
|
719
|
-
return position;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
function sampleRoute(route, stepSize = 0.7, startSampling = 0, length = Number.MAX_VALUE) {
|
|
723
|
-
const endSampling = startSampling + length;
|
|
724
|
-
const sampledRoute = [];
|
|
725
|
-
let lastSample;
|
|
726
|
-
let totalDistanceTraveled = 0;
|
|
727
|
-
let distanceToNextSample = 0;
|
|
728
|
-
let startFound = false;
|
|
729
|
-
for (let segmentIndex = 0; segmentIndex < route.length - 1; segmentIndex++) {
|
|
730
|
-
const p1 = route[segmentIndex];
|
|
731
|
-
const p2 = route[segmentIndex + 1];
|
|
732
|
-
const segmentSize = p1.distanceTo(p2);
|
|
733
|
-
const segmentBearing = p1.bearingTo(p2);
|
|
734
|
-
let distanceTraveledOnSegment = 0;
|
|
735
|
-
if (!startFound) {
|
|
736
|
-
if (startSampling < totalDistanceTraveled + segmentSize) {
|
|
737
|
-
startFound = true;
|
|
738
|
-
distanceToNextSample = startSampling - totalDistanceTraveled;
|
|
739
|
-
} else {
|
|
740
|
-
totalDistanceTraveled += segmentSize;
|
|
741
|
-
continue;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
lastSample = Object.assign(p1.clone(), { bearing: segmentBearing });
|
|
745
|
-
while (distanceTraveledOnSegment + distanceToNextSample < segmentSize && totalDistanceTraveled + distanceToNextSample <= endSampling) {
|
|
746
|
-
const newPoint = lastSample.destinationPoint(distanceToNextSample, segmentBearing);
|
|
747
|
-
newPoint.bearing = segmentBearing;
|
|
748
|
-
sampledRoute.push(newPoint);
|
|
749
|
-
lastSample = newPoint;
|
|
750
|
-
distanceTraveledOnSegment += distanceToNextSample;
|
|
751
|
-
totalDistanceTraveled += distanceToNextSample;
|
|
752
|
-
distanceToNextSample = stepSize;
|
|
753
|
-
}
|
|
754
|
-
if (totalDistanceTraveled + distanceToNextSample > endSampling) {
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
const rest = segmentSize - distanceTraveledOnSegment;
|
|
758
|
-
totalDistanceTraveled += rest;
|
|
759
|
-
distanceToNextSample -= rest;
|
|
760
|
-
}
|
|
761
|
-
return sampledRoute;
|
|
762
|
-
}
|
|
763
|
-
function trimRoute(route, startPosition = route[0], length = Number.MAX_VALUE) {
|
|
764
|
-
const newRoute = [];
|
|
765
|
-
let previousPoint = null;
|
|
766
|
-
let currentPointIndex;
|
|
767
|
-
let cumulativeDistance = 0;
|
|
768
|
-
if (route.length <= 1) {
|
|
769
|
-
throw new Error("Route must have at least 2 points");
|
|
770
|
-
}
|
|
771
|
-
for (currentPointIndex = 1; currentPointIndex < route.length; currentPointIndex++) {
|
|
772
|
-
const p1 = route[currentPointIndex - 1];
|
|
773
|
-
const p2 = route[currentPointIndex];
|
|
774
|
-
if (Coordinates.equals(startPosition, p1)) {
|
|
775
|
-
newRoute.push(p1);
|
|
776
|
-
previousPoint = p1;
|
|
777
|
-
break;
|
|
778
|
-
}
|
|
779
|
-
const proj = startPosition.getSegmentProjection(p1, p2);
|
|
780
|
-
if (proj && Coordinates.equals(startPosition, proj) && !proj.equals(p2)) {
|
|
781
|
-
newRoute.push(proj);
|
|
782
|
-
previousPoint = proj;
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
if (!newRoute.length) {
|
|
787
|
-
throw new Error("startPosition is not on the route");
|
|
788
|
-
}
|
|
789
|
-
while (previousPoint && currentPointIndex < route.length) {
|
|
790
|
-
const currentPoint = route[currentPointIndex];
|
|
791
|
-
const dist = previousPoint.distanceTo(currentPoint);
|
|
792
|
-
if (cumulativeDistance + dist >= length || Math.abs(cumulativeDistance + dist - length) <= EPS_MM) {
|
|
793
|
-
const bearing = previousPoint.bearingTo(currentPoint);
|
|
794
|
-
const remainingLength = length - cumulativeDistance;
|
|
795
|
-
const end = previousPoint.destinationPoint(remainingLength, bearing);
|
|
796
|
-
newRoute.push(end);
|
|
797
|
-
break;
|
|
798
|
-
}
|
|
799
|
-
newRoute.push(currentPoint);
|
|
800
|
-
previousPoint = currentPoint;
|
|
801
|
-
cumulativeDistance += dist;
|
|
802
|
-
currentPointIndex++;
|
|
803
|
-
}
|
|
804
|
-
return newRoute;
|
|
805
|
-
}
|
|
806
|
-
function simplifyRoute(coords, precisionAngle = deg2rad(5)) {
|
|
807
|
-
const isClosed = coords[0].equals(coords[coords.length - 1]);
|
|
808
|
-
let newRoute = coords.slice(0, coords.length - (isClosed ? 1 : 0));
|
|
809
|
-
const len = newRoute.length;
|
|
810
|
-
for (let i = isClosed ? 0 : 1; i < len; i++) {
|
|
811
|
-
const p0 = coords[positiveMod(i - 1, len)];
|
|
812
|
-
const p1 = coords[i];
|
|
813
|
-
const p2 = coords[positiveMod(i + 1, len)];
|
|
814
|
-
const seg1Dir = p0.bearingTo(p1);
|
|
815
|
-
const seg2Dir = p1.bearingTo(p2);
|
|
816
|
-
if (Math.abs(seg2Dir - seg1Dir) < precisionAngle) {
|
|
817
|
-
newRoute = newRoute.filter((coord) => coord !== p1);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
if (isClosed) {
|
|
821
|
-
newRoute.push(newRoute[0]);
|
|
822
|
-
}
|
|
823
|
-
return newRoute;
|
|
824
|
-
}
|
|
825
|
-
function geolocationPositionToUserPosition(geolocationPosition) {
|
|
826
|
-
if (geolocationPosition === null) {
|
|
827
|
-
return null;
|
|
828
|
-
}
|
|
829
|
-
const { latitude, longitude, accuracy, heading } = geolocationPosition.coords;
|
|
830
|
-
const userPosition = new UserPosition(latitude, longitude);
|
|
831
|
-
userPosition.time = geolocationPosition.timestamp;
|
|
832
|
-
userPosition.accuracy = accuracy;
|
|
833
|
-
userPosition.bearing = heading ? deg2rad(heading) : null;
|
|
834
|
-
return userPosition;
|
|
835
|
-
}
|
|
836
|
-
function calcDistance(coords) {
|
|
837
|
-
return coords.reduce((acc, coords2, idx, arr) => acc + (idx ? arr[idx - 1].distanceTo(coords2) : 0), 0);
|
|
838
|
-
}
|
|
839
|
-
function createSegmentsAtLevel(itineraryCoords, segmentsLevel, useMultiLevelSegments = true) {
|
|
840
|
-
const itineraryCoordsLength = itineraryCoords.length;
|
|
841
|
-
let previousLevelCorrespond = false;
|
|
842
|
-
const segments = [];
|
|
843
|
-
let coordinates = [];
|
|
844
|
-
for (let i = 0; i < itineraryCoordsLength; i++) {
|
|
845
|
-
const coords = itineraryCoords[i];
|
|
846
|
-
const levelCorrespond = Level.intersect(segmentsLevel, coords.level);
|
|
847
|
-
if (useMultiLevelSegments && !levelCorrespond && previousLevelCorrespond) {
|
|
848
|
-
coordinates.push(coords);
|
|
849
|
-
} else if (levelCorrespond) {
|
|
850
|
-
if (!previousLevelCorrespond) {
|
|
851
|
-
coordinates = [];
|
|
852
|
-
segments.push(coordinates);
|
|
853
|
-
if (useMultiLevelSegments && i !== 0) {
|
|
854
|
-
coordinates.push(itineraryCoords[i - 1]);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
coordinates.push(coords);
|
|
858
|
-
}
|
|
859
|
-
previousLevelCorrespond = levelCorrespond;
|
|
860
|
-
}
|
|
861
|
-
return segments;
|
|
862
|
-
}
|
|
863
|
-
const Utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
864
|
-
__proto__: null,
|
|
865
|
-
calcDistance,
|
|
866
|
-
createSegmentsAtLevel,
|
|
867
|
-
geolocationPositionToUserPosition,
|
|
868
|
-
sampleRoute,
|
|
869
|
-
simplifyRoute,
|
|
870
|
-
trimRoute
|
|
871
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
872
|
-
class BoundingBox {
|
|
873
|
-
constructor(northEast, southWest) {
|
|
874
|
-
__publicField(this, "northEast");
|
|
875
|
-
__publicField(this, "southWest");
|
|
876
|
-
this.northEast = northEast;
|
|
877
|
-
this.southWest = southWest;
|
|
878
|
-
if (this.northEast && this.southWest && this.getNorth() < this.getSouth()) {
|
|
879
|
-
throw new Error("Incorrect bounding box");
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Returns the geographical coordinate equidistant from the bounding box's corners.
|
|
884
|
-
*/
|
|
885
|
-
get center() {
|
|
886
|
-
const latCenter = (this.southWest.lat + this.northEast.lat) / 2;
|
|
887
|
-
const lngCenter = (this.northEast.lng + this.southWest.lng) / 2;
|
|
888
|
-
return new Coordinates(latCenter, lngCenter);
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Check if a point is contained in the bounding box.
|
|
892
|
-
*/
|
|
893
|
-
contains(point) {
|
|
894
|
-
return point.lat <= this.northEast.lat && point.lat >= this.southWest.lat && point.lng <= this.northEast.lng && point.lng >= this.southWest.lng;
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* Extend the bounds to include a given LngLat or LngLatBounds.
|
|
898
|
-
*/
|
|
899
|
-
extend(obj) {
|
|
900
|
-
const sw = this.southWest, ne = this.northEast;
|
|
901
|
-
let sw2, ne2;
|
|
902
|
-
if (obj instanceof Coordinates) {
|
|
903
|
-
sw2 = obj;
|
|
904
|
-
ne2 = obj;
|
|
905
|
-
} else if (obj instanceof BoundingBox) {
|
|
906
|
-
sw2 = obj.southWest;
|
|
907
|
-
ne2 = obj.northEast;
|
|
908
|
-
} else {
|
|
909
|
-
throw new Error("Unknown parameter");
|
|
910
|
-
}
|
|
911
|
-
this.southWest = new Coordinates(
|
|
912
|
-
Math.min(sw2.lat, sw.lat),
|
|
913
|
-
Math.min(sw2.lng, sw.lng)
|
|
914
|
-
);
|
|
915
|
-
this.northEast = new Coordinates(
|
|
916
|
-
Math.max(ne2.lat, ne.lat),
|
|
917
|
-
Math.max(ne2.lng, ne.lng)
|
|
918
|
-
);
|
|
919
|
-
return this;
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* This method extends the bounding box with a value in meters
|
|
923
|
-
* /*\ This method is not precise as distance differs in function of latitude
|
|
924
|
-
*/
|
|
925
|
-
extendsWithMeasure(measure) {
|
|
926
|
-
if (typeof measure !== "number") {
|
|
927
|
-
throw new Error("measure is not a number");
|
|
928
|
-
}
|
|
929
|
-
this.northEast = this.northEast.destinationPoint(measure, 0).move(measure, Math.PI / 2);
|
|
930
|
-
this.southWest = this.southWest.clone().destinationPoint(measure, -Math.PI / 2).destinationPoint(measure, Math.PI);
|
|
931
|
-
return this;
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
|
|
935
|
-
* For example, a ratio of 0.5 extends the bounds by 50% in each direction.
|
|
936
|
-
* Negative values will retract the bounds.
|
|
937
|
-
*/
|
|
938
|
-
pad(bufferRatio) {
|
|
939
|
-
const sw = this.southWest;
|
|
940
|
-
const ne = this.northEast;
|
|
941
|
-
const heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio;
|
|
942
|
-
const widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
|
|
943
|
-
this.southWest = new Coordinates(sw.lat - heightBuffer, sw.lng - widthBuffer);
|
|
944
|
-
this.northEast = new Coordinates(ne.lat + heightBuffer, ne.lng + widthBuffer);
|
|
945
|
-
return this;
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Returns the southwest corner of the bounding box.
|
|
949
|
-
*/
|
|
950
|
-
getSouthWest() {
|
|
951
|
-
return this.southWest;
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
* Returns the northeast corner of the bounding box.
|
|
955
|
-
*/
|
|
956
|
-
getNorthEast() {
|
|
957
|
-
return this.northEast;
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* Returns the northwest corner of the bounding box.
|
|
961
|
-
*/
|
|
962
|
-
getNorthWest() {
|
|
963
|
-
return new Coordinates(this.getNorth(), this.getWest());
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Returns the southeast corner of the bounding box.
|
|
967
|
-
*/
|
|
968
|
-
getSouthEast() {
|
|
969
|
-
return new Coordinates(this.getSouth(), this.getEast());
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* Returns the west edge of the bounding box.
|
|
973
|
-
*/
|
|
974
|
-
getWest() {
|
|
975
|
-
return this.southWest.lng;
|
|
976
|
-
}
|
|
977
|
-
/**
|
|
978
|
-
* Returns the south edge of the bounding box.
|
|
979
|
-
*/
|
|
980
|
-
getSouth() {
|
|
981
|
-
return this.southWest.lat;
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Returns the east edge of the bounding box.
|
|
985
|
-
*/
|
|
986
|
-
getEast() {
|
|
987
|
-
return this.northEast.lng;
|
|
988
|
-
}
|
|
989
|
-
/**
|
|
990
|
-
* Returns the north edge of the bounding box.
|
|
991
|
-
*/
|
|
992
|
-
getNorth() {
|
|
993
|
-
return this.northEast.lat;
|
|
994
|
-
}
|
|
995
|
-
static equals(bb1, bb2) {
|
|
996
|
-
return Coordinates.equals(bb1.northEast, bb2.northEast) && Coordinates.equals(bb1.southWest, bb2.southWest);
|
|
997
|
-
}
|
|
998
|
-
equals(other) {
|
|
999
|
-
return BoundingBox.equals(this, other);
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Create a BoundingBox from a WSEN array
|
|
1003
|
-
*/
|
|
1004
|
-
static fromArray(bounds) {
|
|
1005
|
-
return new BoundingBox(
|
|
1006
|
-
new Coordinates(bounds[3], bounds[2]),
|
|
1007
|
-
new Coordinates(bounds[1], bounds[0])
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
static fromCoordinates(coords) {
|
|
1011
|
-
if (coords.length === 0) {
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
return coords.reduce(
|
|
1015
|
-
(acc, _coords) => acc.extend(_coords),
|
|
1016
|
-
new BoundingBox(coords[0], coords[0])
|
|
1017
|
-
);
|
|
1018
|
-
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Returns the WSEN array
|
|
1021
|
-
*/
|
|
1022
|
-
toArray() {
|
|
1023
|
-
return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()];
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
class RelativePosition {
|
|
1027
|
-
constructor(x, y, z, time = null, accuracy = null, bearing = null) {
|
|
1028
|
-
__publicField(this, "x");
|
|
1029
|
-
__publicField(this, "y");
|
|
1030
|
-
__publicField(this, "z");
|
|
1031
|
-
__publicField(this, "time", null);
|
|
1032
|
-
__publicField(this, "_accuracy", null);
|
|
1033
|
-
__publicField(this, "_bearing", null);
|
|
1034
|
-
this.x = x;
|
|
1035
|
-
this.y = y;
|
|
1036
|
-
this.z = z;
|
|
1037
|
-
this.time = time;
|
|
1038
|
-
this.accuracy = accuracy;
|
|
1039
|
-
this.bearing = bearing;
|
|
1040
|
-
}
|
|
1041
|
-
get accuracy() {
|
|
1042
|
-
return this._accuracy;
|
|
1043
|
-
}
|
|
1044
|
-
set accuracy(accuracy) {
|
|
1045
|
-
if (accuracy !== null && accuracy < 0) {
|
|
1046
|
-
throw new Error("accuracy argument is not a positive number");
|
|
1047
|
-
}
|
|
1048
|
-
this._accuracy = accuracy;
|
|
1049
|
-
}
|
|
1050
|
-
get bearing() {
|
|
1051
|
-
return this._bearing;
|
|
1052
|
-
}
|
|
1053
|
-
set bearing(bearing) {
|
|
1054
|
-
this._bearing = bearing !== null ? bearing % (2 * Math.PI) : null;
|
|
1055
|
-
}
|
|
1056
|
-
clone() {
|
|
1057
|
-
return new RelativePosition(this.x, this.y, this.z, this.time, this.accuracy, this.bearing);
|
|
1058
|
-
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Compares two RelativePosition
|
|
1061
|
-
* @param {RelativePosition} pos1 position 1
|
|
1062
|
-
* @param {RelativePosition} pos2 position 2
|
|
1063
|
-
* @param {Number} eps x, y, z epsilon in meters (default: 1e-3 [= 1mm])
|
|
1064
|
-
*/
|
|
1065
|
-
static equals(pos1, pos2, eps = EPS_MM) {
|
|
1066
|
-
if (pos1 === null && pos1 === pos2) {
|
|
1067
|
-
return true;
|
|
1068
|
-
}
|
|
1069
|
-
if (!(pos1 instanceof RelativePosition) || !(pos2 instanceof RelativePosition)) {
|
|
1070
|
-
return false;
|
|
1071
|
-
}
|
|
1072
|
-
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;
|
|
1073
|
-
}
|
|
1074
|
-
equals(other) {
|
|
1075
|
-
return RelativePosition.equals(this, other);
|
|
1076
|
-
}
|
|
1077
|
-
toJson() {
|
|
1078
|
-
return {
|
|
1079
|
-
x: this.x,
|
|
1080
|
-
y: this.y,
|
|
1081
|
-
z: this.z,
|
|
1082
|
-
...this.time !== null && { time: this.time },
|
|
1083
|
-
...this.accuracy !== null && { accuracy: this.accuracy },
|
|
1084
|
-
...this.bearing !== null && { bearing: this.bearing }
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
static fromJson(json) {
|
|
1088
|
-
return new RelativePosition(json.x, json.y, json.z, json.time, json.accuracy, json.bearing);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
class GeoRelativePosition extends RelativePosition {
|
|
1092
|
-
}
|
|
1093
|
-
class GeoRef {
|
|
1094
|
-
constructor(origin, buildingLevels = []) {
|
|
1095
|
-
__publicField(this, "scale", 1);
|
|
1096
|
-
__publicField(this, "heading", 0);
|
|
1097
|
-
this.origin = origin;
|
|
1098
|
-
this.buildingLevels = buildingLevels;
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* LocalPosition in ENU frame
|
|
1102
|
-
*/
|
|
1103
|
-
localToWorld(localPosition) {
|
|
1104
|
-
const enuTranslationScaled = Vector3.multiplyScalar(localPosition, this.scale);
|
|
1105
|
-
const rotationOffset = Quaternion.fromAxisAngle([0, 0, 1], this.heading);
|
|
1106
|
-
const enuToEcefRotationOrigin = Quaternion.multiply(rotationOffset, this.origin.enuToEcefRotation);
|
|
1107
|
-
const ecefTranslation = Quaternion.rotate(enuToEcefRotationOrigin, enuTranslationScaled);
|
|
1108
|
-
const ecef = Vector3.sum(this.origin.ecef, ecefTranslation);
|
|
1109
|
-
const coordinates = Coordinates.fromECEF(ecef);
|
|
1110
|
-
const alt = coordinates.alt;
|
|
1111
|
-
coordinates.heightFromGround = coordinates.alt - this.origin.alt;
|
|
1112
|
-
const updateLevelAndHeightFromFloor = (buildingLevel) => {
|
|
1113
|
-
coordinates.level = buildingLevel.id;
|
|
1114
|
-
coordinates.heightFromFloor = alt - buildingLevel.floorAltitude;
|
|
1115
|
-
};
|
|
1116
|
-
if (!this.buildingLevels.length) {
|
|
1117
|
-
return coordinates;
|
|
1118
|
-
}
|
|
1119
|
-
const correspondedLevelsByAltitude = this.buildingLevels.filter(
|
|
1120
|
-
(buildingLevel) => alt >= buildingLevel.floorAltitude && alt <= buildingLevel.ceilingAltitude
|
|
1121
|
-
);
|
|
1122
|
-
if (!correspondedLevelsByAltitude.length) {
|
|
1123
|
-
return coordinates;
|
|
1124
|
-
}
|
|
1125
|
-
const levelByAltitude = correspondedLevelsByAltitude[0];
|
|
1126
|
-
const geojsonPt = [coordinates.lng, coordinates.lat];
|
|
1127
|
-
const levelsWithCoverage = correspondedLevelsByAltitude.filter((l) => l.geometries.length);
|
|
1128
|
-
if (!levelsWithCoverage.length) {
|
|
1129
|
-
updateLevelAndHeightFromFloor(levelByAltitude);
|
|
1130
|
-
return coordinates;
|
|
1131
|
-
}
|
|
1132
|
-
const correspondedLevelsByCoverage = levelsWithCoverage.filter((l) => l.geometries.some((geom) => booleanPointInPolygon(geojsonPt, geom)));
|
|
1133
|
-
const levelByCoverage = correspondedLevelsByCoverage[0];
|
|
1134
|
-
if (!levelByCoverage) {
|
|
1135
|
-
return coordinates;
|
|
1136
|
-
}
|
|
1137
|
-
updateLevelAndHeightFromFloor(levelByCoverage);
|
|
1138
|
-
return coordinates;
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* LocalPosition in ENU frame
|
|
1142
|
-
*/
|
|
1143
|
-
worldToLocal(coords) {
|
|
1144
|
-
const rotationOffset = Quaternion.fromAxisAngle([0, 0, 1], -this.heading);
|
|
1145
|
-
const ecefToEnuRotationOrigin = Quaternion.multiply(this.origin.ecefToEnuRotation, rotationOffset);
|
|
1146
|
-
const ecefTranslation = Vector3.subtract(coords.ecef, this.origin.ecef);
|
|
1147
|
-
const ecefTranslationScaled = Vector3.multiplyScalar(ecefTranslation, 1 / this.scale);
|
|
1148
|
-
return Quaternion.rotate(ecefToEnuRotationOrigin, ecefTranslationScaled);
|
|
1149
|
-
}
|
|
1150
|
-
toJson() {
|
|
1151
|
-
return {
|
|
1152
|
-
origin: this.origin.toJson(),
|
|
1153
|
-
...this.scale !== 1 && { scale: this.scale },
|
|
1154
|
-
...this.heading !== 0 && { heading: this.heading }
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
|
-
static fromJson(json) {
|
|
1158
|
-
const geoRef = new GeoRef(Coordinates.fromJson(json.origin));
|
|
1159
|
-
geoRef.scale = typeof json.scale !== "undefined" ? json.scale : 1;
|
|
1160
|
-
geoRef.heading = typeof json.heading !== "undefined" ? json.heading : 0;
|
|
1161
|
-
return geoRef;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
class Attitude {
|
|
1165
|
-
constructor(quaternion, time = null, accuracy = null) {
|
|
1166
|
-
__publicField(this, "_quaternion", [1, 0, 0, 0]);
|
|
1167
|
-
__publicField(this, "_heading", null);
|
|
1168
|
-
__publicField(this, "_eulerAngles", null);
|
|
1169
|
-
__publicField(this, "_time", null);
|
|
1170
|
-
__publicField(this, "_accuracy", null);
|
|
1171
|
-
this.quaternion = quaternion;
|
|
1172
|
-
this.time = time;
|
|
1173
|
-
this.accuracy = accuracy;
|
|
1174
|
-
}
|
|
1175
|
-
static unitary() {
|
|
1176
|
-
return new Attitude([1, 0, 0, 0]);
|
|
1177
|
-
}
|
|
1178
|
-
get quaternion() {
|
|
1179
|
-
return this._quaternion;
|
|
1180
|
-
}
|
|
1181
|
-
set quaternion(quaternion) {
|
|
1182
|
-
if (Math.abs(1 - Quaternion.norm(quaternion)) > 1e-4) {
|
|
1183
|
-
throw new Error("quaternion is not a unit quaternion");
|
|
1184
|
-
}
|
|
1185
|
-
this._quaternion = quaternion;
|
|
1186
|
-
this._heading = null;
|
|
1187
|
-
this._eulerAngles = null;
|
|
1188
|
-
}
|
|
1189
|
-
get time() {
|
|
1190
|
-
return this._time;
|
|
1191
|
-
}
|
|
1192
|
-
set time(time) {
|
|
1193
|
-
this._time = time;
|
|
1194
|
-
}
|
|
1195
|
-
get accuracy() {
|
|
1196
|
-
return this._accuracy;
|
|
1197
|
-
}
|
|
1198
|
-
set accuracy(accuracy) {
|
|
1199
|
-
if (accuracy !== null && (accuracy < 0 || accuracy > Math.PI)) {
|
|
1200
|
-
throw new Error("accuracy argument (" + accuracy + ") is not in range [0; PI]");
|
|
1201
|
-
}
|
|
1202
|
-
this._accuracy = accuracy;
|
|
1203
|
-
}
|
|
1204
|
-
get eulerAngles() {
|
|
1205
|
-
if (this._eulerAngles === null) {
|
|
1206
|
-
this._eulerAngles = Rotations.quaternionToEulerZXY(this.quaternion);
|
|
1207
|
-
}
|
|
1208
|
-
return this._eulerAngles;
|
|
1209
|
-
}
|
|
1210
|
-
get eulerAnglesDegrees() {
|
|
1211
|
-
return this.eulerAngles.map((x) => rad2deg(x));
|
|
1212
|
-
}
|
|
1213
|
-
get heading() {
|
|
1214
|
-
if (this._heading === null) {
|
|
1215
|
-
let offset = 0;
|
|
1216
|
-
if (typeof window !== "undefined" && window && window.orientation) {
|
|
1217
|
-
offset = deg2rad(window.orientation);
|
|
1218
|
-
}
|
|
1219
|
-
this._heading = Rotations.getHeadingFromQuaternion(this.quaternion) + offset;
|
|
1220
|
-
}
|
|
1221
|
-
return this._heading;
|
|
1222
|
-
}
|
|
1223
|
-
get headingDegrees() {
|
|
1224
|
-
return rad2deg(this.heading);
|
|
1225
|
-
}
|
|
1226
|
-
static equals(att1, att2) {
|
|
1227
|
-
if (att1 === null && att1 === att2) {
|
|
1228
|
-
return true;
|
|
1229
|
-
}
|
|
1230
|
-
if (!(att1 instanceof Attitude) || !(att2 instanceof Attitude)) {
|
|
1231
|
-
return false;
|
|
1232
|
-
}
|
|
1233
|
-
if (att1 === att2) {
|
|
1234
|
-
return true;
|
|
1235
|
-
}
|
|
1236
|
-
return Quaternion.equals(att1.quaternion, att2.quaternion);
|
|
1237
|
-
}
|
|
1238
|
-
equals(other) {
|
|
1239
|
-
return Attitude.equals(this, other);
|
|
1240
|
-
}
|
|
1241
|
-
toJson() {
|
|
1242
|
-
if (this.time === null && this.accuracy === null) {
|
|
1243
|
-
return this.quaternion;
|
|
1244
|
-
}
|
|
1245
|
-
return {
|
|
1246
|
-
q: this.quaternion,
|
|
1247
|
-
...this.time !== null && { time: this.time },
|
|
1248
|
-
...this.accuracy !== null && { accuracy: this.accuracy }
|
|
1249
|
-
};
|
|
1250
|
-
}
|
|
1251
|
-
static fromJson(json) {
|
|
1252
|
-
if (Array.isArray(json)) {
|
|
1253
|
-
return new Attitude(json, null, null);
|
|
1254
|
-
}
|
|
1255
|
-
return new Attitude(json.q, json.time, json.accuracy);
|
|
1256
|
-
}
|
|
1257
|
-
clone() {
|
|
1258
|
-
return new Attitude(this.quaternion.slice(0), this.time, this.accuracy);
|
|
1259
|
-
}
|
|
1260
|
-
/**
|
|
1261
|
-
* Calculate the relative attitude between two given attitudes
|
|
1262
|
-
*/
|
|
1263
|
-
static diff(attitudeStart, attitudeEnd) {
|
|
1264
|
-
const quaternionDiff = Quaternion.multiply(
|
|
1265
|
-
Quaternion.inverse(attitudeStart.quaternion),
|
|
1266
|
-
attitudeEnd.quaternion
|
|
1267
|
-
);
|
|
1268
|
-
let timeDiff = null;
|
|
1269
|
-
if (attitudeEnd.time !== null && attitudeStart.time !== null) {
|
|
1270
|
-
timeDiff = attitudeEnd.time - attitudeStart.time;
|
|
1271
|
-
}
|
|
1272
|
-
let accuracyDiff = null;
|
|
1273
|
-
if (attitudeStart.accuracy !== null && attitudeEnd.accuracy !== null) {
|
|
1274
|
-
accuracyDiff = Math.max(attitudeEnd.accuracy - attitudeStart.accuracy);
|
|
1275
|
-
}
|
|
1276
|
-
return new Attitude(quaternionDiff, timeDiff, accuracyDiff);
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
class AbsoluteHeading {
|
|
1280
|
-
constructor(heading, time = null, accuracy = null) {
|
|
1281
|
-
__publicField(this, "heading");
|
|
1282
|
-
__publicField(this, "time", null);
|
|
1283
|
-
__publicField(this, "_accuracy", null);
|
|
1284
|
-
this.heading = heading;
|
|
1285
|
-
this.time = time;
|
|
1286
|
-
this.accuracy = accuracy;
|
|
1287
|
-
}
|
|
1288
|
-
get accuracy() {
|
|
1289
|
-
return this._accuracy;
|
|
1290
|
-
}
|
|
1291
|
-
set accuracy(accuracy) {
|
|
1292
|
-
if (accuracy !== null && (accuracy < 0 || accuracy > Math.PI)) {
|
|
1293
|
-
throw new Error("accuracy argument (" + accuracy + ") is not in range [0; PI]");
|
|
1294
|
-
}
|
|
1295
|
-
this._accuracy = accuracy;
|
|
1296
|
-
}
|
|
1297
|
-
toAttitude() {
|
|
1298
|
-
return new Attitude(
|
|
1299
|
-
Quaternion.fromAxisAngle([0, 0, 1], -this.heading),
|
|
1300
|
-
this.time,
|
|
1301
|
-
this.accuracy
|
|
1302
|
-
);
|
|
1303
|
-
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Compares two AbsoluteHeading
|
|
1306
|
-
* @param {AbsoluteHeading} heading1 heading 1
|
|
1307
|
-
* @param {AbsoluteHeading} heading2 heading 2
|
|
1308
|
-
*/
|
|
1309
|
-
static equals(heading1, heading2) {
|
|
1310
|
-
if (heading1 === null && heading1 === heading2) {
|
|
1311
|
-
return true;
|
|
1312
|
-
}
|
|
1313
|
-
if (!(heading1 instanceof AbsoluteHeading) || !(heading2 instanceof AbsoluteHeading)) {
|
|
1314
|
-
return false;
|
|
1315
|
-
}
|
|
1316
|
-
return Math.abs(heading1.heading - heading2.heading) < 1e-8;
|
|
1317
|
-
}
|
|
1318
|
-
equals(other) {
|
|
1319
|
-
return AbsoluteHeading.equals(this, other);
|
|
1320
|
-
}
|
|
1321
|
-
toJson() {
|
|
1322
|
-
return {
|
|
1323
|
-
heading: this.heading,
|
|
1324
|
-
...this.time !== null && { time: this.time },
|
|
1325
|
-
...this.accuracy !== null && { accuracy: this.accuracy }
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
static fromJson(json) {
|
|
1329
|
-
return new AbsoluteHeading(json.heading, json.time, json.accuracy);
|
|
1330
|
-
}
|
|
1331
|
-
clone() {
|
|
1332
|
-
return new AbsoluteHeading(this.heading, this.time, this.accuracy);
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
export {
|
|
1336
|
-
AbsoluteHeading,
|
|
1337
|
-
Attitude,
|
|
1338
|
-
BoundingBox,
|
|
1339
|
-
Constants,
|
|
1340
|
-
Coordinates,
|
|
1341
|
-
GeoRef,
|
|
1342
|
-
GeoRelativePosition,
|
|
1343
|
-
Level,
|
|
1344
|
-
RelativePosition,
|
|
1345
|
-
UserPosition,
|
|
1346
|
-
Utils
|
|
1347
|
-
};
|
|
1348
|
-
//# sourceMappingURL=index.mjs.map
|