gps-plus-slam-js 1.4.0 → 1.6.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/dist/index.d.ts
CHANGED
|
@@ -133,6 +133,27 @@ interface RawDeviceOrientation {
|
|
|
133
133
|
/** Whether alpha is relative to magnetic north. */
|
|
134
134
|
absolute: boolean;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Raw `AbsoluteOrientationSensor` (Generic Sensor API) reading — an
|
|
138
|
+
* Earth-referenced (magnetic ENU) device orientation, captured per GPS event as
|
|
139
|
+
* a GPS-independent north reference. See
|
|
140
|
+
* GpsPlusSlamJs_Docs/docs/2026-06-25-absolute-orientation-sensor-plan.md §5.
|
|
141
|
+
*
|
|
142
|
+
* Unlike {@link RawDeviceOrientation} (whose `absolute` flag is `false` on most
|
|
143
|
+
* Android browsers), this quaternion is magnetometer-fused and meaningfully
|
|
144
|
+
* absolute. Stored verbatim — magnetic→true correction (WMM declination) and the
|
|
145
|
+
* AR→north composition happen offline/at-consumption, not here.
|
|
146
|
+
*/
|
|
147
|
+
interface RawAbsoluteOrientation {
|
|
148
|
+
/** [x,y,z,w] quaternion mapping device-frame → Earth ENU (+X East, +Y (magnetic) North, +Z Up). */
|
|
149
|
+
quaternion: Quaternion;
|
|
150
|
+
/** Reference frame the quaternion is expressed in. We capture the raw `'device'` frame. */
|
|
151
|
+
referenceFrame: 'device' | 'screen';
|
|
152
|
+
/** `screen.orientation.angle` at sample time — needed to reconstruct the `'device'` frame. */
|
|
153
|
+
screenAngleDeg: number;
|
|
154
|
+
/** Sensor reading time (epoch ms) — lets magnetometer warm-up be detected offline. */
|
|
155
|
+
sampleTimestamp: number;
|
|
156
|
+
}
|
|
136
157
|
/**
|
|
137
158
|
* Convert Euler angles from W3C DeviceOrientationEvent to a Quaternion.
|
|
138
159
|
* Rotation order: ZXY intrinsic (per W3C DeviceOrientation spec §3.1).
|
|
@@ -145,6 +166,41 @@ interface RawDeviceOrientation {
|
|
|
145
166
|
*/
|
|
146
167
|
declare const eulerToQuaternion: (alpha: number, beta: number, gamma: number) => Quaternion;
|
|
147
168
|
//#endregion
|
|
169
|
+
//#region ../src/state/models/compass-trust.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* Phase 4 Stage A — magnetometer consistency-trust state machine
|
|
172
|
+
* (CLOSED / INTERNAL ONLY).
|
|
173
|
+
*
|
|
174
|
+
* A pure, deterministic reducer that earns (or withholds) trust in the absolute
|
|
175
|
+
* compass by comparing it against the **GPS-only** alignment yaw over time. It
|
|
176
|
+
* exists to feed the *steady-state* Stage B/C use of the compass (detection /
|
|
177
|
+
* soft-prior correction) — it is built in the same iteration as Stage 0 but is
|
|
178
|
+
* **deliberately orthogonal to the Stage-0 cold-start handover**, which stays
|
|
179
|
+
* GPS-intrinsic and never consults trust (plan §7b "the circularity trap").
|
|
180
|
+
*
|
|
181
|
+
* The cardinal property (plan §4c item 9, §10): a **consistently-biased**
|
|
182
|
+
* magnetometer must NEVER reach "trusted" once GPS yaw is observable — its
|
|
183
|
+
* disagreement keeps it out of the trusted state, and a sustained disagreement
|
|
184
|
+
* raises the figure-8 recalibration recommendation instead.
|
|
185
|
+
*
|
|
186
|
+
* Trust ordering (plan §7b Stage A):
|
|
187
|
+
* - Before GPS yaw is observable there is nothing trustworthy to validate
|
|
188
|
+
* against ⇒ the machine is `dormant` (Stage 0's unconditional override holds).
|
|
189
|
+
* - Once observable, agreement within tolerance for a sustained streak ⇒
|
|
190
|
+
* `trusted`; a later large disagreement drops it back to `untrusted`.
|
|
191
|
+
*
|
|
192
|
+
* See the colocated `.md` sidecar for the API, invariants and test strategy.
|
|
193
|
+
*/
|
|
194
|
+
type TrustState = 'dormant' | 'untrusted' | 'trusted';
|
|
195
|
+
/** Serializable trust memory (survives Redux round-trips). */
|
|
196
|
+
interface TrustMemory {
|
|
197
|
+
readonly state: TrustState;
|
|
198
|
+
readonly agreeStreak: number;
|
|
199
|
+
readonly disagreeStreak: number;
|
|
200
|
+
/** Derived per-step: surface the figure-8 prompt (observable + sustained disagreement). */
|
|
201
|
+
readonly recommendRecalibration: boolean;
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
148
204
|
//#region ../src/state/models/gpsEvents.d.ts
|
|
149
205
|
/**
|
|
150
206
|
* Plain object representing GPS events state.
|
|
@@ -166,6 +222,36 @@ interface GpsEventsState {
|
|
|
166
222
|
gpsAccuracyMedian: number | null;
|
|
167
223
|
gpsAccuracyMean: number | null;
|
|
168
224
|
currentGpsPosGeoHash: string | null;
|
|
225
|
+
/**
|
|
226
|
+
* Odometry position + GPS timestamp of the last sample admitted into the
|
|
227
|
+
* stored history (Proposal B distance-gating). Null until the first sample
|
|
228
|
+
* and after any rebuild that empties the history. Always reflects the last
|
|
229
|
+
* *stored* sample (the only current reason to skip storing is gating itself);
|
|
230
|
+
* the incremental path reads it to gate the next sample, and the batch path
|
|
231
|
+
* recomputes it from the stored arrays — which is why both origin-jump
|
|
232
|
+
* handlers (which rebuild via the batch path) reset it for free. Kept
|
|
233
|
+
* serializable (plain tuple) so it survives Redux round-trips. Only
|
|
234
|
+
* meaningful when `useDistanceGating` is on.
|
|
235
|
+
*/
|
|
236
|
+
lastAdmittedOdomPos: Vector3 | null;
|
|
237
|
+
lastAdmittedTs: number | null;
|
|
238
|
+
/**
|
|
239
|
+
* Phase-4 Stage 0 (only present when `useCompassColdStartOverride` is on):
|
|
240
|
+
* ring buffer of the last N GPS-only solved yaw bearings (deg) feeding the
|
|
241
|
+
* intrinsic observability gate, and the last applied override bearing driving
|
|
242
|
+
* the snap-correction across incremental calls. **Absent** when the override is
|
|
243
|
+
* off ⇒ byte-identical state shape for every existing consumer. Reset on
|
|
244
|
+
* origin-jump rebuilds (the batch path rebuilds the window from prefixes).
|
|
245
|
+
*/
|
|
246
|
+
coldStartYawWindowDeg?: number[];
|
|
247
|
+
coldStartAppliedBearingDeg?: number | null;
|
|
248
|
+
/**
|
|
249
|
+
* Phase-4 Stage C (only present when `useCompassRotationPrior` is on): the
|
|
250
|
+
* compass consistency-trust memory, updated each observation from the windowed
|
|
251
|
+
* compass-vs-GPS-only-yaw agreement (gated on observability). Absent otherwise
|
|
252
|
+
* ⇒ byte-identical state shape. Reset to dormant on origin-jump rebuilds.
|
|
253
|
+
*/
|
|
254
|
+
compassTrust?: TrustMemory;
|
|
169
255
|
}
|
|
170
256
|
//#endregion
|
|
171
257
|
//#region ../src/state/types.d.ts
|
|
@@ -221,6 +307,13 @@ interface GpsPoint extends RawGpsPoint {
|
|
|
221
307
|
coordinates: Vector3;
|
|
222
308
|
readonly weight: number;
|
|
223
309
|
deviceRotation?: Quaternion;
|
|
310
|
+
/**
|
|
311
|
+
* Raw AbsoluteOrientationSensor reading captured with this GPS event — the
|
|
312
|
+
* GPS-independent (magnetic ENU) device orientation. Paired 1:1 with the
|
|
313
|
+
* event's `odomRotation`, so the offline AR→north composition is exact.
|
|
314
|
+
* `undefined` on recordings/devices without the sensor (most non-Chrome-Android).
|
|
315
|
+
*/
|
|
316
|
+
absoluteOrientation?: RawAbsoluteOrientation;
|
|
224
317
|
}
|
|
225
318
|
interface GpsMarker {
|
|
226
319
|
id: string;
|
|
@@ -312,6 +405,16 @@ interface GpsModel {
|
|
|
312
405
|
zero: LatLong;
|
|
313
406
|
gpsEvents: GpsEventsType;
|
|
314
407
|
odometryPath: OdometryPath;
|
|
408
|
+
/**
|
|
409
|
+
* Runtime opt-in for the Phase-4 Stage-0 cold-start compass yaw override
|
|
410
|
+
* (closed/internal alignment feature). Set via `setColdStartOverrideEnabled`
|
|
411
|
+
* — must be dispatched **after** `setZeroPos` (state is null before it). When
|
|
412
|
+
* truthy the reducer threads `{ useCompassColdStartOverride: true }` into the
|
|
413
|
+
* solve; absent/false ⇒ byte-identical to today. A debug/experiment flag —
|
|
414
|
+
* keep OFF until the §6a field matrix re-tunes the thresholds. See
|
|
415
|
+
* GpsPlusSlamJs_Docs/docs/2026-06-26-absolute-orientation-sensor-phase4-stage0-followup.md.
|
|
416
|
+
*/
|
|
417
|
+
coldStartOverrideEnabled?: boolean;
|
|
315
418
|
}
|
|
316
419
|
interface GpsSlamState {
|
|
317
420
|
gpsData: GpsModel | null;
|
|
@@ -323,6 +426,12 @@ interface RecordGpsEventPayload {
|
|
|
323
426
|
odomRotation: Quaternion;
|
|
324
427
|
rawGpsPoint: RawGpsPoint;
|
|
325
428
|
rawDeviceOrientation?: RawDeviceOrientation;
|
|
429
|
+
/**
|
|
430
|
+
* Raw AbsoluteOrientationSensor reading at GPS-event time (Phase 1 capture).
|
|
431
|
+
* Optional ⇒ old recordings remain valid untouched; the field is simply absent
|
|
432
|
+
* on devices/platforms without the sensor.
|
|
433
|
+
*/
|
|
434
|
+
rawAbsoluteOrientation?: RawAbsoluteOrientation;
|
|
326
435
|
}
|
|
327
436
|
interface OdometryTrackingRestartedPayload {
|
|
328
437
|
lastValidOdomPos: Vector3;
|
|
@@ -429,6 +538,7 @@ declare const recordGpsEvent: _$_reduxjs_toolkit0.ActionCreatorWithPayload<Recor
|
|
|
429
538
|
declare const odometryTrackingRestarted: _$_reduxjs_toolkit0.ActionCreatorWithPayload<OdometryTrackingRestartedPayload, "gpsData/odometryTrackingRestarted">;
|
|
430
539
|
declare const arLoopClosureDetected: _$_reduxjs_toolkit0.ActionCreatorWithPayload<ArLoopClosureDetectedPayload, "gpsData/arLoopClosureDetected">;
|
|
431
540
|
declare const add2dImage: _$_reduxjs_toolkit0.ActionCreatorWithPayload<ArImageCapture, "gpsData/add2dImage">;
|
|
541
|
+
declare const setColdStartOverrideEnabled: _$_reduxjs_toolkit0.ActionCreatorWithPayload<boolean, "gpsData/setColdStartOverrideEnabled">;
|
|
432
542
|
declare const gpsDataReducer: (state: GpsModel | null | undefined, action: _$redux.UnknownAction) => GpsModel | null;
|
|
433
543
|
//#endregion
|
|
434
544
|
//#region ../src/state/gpsElementsSlice.d.ts
|
|
@@ -495,6 +605,106 @@ declare const getOpenStreetMapLink: (coord: LatLong, zoomLevel?: number) => stri
|
|
|
495
605
|
*/
|
|
496
606
|
declare const fusedGpsFromOdom: (alignmentMatrix: Matrix4, odomPosition: Vector3, zeroRef: LatLong) => LatLongAlt;
|
|
497
607
|
//#endregion
|
|
608
|
+
//#region ../src/math/magnetic-declination.d.ts
|
|
609
|
+
/**
|
|
610
|
+
* Pure, dependency-free World Magnetic Model 2025 (WMM2025) magnetic-declination
|
|
611
|
+
* synthesis.
|
|
612
|
+
*
|
|
613
|
+
* Sign convention (WMM): declination D is the angle of the horizontal magnetic
|
|
614
|
+
* field measured clockwise (EAST) from geographic (true) north, EAST-positive.
|
|
615
|
+
* To convert a compass (magnetic) heading to a true heading:
|
|
616
|
+
*
|
|
617
|
+
* trueHeading = normalize(magneticHeading + D)
|
|
618
|
+
*
|
|
619
|
+
* Example: a magnetic heading of 90 deg at a location with D = +5 deg (5 deg
|
|
620
|
+
* EAST) corresponds to a true heading of 95 deg.
|
|
621
|
+
*
|
|
622
|
+
* The Gauss coefficients below are the official WMM2025 model (epoch 2025.0,
|
|
623
|
+
* valid 2025.0-2030.0), transcribed verbatim from the NOAA-distributed
|
|
624
|
+
* WMM.COF (header "2025.0 WMM-2025 11/13/2024"). Source mirror:
|
|
625
|
+
* https://github.com/boxpet/pygeomag (pygeomag/wmm/WMM_2025.COF)
|
|
626
|
+
* Acceptance oracle (official NOAA test values):
|
|
627
|
+
* https://www.ncei.noaa.gov/sites/default/files/2025-02/WMM2025testvalues.pdf
|
|
628
|
+
*
|
|
629
|
+
* The synthesis follows the standard WMM technical-report algorithm: decimal
|
|
630
|
+
* year -> time-adjusted Gauss coefficients -> geodetic-to-geocentric mapping
|
|
631
|
+
* (WGS-84) -> Schmidt semi-normalized associated Legendre functions ->
|
|
632
|
+
* spherical-harmonic field components (X north, Y east, Z down) ->
|
|
633
|
+
* D = atan2(Y, X).
|
|
634
|
+
*/
|
|
635
|
+
/** Model epoch (decimal year) of WMM2025. */
|
|
636
|
+
declare const WMM2025_EPOCH = 2025;
|
|
637
|
+
/** Validity window of WMM2025 (decimal years), inclusive lower / exclusive upper. */
|
|
638
|
+
declare const WMM2025_VALID_FROM = 2025;
|
|
639
|
+
declare const WMM2025_VALID_TO = 2030;
|
|
640
|
+
/**
|
|
641
|
+
* Magnetic declination in degrees, EAST-positive (WMM convention).
|
|
642
|
+
* D = atan2(Y, X) where X is geodetic-north and Y is geodetic-east field.
|
|
643
|
+
*
|
|
644
|
+
* @param latDeg geodetic latitude in degrees (-90..90)
|
|
645
|
+
* @param lonDeg geodetic longitude in degrees (-180..180)
|
|
646
|
+
* @param opts.altitudeKm height above WGS-84 ellipsoid in km (default 0)
|
|
647
|
+
* @param opts.date Date or epoch-millis; defaults to a fixed in-model decimal year
|
|
648
|
+
* @returns declination in degrees, or NaN if inputs are non-finite.
|
|
649
|
+
*/
|
|
650
|
+
declare function magneticDeclinationDeg(latDeg: number, lonDeg: number, opts?: {
|
|
651
|
+
altitudeKm?: number;
|
|
652
|
+
date?: Date | number;
|
|
653
|
+
}): number;
|
|
654
|
+
/**
|
|
655
|
+
* Convert a magnetic heading to a true heading:
|
|
656
|
+
* trueHeading = normalize(magneticDeg + D)
|
|
657
|
+
* where D is the magnetic declination (EAST-positive). Result is in [0, 360).
|
|
658
|
+
*/
|
|
659
|
+
declare function magneticToTrueHeadingDeg(magneticDeg: number, latDeg: number, lonDeg: number, opts?: {
|
|
660
|
+
altitudeKm?: number;
|
|
661
|
+
date?: Date | number;
|
|
662
|
+
}): number;
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region ../src/math/orientation-heading.d.ts
|
|
665
|
+
/**
|
|
666
|
+
* Magnetic heading in degrees (0 = North, 90 = East, clockwise) from a
|
|
667
|
+
* device→ENU quaternion, derived by rotating the device camera-forward (−Z)
|
|
668
|
+
* into ENU and taking its horizontal bearing.
|
|
669
|
+
*
|
|
670
|
+
* Returns `null` when the camera points within ~5° of straight up/down, where
|
|
671
|
+
* the horizontal projection is too small for a stable bearing (the v3 guard).
|
|
672
|
+
*
|
|
673
|
+
* This is an *indicative* read-out (the exact forward axis depends on how the
|
|
674
|
+
* phone is held); the rigorous AR→north comparison is {@link arNorthBearingDeg}.
|
|
675
|
+
*/
|
|
676
|
+
declare function magneticHeadingFromEnuQuat(q: Quaternion): number | null;
|
|
677
|
+
/**
|
|
678
|
+
* The composed AR→ENU rotation: maps a vector expressed in the AR (WebXR)
|
|
679
|
+
* frame into the magnetic ENU frame, using **only** the sensor reading and the
|
|
680
|
+
* AR pose — never GPS.
|
|
681
|
+
*
|
|
682
|
+
* q(AR→ENU) = q(device→ENU)_sensor ∘ q(device→AR)⁻¹_arPose
|
|
683
|
+
*
|
|
684
|
+
* This is the GPS-independent estimate of the alignment's own rotation (plan §3).
|
|
685
|
+
* Both inputs ride in the same recorded GPS event, so pairing is exact.
|
|
686
|
+
*/
|
|
687
|
+
declare function arToEnuRotation(qSensorDeviceToEnu: Quaternion, qArPoseDeviceToAr: Quaternion): Quaternion;
|
|
688
|
+
/**
|
|
689
|
+
* Magnetic bearing (deg, 0 = North, 90 = East, clockwise) that the AR frame's
|
|
690
|
+
* north axis (−Z) points toward, computed independently of GPS from the sensor
|
|
691
|
+
* quaternion + the AR pose. This is the "AR→north" yaw the Phase-2 harness
|
|
692
|
+
* compares against the GPS-derived alignment matrix's yaw (plan §3, §4c kernel 4).
|
|
693
|
+
*
|
|
694
|
+
* Returns `null` if the AR-north axis maps near-vertical in ENU (degenerate,
|
|
695
|
+
* e.g. a physically impossible pose); horizontal otherwise.
|
|
696
|
+
*
|
|
697
|
+
* NOTE: this is a **magnetic** bearing. Correct to true north via the WMM
|
|
698
|
+
* declination module before comparing against a geographic-ENU alignment.
|
|
699
|
+
*/
|
|
700
|
+
declare function arNorthBearingDeg(qSensorDeviceToEnu: Quaternion, qArPoseDeviceToAr: Quaternion): number | null;
|
|
701
|
+
/**
|
|
702
|
+
* Signed smallest-magnitude difference between two bearings, in (−180, 180].
|
|
703
|
+
* Positive = `a` is clockwise (east) of `b`. Useful for the AR→north vs
|
|
704
|
+
* alignment-yaw discrepancy (plan §3) and the Phase-4 trust comparisons.
|
|
705
|
+
*/
|
|
706
|
+
declare function bearingDeltaDeg(a: number, b: number): number;
|
|
707
|
+
//#endregion
|
|
498
708
|
//#region ../src/state/selectors.d.ts
|
|
499
709
|
/** Returns the 4×4 alignment matrix, or null if not yet computed. */
|
|
500
710
|
declare const getAlignmentMatrix: (state: GpsSlamState) => Matrix4 | null;
|
|
@@ -534,4 +744,4 @@ declare function validateLicenseKey(key: string): LicenseValidationResult;
|
|
|
534
744
|
//#region ../src/index.d.ts
|
|
535
745
|
declare const LIB_VERSION = "1.0.0";
|
|
536
746
|
//#endregion
|
|
537
|
-
export { type Add2dImagePayload, type ArElementsState, type ArImageCapture, type ArLoopClosureDetectedPayload, type AreaPolygon, type CreateGpsSlamStoreOptions, type DetectedArPlane, type EventArea, type FloorDetection, type GpsElementsState, type GpsLine, type GpsMarker, type GpsModel, type GpsPoint, type GpsSlamDispatch, type GpsSlamState, type GpsSlamStore, type HeatArea, type HeatMapTile, IDENTITY_MATRIX4, IDENTITY_QUATERNION, LIB_VERSION, type LatLong, type LatLongAlt, type LicenseValidationResult, type Matrix4, type OdometryPath, type OdometryTrackingRestartedPayload, type Quaternion, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RootState, type Vector3, type Vector4, ZERO_VECTOR3, add2dImage, addArea, addCornerToEventArea, addHeatMapArea, addLine, addMarker, addOrUpdateArPlane, addToHeatMaps, anchorEventAreaInWorldSpace, arElementsReducer, arLoopClosureDetected, calcGeoHash, calcGpsCoords, calcQuadKey, calcRelativeCoordsInMeters, cloneMatrix4, cloneQuaternion, cloneVector3, correctArDriftForEventArea, createGpsSlamStore, distanceInMeters, distanceInMetersRelative, enuQuaternionToNUE, eulerToQuaternion, finishEventArea, fromMatrix4, fromQuaternion, fromVector3, fusedGpsFromOdom, geoHashToLatLong, getAlignmentMatrix, getAlignmentRotation, getGoogleMapsDirectionsLink, getGoogleMapsLink, getGpsAccuracyStats, getGpsPositions, getOdometryPositions, getOdometryRotations, getOpenStreetMapLink, getZeroReference, gpsDataReducer, gpsElementsReducer, invertQuaternion, isIdentityMatrix4, isIdentityQuaternion, isNearIdentityQuaternion, multiplyQuaternions, normalizeQuaternion, nueQuaternionToENU, nueQuaternionToWebXR, nueToWebXR, odometryTrackingRestarted, quadKeyToLatLong, quaternionMagnitude, quaternionsEquivalent, recordGpsEvent, recordPhoneHeight, resetArElements, resetGpsElements, sanitizeForDevTools, setZeroPos, startEventArea, toEarthCenteredCoordinates, toMatrix4, toQuaternion, toVector3, validateLicenseKey, webxrQuaternionToNUE, webxrToNUE };
|
|
747
|
+
export { type Add2dImagePayload, type ArElementsState, type ArImageCapture, type ArLoopClosureDetectedPayload, type AreaPolygon, type CreateGpsSlamStoreOptions, type DetectedArPlane, type EventArea, type FloorDetection, type GpsElementsState, type GpsLine, type GpsMarker, type GpsModel, type GpsPoint, type GpsSlamDispatch, type GpsSlamState, type GpsSlamStore, type HeatArea, type HeatMapTile, IDENTITY_MATRIX4, IDENTITY_QUATERNION, LIB_VERSION, type LatLong, type LatLongAlt, type LicenseValidationResult, type Matrix4, type OdometryPath, type OdometryTrackingRestartedPayload, type Quaternion, type RawAbsoluteOrientation, type RawDeviceOrientation, type RawGpsPoint, type RecordGpsEventPayload, type RootState, type Vector3, type Vector4, WMM2025_EPOCH, WMM2025_VALID_FROM, WMM2025_VALID_TO, ZERO_VECTOR3, add2dImage, addArea, addCornerToEventArea, addHeatMapArea, addLine, addMarker, addOrUpdateArPlane, addToHeatMaps, anchorEventAreaInWorldSpace, arElementsReducer, arLoopClosureDetected, arNorthBearingDeg, arToEnuRotation, bearingDeltaDeg, calcGeoHash, calcGpsCoords, calcQuadKey, calcRelativeCoordsInMeters, cloneMatrix4, cloneQuaternion, cloneVector3, correctArDriftForEventArea, createGpsSlamStore, distanceInMeters, distanceInMetersRelative, enuQuaternionToNUE, eulerToQuaternion, finishEventArea, fromMatrix4, fromQuaternion, fromVector3, fusedGpsFromOdom, geoHashToLatLong, getAlignmentMatrix, getAlignmentRotation, getGoogleMapsDirectionsLink, getGoogleMapsLink, getGpsAccuracyStats, getGpsPositions, getOdometryPositions, getOdometryRotations, getOpenStreetMapLink, getZeroReference, gpsDataReducer, gpsElementsReducer, invertQuaternion, isIdentityMatrix4, isIdentityQuaternion, isNearIdentityQuaternion, magneticDeclinationDeg, magneticHeadingFromEnuQuat, magneticToTrueHeadingDeg, multiplyQuaternions, normalizeQuaternion, nueQuaternionToENU, nueQuaternionToWebXR, nueToWebXR, odometryTrackingRestarted, quadKeyToLatLong, quaternionMagnitude, quaternionsEquivalent, recordGpsEvent, recordPhoneHeight, resetArElements, resetGpsElements, sanitizeForDevTools, setColdStartOverrideEnabled, setZeroPos, startEventArea, toEarthCenteredCoordinates, toMatrix4, toQuaternion, toVector3, validateLicenseKey, webxrQuaternionToNUE, webxrToNUE };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
|
|
2
|
-
import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,ee=Math.PI/180,C=180/Math.PI,w=`0123456789bcdefghjkmnpqrstuvwxyz`,T=[16,8,4,2,1],E=(e,t,n)=>Math.min(Math.max(e,t),n),te=e=>E(e,-180,180),D=e=>e*ee,ne=e=>e*C,re=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},ie=e=>({lat:re(e.lat),lon:re(e.lon)}),ae=(e,t=0)=>{let n=D(e.lat),r=D(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},oe=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=E(e.lat,-85.05112878,85.05112878),r=te(e.lon),i=Math.sin(D(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=E(Math.floor(o/256),0,c),u=E(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),se=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:ne(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),ce=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=T[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=T[s],n=t):r=t}l=!l,s<4?s+=1:(o+=w[c],s=0,c=0)}return o}),le=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=w.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of T){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),ue=f((e,t)=>{let n=D(e.lat),r=D(t.lat),i=r-n,a=D(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),de=f((e,t)=>{let n=O(e,t);return Math.hypot(n[0],n[2])}),O=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(D(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),k=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(D(e.lat)))+e.lon}}),fe=f((e,t=0)=>ae(e,t)),A=e=>{let{lat:t,lon:n}=ie(e);return`${t}%2C${n}`},pe=f(e=>`https://www.google.com/maps/search/?api=1&query=${A(e)}`),me=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${A(t)}&destination=${A(e)}`),he=f((e,t=19)=>{let{lat:n,lon:r}=ie(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),ge=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();o.transformMat4(s,a,r);let{lat:c,lon:l}=k(n,s);return{lat:c,lon:l,altitude:s[1]}}),_e=()=>({points:[]}),ve=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),j=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),M=e=>o.fromValues(e[0],e[1],e[2]),N=e=>a.fromValues(e[0],e[1],e[2],e[3]),ye=e=>i.fromValues(...e),P=e=>[e[0],e[1],e[2]],F=e=>[e[0],e[1],e[2],e[3]],I=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],L=e=>[e[0],e[1],e[2]],be=e=>[e[0],e[1],e[2],e[3]],xe=e=>[...e],R=Object.freeze([0,0,0,1]),z=Object.freeze([0,0,0]),B=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),V=e=>{let t=Ce(e);if(t===0)return R;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},Se=e=>{for(let t=0;t<16;t++)if(e[t]!==B[t])return!1;return!0},Ce=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),we=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,Te=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,Ee=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},De=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},Oe=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},H=e=>[-e[2],e[1],e[0]],ke=e=>[e[2],e[1],-e[0]],U=e=>[-e[2],e[1],e[0],e[3]],Ae=e=>[e[2],e[1],-e[0],e[3]],W=e=>[e[1],e[2],e[0],e[3]],je=e=>[e[2],e[0],e[1],e[3]],G=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},Me=t({name:`arElements`,initialState:j(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(L(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=ye(r),l=s.arPoints.map(e=>{let t=M(e);return k(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>j()}}),{recordPhoneHeight:Ne,addOrUpdateArPlane:Pe,startEventArea:Fe,addCornerToEventArea:Ie,correctArDriftForEventArea:Le,finishEventArea:Re,anchorEventAreaInWorldSpace:ze,resetArElements:Be}=Me.actions,Ve=p(Ne),He=p(Pe),Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=p(Be),Ye=m(Me.reducer);function Xe(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),K(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),K(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function Ze(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function Qe(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Xe(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new $e(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return Ze(i.create(),d,f,m,p,c,h)}var $e=class e{ignoreYAxis;static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),K(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const K=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function et(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return rt(s,tt(s,t,n,r,i,a,o),i,a)}function tt(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){it(e,n,o,c,l);let{inliers:r,outliers:u}=nt(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=q(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function nt(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function rt(e,t,n,r){let i=t??n(e);q(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=nt(e,null,i,r);return a.length>0&&(i=n(a),q(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function q(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function it(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const at={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:2500,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.1,altitudeAlignmentMode:`separate1D`,altitudeAggregator:`mean`,altitudeSampleSet:`all`,gpsVerticalAccuracyExponent:5},ot=e=>e.map(e=>L(e)),st=e=>e.map(e=>V(e)),ct=e=>e.map(Nt),lt=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),J=e=>Array.isArray(e)?e:P(e),Y=e=>Array.isArray(e)?e:F(e),ut=(e,t,n,r)=>({odometryPosOffset:L(J(e)),odometryRotOffset:V(Y(t)),latestLoopClosureFixPointPos:n?L(J(n)):null,latestLoopClosureFixPointRot:r?V(Y(r)):null}),dt=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...B],alignmentRotation:[...R],alignmentTranslation:[...z],alignmentRotationInDegree:[...z],...ut(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null}),ft=()=>dt(z,R,null,null),pt=(e,t,n,r)=>{let i=e.map(e=>J(e)),a=t.map(e=>V(Y(e))),o=n.map(Nt),s=r.gpsAccuracyExponent;for(let e of o)e.weight=X(e.latLongAccuracy,s);let c=o.map(jt),l=r.includeTimeWeight?Dt(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?Tt(o,r.recentSeconds):0,d=i.slice(u).map(M),f=o.slice(u);return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:d,solverGpsPoints:f,solverWeightedVec4Tuples:l.slice(u),solverVerticalWeights:Et(f,r)}},mt=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=z,odometryRotOffset:s=R,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...at,...u??{}};if(t.length===0)return dt(i,s,c,l);let{allPositionTuples:p,allRotationTuples:m,allGpsPoints:h,allVec4Tuples:g,solverPositionsTyped:_,solverGpsPoints:v,solverWeightedVec4Tuples:y,solverVerticalWeights:b}=pt(t,n,r,f),x=vt(_,y,b,f,d??Math.random),S=a.create(),ee=o.create(),C=o.create();gt(x.matrix,S,ee,C);let{mean:w,median:T}=kt(h),E=Ot(r,f.geohashPrecision,f.geohashWindowSize);return{odometryPositions:ot(p),odometryRotations:st(m),gpsPositions:ct(h),gpsPositionsVec4:lt(g),alignmentMatrix:I(x.matrix),alignmentRotation:F(S),alignmentTranslation:P(ee),alignmentRotationInDegree:P(C),...ut(i,s,c,l),gpsAccuracyMedian:T,gpsAccuracyMean:w,currentGpsPosGeoHash:E}},X=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,ht=(e,t,n,r,i,s)=>{let c=e.odometryPositions,l=e.odometryRotations,u=e.gpsPositions,d=e.gpsPositionsVec4;c.push(J(t)),l.push(V(Y(n)));let f=Nt(r);u.push(f);let p={...at,...i??{}};f.weight=X(f.latLongAccuracy,p.gpsAccuracyExponent);let m=jt(f);if(p.includeTimeWeight){d.push(m);let e=Dt(d,u,p.weightByTimeFactor);for(let t=0;t<e.length;t++)d[t]=e[t]}else d.push(m);let h=0;p.useOnlyRecentData&&(h=Tt(u,p.recentSeconds));let g=vt(c.slice(h).map(M),d.slice(h),Et(u.slice(h),p),p,s??Math.random);e.alignmentMatrix=I(g.matrix);let _=a.create(),v=o.create(),y=o.create();gt(g.matrix,_,v,y),e.alignmentRotation=F(_),e.alignmentTranslation=P(v),e.alignmentRotationInDegree=P(y);let{mean:b,median:x}=kt(u);e.gpsAccuracyMean=b,e.gpsAccuracyMedian=x,e.currentGpsPosGeoHash=Ot(u,p.geohashPrecision,p.geohashWindowSize)},gt=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),At(t,r)},Z=o.create(),_t=o.create(),vt=(e,t,n,r,a)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let o=e.length,c=[];for(let r=0;r<o;r+=1){let i=t[r];c.push({odom:e[r],ref:s.fromValues(i[0],i[1],i[2],i[3]),verticalWeight:n[r]??1})}let l,u,d;if(r.useRansac&&c.length>=3){let e=c.length,t=Math.max(3,Math.min(e,Math.ceil(e*r.ransacSampleRatio))),n=et(c,Math.max(t,Math.min(e,Math.ceil(e*r.ransacInlierRatio))),t,r.ransacMaxIterations,e=>St(e,r.ignoreYAxisForRotation),(e,t)=>Ct(e,t)<e.meanAlignmentError+r.ransacErrorTolerance,a);l=n.alignmentMatrix,u=n.meanAlignmentError,n.inliers!==void 0&&(d=Array.from(n.inliers))}else{let e=St(c,r.ignoreYAxisForRotation);l=e.alignmentMatrix,u=e.meanAlignmentError}return r.altitudeAlignmentMode===`separate1D`&&yt(l,c,d,r),{matrix:l,meanError:u}},yt=(e,t,n,r)=>{let i=r.altitudeSampleSet===`horizontalInliers`&&n!==void 0&&n.length>0?n:t;if(i.length===0)return;let a=Array(i.length),o=Array(i.length);for(let e=0;e<i.length;e+=1){let t=i[e];a[e]=t.ref[1]-t.odom[1];let n=t.verticalWeight;o[e]=n>0&&Number.isFinite(n)?n:0}e[13]=r.altitudeAggregator===`median`?xt(a,o):bt(a,o)},bt=(e,t)=>{let n=0,r=0;for(let i=0;i<e.length;i+=1)n+=e[i]*t[i],r+=t[i];if(r>0&&Number.isFinite(r))return n/r;let i=0;for(let t=0;t<e.length;t+=1)i+=e[t];return e.length===0?0:i/e.length},xt=(e,t)=>{if(e.length===0)return 0;if(e.length===1)return e[0];let n=e.map((e,t)=>t);n.sort((t,n)=>e[t]-e[n]);let r=0;for(let e=0;e<t.length;e+=1)r+=t[e];if(!(r>0)||!Number.isFinite(r)){let t=n.map(t=>e[t]);return t[Math.floor((t.length-1)/2)]}let i=r/2,a=0;for(let r=0;r<n.length;r+=1)if(a+=t[n[r]],a>=i)return e[n[r]];return e[n[n.length-1]]},St=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=Qe(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(Z,i[e],s),c+=wt(Z,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},Ct=(e,t)=>(o.transformMat4(Z,t.odom,e.alignmentMatrix),wt(Z,t.ref)),wt=(e,t)=>(o.set(_t,t[0],t[1],t[2]),o.squaredDistance(e,_t)),Tt=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},Et=(e,t)=>{let n=t.gpsVerticalAccuracyExponent,r=e.map(e=>{let t=e.altitudeAccuracy;return X(t!=null&&Number.isFinite(t)&&t>0?t:e.latLongAccuracy,n)});if(!t.includeTimeWeight||e.length<2)return r;let i=e[e.length-1].timestamp,a=e[0].timestamp,o=Math.max(1,i-a),s=t.weightByTimeFactor;return r.map((t,n)=>{if(!(t>0&&Number.isFinite(t)))return 0;let r=s*((i-e[n].timestamp)/o)+1;return 1/(1/t+r)})},Dt=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight;if(!(s>0&&Number.isFinite(s)))return[e[0],e[1],e[2],0];let c=1/(1/s+o);return[e[0],e[1],e[2],c]})},Q=new Map,Ot=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Q.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=ce(Mt(r),t),a=Q.get(i);a?(a.count+=1,a.index=n):Q.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Q)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},kt=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},At=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},jt=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],Mt=e=>({lat:e.latitude,lon:e.longitude}),Nt=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?V(Y(e.deviceRotation)):void 0}),Pt=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},Ft=o.fromValues(1,1,1),It=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],Lt=(e,t,n)=>{let r=O(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),i=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?W(G(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:P(r),weight:1,deviceRotation:i}},Rt=(e,t,n)=>{if(e)return G(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},zt=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},Bt=e=>{let t=zt(a.normalize(a.create(),N(e.lastSensorRot))),n=a.normalize(a.create(),N(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),N(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),N(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),F(c)},Vt=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),N(t)),M(e),Ft),n},Ht=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},Ut=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=Ht(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},Wt=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},Gt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],Kt=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],qt=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:ft(),odometryPath:_e()}:e,prepare:e=>(Pt(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a}=t.payload,o=Lt(i,e.zero,a);ht(e.gpsEvents,H(n),U(r),o)},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=It(e.gpsEvents.odometryPosOffset,H(t.payload.lastValidOdomPos)),r=Rt(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Rt(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(mt({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:Bt({...t.payload,lastValidOdomRot:U(t.payload.lastValidOdomRot),newOdomRot:U(t.payload.newOdomRot),lastSensorRot:W(r),newSensorRot:W(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?Wt(n.odometryPositions,n.latestLoopClosureFixPointPos,Gt):0,s=n.latestLoopClosureFixPointRot?Wt(n.odometryRotations,n.latestLoopClosureFixPointRot,Kt):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>L(e)),u=n.odometryRotations.slice(0,r).map(e=>V(e)),d=n.odometryPositions.slice(r).map(e=>L(e)),f=n.odometryRotations.slice(r).map(e=>V(e)),p=d.map((e,t)=>Vt(e,f[t])),m=Vt(H(t.payload.lastPos),V(U(t.payload.lastRot)));p.push(m);let h=Ut(p,Vt(H(t.payload.newPos),V(U(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(P(n)),_.push(F(r))}e.gpsEvents=c(mt({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:H(t.payload.position),rotation:V(U(t.payload.rotation)),capturedAt:t.payload.capturedAt,width:t.payload.width,height:t.payload.height}))}}}),{setZeroPos:Jt,recordGpsEvent:Yt,odometryTrackingRestarted:Xt,arLoopClosureDetected:Zt,add2dImage:Qt}=qt.actions,$t=p(Jt),en=p(Yt),tn=p(Xt),nn=p(Zt),rn=p(Qt),an=m(qt.reducer),on=t({name:`gpsElements`,initialState:ve(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>ve()}}),{addMarker:sn,addLine:cn,addArea:ln,addToHeatMaps:un,addHeatMapArea:dn,resetGpsElements:fn}=on.actions,pn=p(sn),mn=p(cn),hn=p(ln),gn=p(un),_n=p(dn),vn=p(fn),yn=m(on.reducer),bn=()=>({gpsData:null,gpsElements:ve(),arElements:j()}),xn=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},Sn=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),$=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return Sn(e)?xn(e):e.map(e=>$(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=$(i,t+1);return n}return e},Cn=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=bn(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:an,gpsElements:yn,arElements:Ye},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:$,stateSanitizer:$}:!0})},wn=[],Tn=[],En=[],Dn={median:null,mean:null},On=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),kn=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),An=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??wn}),jn=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??Tn}),Mn=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??En}),Nn=f(function(e){return e.gpsData?.zero??null}),Pn=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?Dn:{median:t,mean:n}}),Fn=`1.0.0`;export{B as IDENTITY_MATRIX4,R as IDENTITY_QUATERNION,Fn as LIB_VERSION,z as ZERO_VECTOR3,rn as add2dImage,hn as addArea,We as addCornerToEventArea,_n as addHeatMapArea,mn as addLine,pn as addMarker,He as addOrUpdateArPlane,gn as addToHeatMaps,qe as anchorEventAreaInWorldSpace,Ye as arElementsReducer,nn as arLoopClosureDetected,ce as calcGeoHash,k as calcGpsCoords,oe as calcQuadKey,O as calcRelativeCoordsInMeters,xe as cloneMatrix4,be as cloneQuaternion,L as cloneVector3,Ge as correctArDriftForEventArea,Cn as createGpsSlamStore,ue as distanceInMeters,de as distanceInMetersRelative,W as enuQuaternionToNUE,G as eulerToQuaternion,Ke as finishEventArea,ye as fromMatrix4,N as fromQuaternion,M as fromVector3,ge as fusedGpsFromOdom,le as geoHashToLatLong,On as getAlignmentMatrix,kn as getAlignmentRotation,me as getGoogleMapsDirectionsLink,pe as getGoogleMapsLink,Pn as getGpsAccuracyStats,Mn as getGpsPositions,An as getOdometryPositions,jn as getOdometryRotations,he as getOpenStreetMapLink,Nn as getZeroReference,an as gpsDataReducer,yn as gpsElementsReducer,Oe as invertQuaternion,Se as isIdentityMatrix4,we as isIdentityQuaternion,Te as isNearIdentityQuaternion,De as multiplyQuaternions,V as normalizeQuaternion,je as nueQuaternionToENU,Ae as nueQuaternionToWebXR,ke as nueToWebXR,tn as odometryTrackingRestarted,se as quadKeyToLatLong,Ce as quaternionMagnitude,Ee as quaternionsEquivalent,en as recordGpsEvent,Ve as recordPhoneHeight,Je as resetArElements,vn as resetGpsElements,$ as sanitizeForDevTools,$t as setZeroPos,Ue as startEventArea,fe as toEarthCenteredCoordinates,I as toMatrix4,F as toQuaternion,P as toVector3,g as validateLicenseKey,U as webxrQuaternionToNUE,H as webxrToNUE};
|
|
2
|
+
import{configureStore as e,createSlice as t}from"@reduxjs/toolkit";import{ed25519 as n}from"@noble/curves/ed25519.js";import{hexToBytes as r}from"@noble/curves/utils.js";import{mat4 as i,quat as a,vec3 as o,vec4 as s}from"gl-matrix";import{castDraft as c}from"immer";let l=!1;function u(){l=!0}function d(){if(!l)throw Error(`gps-plus-slam-js: license not activated. Construct your store via createRecorderStore() from gps-plus-slam-app-framework, or pass a valid licenseKey to createGpsSlamStore(). See EULA §3.`)}function f(e){return((...t)=>(d(),e(...t)))}function p(e){let t=((...t)=>(d(),e(...t)));return Object.assign(t,e),t.toString=()=>e.type,t}function m(e){return(t,n)=>(d(),e(t,n))}function h(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=`=`.repeat((4-t.length%4)%4),r=atob(t+n);return Uint8Array.from(r,e=>e.charCodeAt(0))}function g(e){let t=new Date,i=e.indexOf(`.`);if(i<=0||i>=e.length-1)throw Error(`Invalid license key format: expected "payload.signature" with base64url-encoded parts.`);let a,o;try{a=h(e.substring(0,i)),o=h(e.substring(i+1))}catch{throw Error(`Invalid license key format: base64url decoding failed.`)}let s=r(`dfe5c62120b7a0ce962b17907a15b27cf1b056e6619bf6ce55e6e094b322a470`);if(!n.verify(o,a,s))throw Error(`Invalid license key: signature verification failed.`);let c=JSON.parse(new TextDecoder().decode(a));if(typeof c!=`object`||!c)throw Error(`Invalid license key: payload must be a JSON object.`);let{type:l,exp:d}=c;if(l!==`community`)throw Error(`Invalid license key: "type" must be "community".`);if(typeof d!=`number`||!Number.isFinite(d))throw Error(`Invalid license key: "exp" must be a finite number (Unix timestamp in seconds).`);if(d<Math.floor(t.getTime()/1e3)){let e=new Date(d*1e3);throw Error(`License key expired on ${e.toISOString()}. Update to the latest version for a renewed community key.`)}return u(),{type:l,expiresAt:new Date(d*1e3)}}const _=180/Math.PI,v=e=>{let t=e[0],n=e[1],r=e[2],i=e[3],a=2*(i*t+n*r),o=1-2*(t*t+n*n),s=Math.atan2(a,o)*_,c=2*(i*n-r*t),l=Math.abs(c)>=1?Math.sign(c)*90:Math.asin(c)*_,u=2*(i*r+t*n),d=1-2*(n*n+r*r);return{pitch:l,yaw:Math.atan2(u,d)*_,roll:s}},y=1/298.257223563,b=y*(2-y),x=40075016.6856/360,S=39940652.7422/360,C=Math.PI/180,w=180/Math.PI,T=`0123456789bcdefghjkmnpqrstuvwxyz`,E=[16,8,4,2,1],D=(e,t,n)=>Math.min(Math.max(e,t),n),ee=e=>D(e,-180,180),O=e=>e*C,k=e=>e*w,A=e=>{if(!Number.isFinite(e))throw RangeError(`Coordinate values must be finite numbers`);return String(e)},j=e=>({lat:A(e.lat),lon:A(e.lon)}),te=(e,t=0)=>{let n=O(e.lat),r=O(e.lon),i=Math.sin(n),a=Math.cos(n),s=Math.sin(r),c=Math.cos(r),l=6378137/Math.sqrt(1-b*i*i),u=(l+t)*a*c,d=(l+t)*a*s,f=(l*(1-b)+t)*i;return o.fromValues(u,d,f)},ne=f((e,t)=>{if(t<1||t>23)throw RangeError(`levelOfDetail must be between 1 and 23 (inclusive)`);let n=D(e.lat,-85.05112878,85.05112878),r=ee(e.lon),i=Math.sin(O(n)),a=256*2**t,o=(r+180)/360*a,s=(.5-Math.log((1+i)/(1-i))/(4*Math.PI))*a,c=2**t-1,l=D(Math.floor(o/256),0,c),u=D(Math.floor(s/256),0,c),d=``;for(let e=t;e>0;--e){let t=0,n=1<<e-1;(l&n)!==0&&(t+=1),(u&n)!==0&&(t+=2),d+=t.toString()}return d}),re=f(e=>{if(!/^[0-3]+$/.test(e))throw Error(`QuadKey must consist of digits 0-3`);let t=e.length;if(t===0)throw Error(`QuadKey must not be empty`);let n=0,r=0;for(let i=0;i<t;i+=1){let a=Number.parseInt(e[i],10),o=1<<t-i-1;a&1&&(n+=o),a&2&&(r+=o)}let i=256*2**t,a=(n+.5)*256,o=(r+.5)*256,s=a/i,c=o/i,l=s*360-180;return{lat:k(Math.atan(Math.sinh(Math.PI*(1-2*c)))),lon:l}}),ie=f((e,t)=>{if(t<=0)throw RangeError(`precision must be greater than 0`);let n=-90,r=90,i=-180,a=180,o=``,s=0,c=0,l=!0;for(;o.length<t;){if(l){let t=(i+a)/2;e.lon>=t?(c|=E[s],i=t):a=t}else{let t=(n+r)/2;e.lat>=t?(c|=E[s],n=t):r=t}l=!l,s<4?s+=1:(o+=T[c],s=0,c=0)}return o}),ae=f(e=>{if(e.length===0)throw Error(`geoHash must not be empty`);let t=-90,n=90,r=-180,i=180,a=!0;for(let o of e){let e=T.indexOf(o);if(e===-1)throw Error(`Invalid geohash character: ${o}`);for(let o of E){if(a){let t=(r+i)/2;(e&o)===0?i=t:r=t}else{let r=(t+n)/2;(e&o)===0?n=r:t=r}a=!a}}return{lat:(t+n)/2,lon:(r+i)/2}}),oe=f((e,t)=>{let n=O(e.lat),r=O(t.lat),i=r-n,a=O(t.lon-e.lon),o=Math.sin(i/2),s=Math.sin(a/2),c=o*o+Math.cos(n)*Math.cos(r)*s*s,l=Math.min(1,Math.max(0,c));return 6371008.8*(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)))}),se=f((e,t)=>{let n=ce(e,t);return Math.hypot(n[0],n[2])}),ce=f((e,t,n=0,r=0)=>{let i=(t.lon-e.lon)*x*Math.cos(O(e.lat)),a=(t.lat-e.lat)*S,s=n-r;return o.fromValues(a,s,i)}),le=f((e,t)=>{let n=t[0],r=t[2];return{lat:n/S+e.lat,lon:r/(x*Math.cos(O(e.lat)))+e.lon}}),ue=f((e,t=0)=>te(e,t)),de=e=>{let{lat:t,lon:n}=j(e);return`${t}%2C${n}`},fe=f(e=>`https://www.google.com/maps/search/?api=1&query=${de(e)}`),pe=f((e,t)=>`https://www.google.com/maps/dir/?api=1&origin=${de(t)}&destination=${de(e)}`),me=f((e,t=19)=>{let{lat:n,lon:r}=j(e);return`https://www.openstreetmap.org/query?lat=${n}&lon=${r}#map=${t}/${n}/${r}`}),he=f((e,t,n)=>{let r=i.fromValues(...e),a=o.fromValues(t[0],t[1],t[2]),s=o.create();o.transformMat4(s,a,r);let{lat:c,lon:l}=le(n,s);return{lat:c,lon:l,altitude:s[1]}}),ge=()=>({points:[]}),_e=()=>({gpsMarkers:[],gpsLines:[],areas:[],heatMap:{},heatAreas:{}}),ve=()=>({arPlanes:{},floorDetections:[],eventAreas:[],currentEventAreaId:null,currentEventAreaPoints:[]}),M=e=>o.fromValues(e[0],e[1],e[2]),N=e=>a.fromValues(e[0],e[1],e[2],e[3]),ye=e=>i.fromValues(...e),P=e=>[e[0],e[1],e[2]],F=e=>[e[0],e[1],e[2],e[3]],I=e=>[e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]],L=e=>[e[0],e[1],e[2]],be=e=>[e[0],e[1],e[2],e[3]],xe=e=>[...e],R=Object.freeze([0,0,0,1]),z=Object.freeze([0,0,0]),Se=Object.freeze([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),B=e=>{let t=we(e);if(t===0)return R;let n=1/t;return[e[0]*n,e[1]*n,e[2]*n,e[3]*n]},Ce=e=>{for(let t=0;t<16;t++)if(e[t]!==Se[t])return!1;return!0},we=e=>Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]+e[3]*e[3]),Te=e=>e[0]===0&&e[1]===0&&e[2]===0&&e[3]===1,Ee=(e,t)=>Math.abs(e[0])<t&&Math.abs(e[1])<t&&Math.abs(e[2])<t&&Math.abs(e[3]-1)<t,De=(e,t,n=1e-10)=>{let r=Math.abs(e[0]-t[0])<n&&Math.abs(e[1]-t[1])<n&&Math.abs(e[2]-t[2])<n&&Math.abs(e[3]-t[3])<n,i=Math.abs(e[0]+t[0])<n&&Math.abs(e[1]+t[1])<n&&Math.abs(e[2]+t[2])<n&&Math.abs(e[3]+t[3])<n;return r||i},Oe=(e,t)=>{let n=a.create();return a.multiply(n,e,t),[n[0],n[1],n[2],n[3]]},ke=e=>{let t=a.create();return a.invert(t,e),[t[0],t[1],t[2],t[3]]},V=e=>[-e[2],e[1],e[0]],Ae=e=>[e[2],e[1],-e[0]],H=e=>[-e[2],e[1],e[0],e[3]],je=e=>[e[2],e[1],-e[0],e[3]],U=e=>[e[1],e[2],e[0],e[3]],Me=e=>[e[2],e[0],e[1],e[3]],Ne=(e,t,n)=>{if(!Number.isFinite(e)||!Number.isFinite(t)||!Number.isFinite(n))throw Error(`eulerToQuaternion: non-finite input (alpha=${e}, beta=${t}, gamma=${n})`);let r=e*Math.PI/180,i=t*Math.PI/180,o=n*Math.PI/180,s=a.create(),c=a.create(),l=a.create();a.setAxisAngle(s,[0,0,1],r),a.setAxisAngle(c,[1,0,0],i),a.setAxisAngle(l,[0,1,0],o);let u=a.create();return a.multiply(u,s,c),a.multiply(u,u,l),[u[0],u[1],u[2],u[3]]},Pe=t({name:`arElements`,initialState:ve(),reducers:{recordPhoneHeight:(e,t)=>{e.floorDetections.push(t.payload)},addOrUpdateArPlane:(e,t)=>{let n=t.payload;if(n.parentPlaneId){delete e.arPlanes[n.planeId];return}e.arPlanes[n.planeId]=c(n)},startEventArea:(e,t)=>{e.currentEventAreaId=t.payload.id,e.currentEventAreaPoints=[]},addCornerToEventArea:(e,t)=>{e.currentEventAreaId&&e.currentEventAreaPoints.push(c(L(t.payload.position)))},correctArDriftForEventArea:(e,t)=>{let n=t.payload.drift;for(let t of e.currentEventAreaPoints)t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]},finishEventArea:(e,t)=>{e.currentEventAreaId&&(e.eventAreas.push({id:e.currentEventAreaId,label:t.payload.label,polygon:[],isInArSpace:!0,arPoints:e.currentEventAreaPoints}),e.currentEventAreaId=null,e.currentEventAreaPoints=[])},anchorEventAreaInWorldSpace:(e,t)=>{let{areaId:n,alignmentMatrix:r,zeroRef:i}=t.payload,a=e.eventAreas.findIndex(e=>e.id===n);if(a===-1)return;let s=e.eventAreas[a];if(!s.isInArSpace||!s.arPoints)throw Error(`The area ${s.id} is already anchored in world space`);let c=ye(r),l=s.arPoints.map(e=>{let t=M(e);return le(i,o.transformMat4(o.create(),t,c))});e.eventAreas[a]={...s,isInArSpace:!1,polygon:l,arPoints:void 0}},resetArElements:()=>ve()}}),{recordPhoneHeight:Fe,addOrUpdateArPlane:Ie,startEventArea:Le,addCornerToEventArea:Re,correctArDriftForEventArea:ze,finishEventArea:Be,anchorEventAreaInWorldSpace:Ve,resetArElements:He}=Pe.actions,Ue=p(Fe),We=p(Ie),Ge=p(Le),Ke=p(Re),qe=p(ze),Je=p(Be),Ye=p(Ve),Xe=p(He),Ze=m(Pe.reducer);function Qe(e,t,n,r){let{inCentroid:i,refCentroid:a,refVec:s,tmp:c}=r;o.set(i,0,0,0),o.set(a,0,0,0);let l=0;for(let n=0;n<e.length;n++){let r=t[n][3];o.scaleAndAdd(i,i,e[n],r),nt(s,t[n]),o.scaleAndAdd(a,a,s,r),l+=r}if(l===0)throw Error(`Weights must not sum to zero when solving Kabsch`);let u=1/l;if(o.scale(i,i,u),o.scale(a,a,u),!n)return{inCentroid:i,refCentroid:a,scaleRatio:1};let d=0,f=0;for(let n=0;n<e.length;n++)o.subtract(c,e[n],i),d+=o.length(c),nt(s,t[n]),o.subtract(c,s,a),f+=o.length(c);return{inCentroid:i,refCentroid:a,scaleRatio:d===0?1:f/d}}function $e(e,t,n,r,a,s,c){let{negCentroid:l,scaleVec:u}=c;return i.fromRotationTranslation(e,r,n),s&&(o.set(u,a,a,a),i.scale(e,e,u)),o.negate(l,t),i.translate(e,e,l),e}function et(e,t,n={}){let r=e.length;if(r!==t.length)throw Error(`Length of the point lists was not equal: ${r} vs ${t.length}`);if(r===0)return i.create();let s=n.solveRotation??!0,c=n.solveScale??!1,l=n.ignoreYAxisForRotation??!1,u=n.rotationIterations??9,{inCentroid:d,refCentroid:f,scaleRatio:p}=Qe(e,t,c,{inCentroid:o.create(),refCentroid:o.create(),refVec:o.create(),tmp:o.create()}),m=a.create();s?new tt(l).solve(e,t,d,f,m,u):a.identity(m);let h={negCentroid:o.create(),scaleVec:o.create()};return $e(i.create(),d,f,m,p,c,h)}var tt=class e{ignoreYAxis;static twoPi=Math.PI*2;static basisX=o.fromValues(1,0,0);static basisY=o.fromValues(0,1,0);static basisZ=o.fromValues(0,0,1);constructor(e){this.ignoreYAxis=e}solve(t,n,r,i,o,s){let c=this.transposeMultSubtract(t,n,r,i);if(this.ignoreYAxis){let t=c[0][0],n=c[0][2],r=c[2][0],i=c[2][2],s=Math.atan2(r-n,t+i);a.setAxisAngle(o,e.basisY,s)}else this.extractRotation(c,o,s)}transposeMultSubtract(e,t,n,r){let i=[o.create(),o.create(),o.create()],a=o.clone(n),s=o.clone(r);this.ignoreYAxis&&(a[1]=0,s[1]=0);let c=o.create(),l=o.create(),u=o.create();for(let n=0;n<e.length;n++){o.copy(c,e[n]),nt(u,t[n]),this.ignoreYAxis&&(c[1]=0,u[1]=0);let r=t[n][3];o.subtract(c,c,a),o.scale(c,c,r),o.subtract(l,u,s),i[0][0]+=c[0]*l[0],i[1][0]+=c[1]*l[0],i[2][0]+=c[2]*l[0],i[0][1]+=c[0]*l[1],i[1][1]+=c[1]*l[1],i[2][1]+=c[2]*l[1],i[0][2]+=c[0]*l[2],i[1][2]+=c[1]*l[2],i[2][2]+=c[2]*l[2]}return i}extractRotation(t,n,r){let i=[o.create(),o.create(),o.create()],s=o.create(),c=o.create(),l=o.create();for(let u=0;u<r;u++){this.fillMatrixFromQuaternion(n,i),o.cross(s,i[0],t[0]),o.cross(l,i[1],t[1]),o.add(s,s,l),o.cross(l,i[2],t[2]),o.add(s,s,l);let r=o.dot(i[0],t[0])+o.dot(i[1],t[1])+o.dot(i[2],t[2])+1e-9;r=Math.abs(r),o.scale(s,s,1/r);let u=o.length(s);if(u<1e-9)break;o.scale(c,s,1/u);let d=u%e.twoPi,f=a.create();a.setAxisAngle(f,c,d),a.multiply(n,f,n),a.normalize(n,n)}}fillMatrixFromQuaternion(t,n){o.transformQuat(n[0],e.basisX,t),o.transformQuat(n[1],e.basisY,t),o.transformQuat(n[2],e.basisZ,t)}};const nt=(e,t)=>(e[0]=t[0],e[1]=t[1],e[2]=t[2],e);function rt(e,t,n,r,i,a,o=Math.random){let s=Array.from(e);if(n>s.length)throw RangeError(`minSampleSize must be smaller than the number of elements: minSampleSize=${n}, elements=${s.length}`);return ot(s,it(s,t,n,r,i,a,o),i,a)}function it(e,t,n,r,i,a,o){let s=null,c=Array(n),l=new Uint8Array(e.length);for(let u=0;u<r;u+=1){ct(e,n,o,c,l);let{inliers:r,outliers:u}=at(e,l,i(c),a);if(s===null||n+r.length>=t){let e=[...c,...r],t=i(e),n=st(t,`createModel must assign totalModelError for comparison during RANSAC`);(s===null||n<s.error)&&(t.inliers=e,t.outliers=u,s={model:t,error:n})}}return s?.model??null}function at(e,t,n,r){let i=[],a=[];for(let o=0;o<e.length;o+=1){if(t?.[o])continue;let s=e[o];(r(n,s)?i:a).push(s)}return{inliers:i,outliers:a}}function ot(e,t,n,r){let i=t??n(e);st(i,`RANSAC failed to produce a model with totalModelError`);let{inliers:a,outliers:o}=at(e,null,i,r);return a.length>0&&(i=n(a),st(i,`createModel must assign totalModelError for final RANSAC model`)),i.inliers=a,i.outliers=o,i}function st(e,t){let{totalModelError:n}=e;if(n==null)throw Error(t);return n}function ct(e,t,n,r,i){i.fill(0);let a=0;for(;a<t;){let t=Math.floor(n()*e.length);i[t]||(i[t]=1,r[a]=e[t],a+=1)}}const lt=.08,ut=180/Math.PI,dt=e=>(e%360+360)%360,ft=(e,t)=>{let[n,r,i,a]=e,[o,s,c]=t,l=2*(r*c-i*s),u=2*(i*o-n*c),d=2*(n*s-r*o);return[o+a*l+(r*d-i*u),s+a*u+(i*l-n*d),c+a*d+(n*u-r*l)]};function pt(e){let[t,n,r,i]=e,a=-2*(t*r+i*n),o=-2*(n*r-i*t);return Math.hypot(a,o)<lt?null:dt(Math.atan2(a,o)*ut)}const mt=[0,0,-1];function ht(e,t){return Oe(e,ke(t))}function gt(e,t){let[n,r]=ft(ht(e,t),mt);return Math.hypot(n,r)<lt?null:dt(Math.atan2(n,r)*ut)}function W(e,t){let n=((e-t+180)%360+360)%360-180;return n===-180&&(n=180),n}const _t=Math.PI/180,vt=180/Math.PI,yt=2025,bt=2025,xt=2030,St=6378137;St*(1-1/298.257223563);const Ct=[[1,0,-29351.8,0,12,0],[1,1,-1410.8,4545.4,9.7,-21.5],[2,0,-2556.6,0,-11.6,0],[2,1,2951.1,-3133.6,-5.2,-27.7],[2,2,1649.3,-815.1,-8,-12.1],[3,0,1361,0,-1.3,0],[3,1,-2404.1,-56.6,-4.2,4],[3,2,1243.8,237.5,.4,-.3],[3,3,453.6,-549.5,-15.6,-4.1],[4,0,895,0,-1.6,0],[4,1,799.5,278.6,-2.4,-1.1],[4,2,55.7,-133.9,-6,4.1],[4,3,-281.1,212,5.6,1.6],[4,4,12.1,-375.6,-7,-4.4],[5,0,-233.2,0,.6,0],[5,1,368.9,45.4,1.4,-.5],[5,2,187.2,220.2,0,2.2],[5,3,-138.7,-122.9,.6,.4],[5,4,-142,43,2.2,1.7],[5,5,20.9,106.1,.9,1.9],[6,0,64.4,0,-.2,0],[6,1,63.8,-18.4,-.4,.3],[6,2,76.9,16.8,.9,-1.6],[6,3,-115.7,48.8,1.2,-.4],[6,4,-40.9,-59.8,-.9,.9],[6,5,14.9,10.9,.3,.7],[6,6,-60.7,72.7,.9,.9],[7,0,79.5,0,-0,0],[7,1,-77,-48.9,-.1,.6],[7,2,-8.8,-14.4,-.1,.5],[7,3,59.3,-1,.5,-.8],[7,4,15.8,23.4,-.1,0],[7,5,2.5,-7.4,-.8,-1],[7,6,-11.1,-25.1,-.8,.6],[7,7,14.2,-2.3,.8,-.2],[8,0,23.2,0,-.1,0],[8,1,10.8,7.1,.2,-.2],[8,2,-17.5,-12.6,0,.5],[8,3,2,11.4,.5,-.4],[8,4,-21.7,-9.7,-.1,.4],[8,5,16.9,12.7,.3,-.5],[8,6,15,.7,.2,-.6],[8,7,-16.8,-5.2,-0,.3],[8,8,.9,3.9,.2,.2],[9,0,4.6,0,-0,0],[9,1,7.8,-24.8,-.1,-.3],[9,2,3,12.2,.1,.3],[9,3,-.2,8.3,.3,-.3],[9,4,-2.5,-3.3,-.3,.3],[9,5,-13.1,-5.2,0,.2],[9,6,2.4,7.2,.3,-.1],[9,7,8.6,-.6,-.1,-.2],[9,8,-8.7,.8,.1,.4],[9,9,-12.9,10,-.1,.1],[10,0,-1.3,0,.1,0],[10,1,-6.4,3.3,0,0],[10,2,.2,0,.1,-0],[10,3,2,2.4,.1,-.2],[10,4,-1,5.3,-0,.1],[10,5,-.6,-9.1,-.3,-.1],[10,6,-.9,.4,0,.1],[10,7,1.5,-4.2,-.1,0],[10,8,.9,-3.8,-.1,-.1],[10,9,-2.7,.9,-0,.2],[10,10,-3.9,-9.1,-0,-0],[11,0,2.9,0,0,0],[11,1,-1.5,0,-0,-0],[11,2,-2.5,2.9,0,.1],[11,3,2.4,-.6,0,-0],[11,4,-.6,.2,0,.1],[11,5,-.1,.5,-.1,-0],[11,6,-.6,-.3,0,-0],[11,7,-.1,-1.2,-0,.1],[11,8,1.1,-1.7,-.1,-0],[11,9,-1,-2.9,-.1,0],[11,10,-.2,-1.8,-.1,0],[11,11,2.6,-2.3,-.1,0],[12,0,-2,0,0,0],[12,1,-.2,-1.3,0,-0],[12,2,.3,.7,-0,0],[12,3,1.2,1,-0,-.1],[12,4,-1.3,-1.4,-0,.1],[12,5,.6,-0,-0,-0],[12,6,.6,.6,.1,-0],[12,7,.5,-.1,-0,-0],[12,8,-.1,.8,0,0],[12,9,-.4,.1,0,-0],[12,10,-.2,-1,-.1,-0],[12,11,-1.3,.1,-0,0],[12,12,-.7,.2,-.1,-.1]];function wt(){let e=()=>Array.from({length:13},()=>Array(13).fill(0)),t={g:e(),h:e(),gdot:e(),hdot:e()};for(let[e,n,r,i,a,o]of Ct)t.g[e][n]=r,t.h[e][n]=i,t.gdot[e][n]=a,t.hdot[e][n]=o;return t}const G=wt();function Tt(){let e=Array(169).fill(0),t=Array(169).fill(0),n=Array(13).fill(0),r=Array(13).fill(0),i=Array(169).fill(0),a=Array(169).fill(0),o=(e,t)=>e*13+t;for(let e=1;e<=12;e++)for(let t=0;t<=e;t++){let n=G.g[e][t],r=G.h[e][t],s=G.gdot[e][t],c=G.hdot[e][t];i[o(t,e)]=n,a[o(t,e)]=s,t!==0&&(i[o(e,t-1)]=r,a[o(e,t-1)]=c)}e[o(0,0)]=1;for(let s=1;s<=12;s++){e[o(0,s)]=e[o(0,s-1)]*(2*s-1)/s;let c=2;for(let n=0,r=(s-n+1)/1;r>0;r--,n+=1){if(t[o(n,s)]=((s-1)*(s-1)-n*n)/((2*s-1)*(2*s-3)),n>0){let t=(s-n+1)*c/(s+n);e[o(n,s)]=e[o(n-1,s)]*Math.sqrt(t),c=1,i[o(s,n-1)]=e[o(n,s)]*i[o(s,n-1)],a[o(s,n-1)]=e[o(n,s)]*a[o(s,n-1)]}i[o(n,s)]=e[o(n,s)]*i[o(n,s)],a[o(n,s)]=e[o(n,s)]*a[o(n,s)]}n[s]=s+1,r[s]=s}return t[o(1,1)]=0,r[0]=0,{snorm:e,k:t,fn:n,fm:r,c:i,cd:a,idx:o}}const Et=Tt();function Dt(e){return e%4==0&&e%100!=0||e%400==0?366:365}function Ot(e){let t=e.getUTCFullYear(),n=Date.UTC(t,0,1);return t+Math.floor((e.getTime()-n)/864e5)/Dt(t)}let kt=!1;function At(e){return e===void 0?2027.5:typeof e==`number`?e>1e4?Ot(new Date(e)):e:Ot(e)}function jt(e,t,n){let r=St/1e3,i=6356752.314245179/1e3,a=r*r,o=i*i,s=a-o,c=a*a;c-o*o;let l=t*t,u=n*n,d=Math.sqrt(a-s*l),f=e*d,p=(f+a)/(f+o)*((f+a)/(f+o)),m=t/Math.sqrt(p*u+l),h=Math.sqrt(1-m*m),g=Math.sqrt(e*e+2*f+(c-22083078973482.75*l)/(d*d)),_=Math.sqrt(a*u+o*l);return{r:g,ct:m,st:h,ca:(e+_)/g,sa:s/_*t*n/g}}function Mt(e,t,n,r,i,a,o,s){if(i===a){e[r(a,i)]=o*e[r(a-1,i-1)],t[r(a,i)]=o*t[r(a-1,i-1)]+s*e[r(a-1,i-1)];return}if(i===1&&a===0){e[r(a,i)]=s*e[r(a,i-1)],t[r(a,i)]=s*t[r(a,i-1)]-o*e[r(a,i-1)];return}a>i-2&&(e[r(a,i-2)]=0,t[r(a,i-2)]=0),e[r(a,i)]=s*e[r(a,i-1)]-n[r(a,i)]*e[r(a,i-2)],t[r(a,i)]=s*t[r(a,i-1)]-o*e[r(a,i-1)]-n[r(a,i)]*t[r(a,i-2)]}function Nt(e,t,n,r){let i=r-yt;(r<2025||r>=2030)&&!kt&&(kt=!0,console.warn(`magneticDeclinationDeg: date ${r.toFixed(3)} is outside the WMM2025 validity window [${bt}, ${xt}); using linear secular extrapolation (accuracy degrades).`));let{snorm:a,k:o,fn:s,fm:c,c:l,cd:u,idx:d}=Et,f=t*_t,p=e*_t,m=Math.sin(f),h=Math.sin(p),g=Math.cos(f),_=Math.cos(p),v=Array(13).fill(0),y=Array(13).fill(0);v[0]=0,y[0]=1,v[1]=m,y[1]=g;let{r:b,ct:x,st:S,ca:C,sa:w}=jt(n,h,_);for(let e=2;e<=12;e++)v[e]=m*y[e-1]+g*v[e-1],y[e]=g*y[e-1]-m*v[e-1];let T=6371.2/b,E=T*T,D=0,ee=0,O=0,k=a.slice(),A=Array(169).fill(0);for(let e=1;e<=12;e++){E*=T;for(let t=0,n=(e+t+1)/1;n>0;n--,t+=1){Mt(k,A,o,d,e,t,S,x);let n=l[d(t,e)]+i*u[d(t,e)],r=E*k[d(t,e)],a,f;if(t===0)a=n*y[t],f=n*v[t];else{let r=l[d(e,t-1)]+i*u[d(e,t-1)];a=n*y[t]+r*v[t],f=n*v[t]-r*y[t]}D-=E*a*A[d(t,e)],ee+=c[t]*f*r,O+=s[e]*a*r}}let j;return j=S===0?0:ee/S,{x:-D*C-O*w,y:j,z:D*w-O*C}}function Pt(e){let t=e%360;return t<0?t+360:t}function Ft(e,t,n){if(!Number.isFinite(e)||!Number.isFinite(t))return NaN;let r=n?.altitudeKm??0;if(!Number.isFinite(r))return NaN;let{x:i,y:a}=Nt(e,t,r,At(n?.date));return Math.atan2(a,i)*vt}function It(e,t,n,r){return Pt(e+Ft(t,n,r))}const K=Math.PI/180,Lt=180/Math.PI,q=e=>(e%360+360)%360,J=e=>e<0?0:e>1?1:e,Rt=e=>{let t=0,n=0;for(let r of e)t+=Math.sin(r*K),n+=Math.cos(r*K);return t===0&&n===0?0:q(Math.atan2(t,n)*Lt)},zt=e=>{if(e.length===0)return 0;let t=Rt(e),n=0;for(let r of e){let e=Math.abs(W(r,t));e>n&&(n=e)}return n},Bt=(e,t,n)=>n<=0?+(e<=t):J((t+n-e)/n),Vt=(e,t,n)=>n<=0?+(e>=t):J((e-(t-n))/n),Ht=(e,t,n)=>{if(e.length<n.minWindow)return 0;let r=Bt(zt(e),n.collapseThresholdDeg,n.rampWidthDeg),i=Vt(t,n.baselineMinM,n.baselineRampM);return Math.min(r,i)},Ut=e=>J(1-e),Wt=(e,t,n)=>{let r=J(n),i=Math.cos(e*K),a=Math.sin(e*K),o=Math.cos(t*K),s=Math.sin(t*K),c=o+(i-o)*r,l=s+(a-s)*r;return q(Math.hypot(c,l)<1e-9?t:Math.atan2(l,c)*Lt)},Gt=(e,t,n)=>{if(e===null)return q(t);let r=J(n);return q(e+W(t,e)*r)},Y=e=>q(Math.atan2(e[2],e[0])*Lt),Kt=(e,t,n)=>{let r=i.fromValues(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15]),a=W(n,Y(e)),s=o.fromValues(t[0],t[1],t[2]),c=o.create();o.transformMat4(c,s,r);let l=i.create();i.fromYRotation(l,-a*K);let u=i.create();i.fromTranslation(u,c),i.multiply(u,u,l);let d=o.fromValues(-c[0],-c[1],-c[2]);i.translate(u,u,d);let f=i.create();return i.multiply(f,u,r),I(f)},qt=()=>({state:`dormant`,agreeStreak:0,disagreeStreak:0,recommendRecalibration:!1}),Jt=(e,t,n)=>{if(!t.gpsYawObservable)return{state:`dormant`,agreeStreak:0,disagreeStreak:0,recommendRecalibration:!1};let r=Math.abs(t.agreementDeltaDeg);if(r<=n.agreeToleranceDeg){let t=e.agreeStreak+1;return{state:t>=n.minAgreeStreak?`trusted`:`untrusted`,agreeStreak:t,disagreeStreak:0,recommendRecalibration:!1}}let i=e.disagreeStreak+1;return{state:e.state===`trusted`&&r>n.dropToleranceDeg?`untrusted`:e.state===`trusted`?`trusted`:`untrusted`,agreeStreak:0,disagreeStreak:i,recommendRecalibration:i>=n.recalibrateAfterStreak}},Yt=e=>e<0?0:e>1?1:e,Xt=e=>+(e===`trusted`),Zt=(e,t,n)=>{let r=Yt(e),i=Yt(t);return Yt(1-r+r*i*n.steadyStateMaxWeight)},Qt={useOnlyRecentData:!1,recentSeconds:180,includeTimeWeight:!0,weightByTimeFactor:2500,ignoreYAxisForRotation:!0,useRansac:!1,ransacInlierRatio:.95,ransacSampleRatio:.3,ransacMaxIterations:230,ransacErrorTolerance:1,geohashPrecision:8,geohashWindowSize:10,gpsAccuracyExponent:.1,altitudeAlignmentMode:`separate1D`,altitudeAggregator:`mean`,altitudeSampleSet:`all`,gpsVerticalAccuracyExponent:5,useOutlierRejection:!1,outlierThresholdMeters:10,outlierRetainRatio:.6,useDistanceGating:!1,minSampleDistanceMeters:1,maxSampleIntervalMs:3e3,useCompassColdStartOverride:!1,coldStartObservabilityCollapseDeg:12,coldStartObservabilityRampDeg:8,coldStartObservabilityWindow:5,coldStartBaselineMinM:15,coldStartBaselineRampM:10,coldStartSnapAlpha:.5,useCompassRotationPrior:!1,compassSteadyStateMaxWeight:.3},$t=e=>e.map(e=>L(e)),en=e=>e.map(e=>B(e)),tn=e=>e.map(Un),nn=e=>e.map(e=>[e[0],e[1],e[2],e[3]]),X=e=>Array.isArray(e)?e:P(e),Z=e=>Array.isArray(e)?e:F(e),rn=(e,t,n,r)=>({odometryPosOffset:L(X(e)),odometryRotOffset:B(Z(t)),latestLoopClosureFixPointPos:n?L(X(n)):null,latestLoopClosureFixPointRot:r?B(Z(r)):null}),an=(e,t,n,r)=>({odometryPositions:[],odometryRotations:[],gpsPositions:[],gpsPositionsVec4:[],alignmentMatrix:[...Se],alignmentRotation:[...R],alignmentTranslation:[...z],alignmentRotationInDegree:[...z],...rn(e,t,n,r),gpsAccuracyMedian:null,gpsAccuracyMean:null,currentGpsPosGeoHash:null,lastAdmittedOdomPos:null,lastAdmittedTs:null}),on=()=>an(z,R,null,null),sn=(e,t,n,r)=>{let i=e.map(e=>X(e)),a=t.map(e=>B(Z(e))),o=n.map(Un),s=r.gpsAccuracyExponent;for(let e of o)e.weight=dn(e.latLongAccuracy,s);let c=o.map(Vn),l=r.includeTimeWeight?In(c,o,r.weightByTimeFactor):c,u=r.useOnlyRecentData?Pn(o,r.recentSeconds):0,d=i.slice(u).map(M),f=o.slice(u);return{allPositionTuples:i,allRotationTuples:a,allGpsPoints:o,allVec4Tuples:l,solverPositionsTyped:d,solverGpsPoints:f,solverWeightedVec4Tuples:l.slice(u),solverVerticalWeights:Fn(f,r)}},cn=(e,t,n,r,i)=>{if(e==null||t==null)return!0;let a=n[0]-e[0],o=n[2]-e[2],s=i.minSampleDistanceMeters*i.minSampleDistanceMeters;return a*a+o*o>=s||r-t>=i.maxSampleIntervalMs},ln=(e,t,n,r)=>{if(!r.useDistanceGating)return{odometryPositions:e,odometryRotations:t,gpsPoints:n};let i=[],a=[],o=[],s=null,c=null;for(let l=0;l<n.length;l+=1){let u=X(e[l]),d=n[l].timestamp;cn(s,c,u,d,r)&&(i.push(e[l]),a.push(t[l]),o.push(n[l]),s=u,c=d)}return{odometryPositions:i,odometryRotations:a,gpsPoints:o}},un=e=>{let{odometryPositions:t,odometryRotations:n,gpsPoints:r,odometryPosOffset:i=z,odometryRotOffset:s=R,latestLoopClosureFixPointPos:c=null,latestLoopClosureFixPointRot:l=null,alignmentConfig:u,random:d}=e;if(t.length!==n.length||t.length!==r.length)throw Error(`GpsEvents requires odometry positions, rotations, and gps points to have identical lengths`);let f={...Qt,...u??{}};if(t.length===0)return an(i,s,c,l);let p=ln(t,n,r,f),{allPositionTuples:m,allRotationTuples:h,allGpsPoints:g,allVec4Tuples:_,solverPositionsTyped:v,solverGpsPoints:y,solverWeightedVec4Tuples:b,solverVerticalWeights:x}=sn(p.odometryPositions,p.odometryRotations,p.gpsPoints,f),S=On(hn(v,b,x,f,d??Math.random).matrix,v,b,x,g,h,f,d??Math.random),C=S.matrix,w=a.create(),T=o.create(),E=o.create();pn(C,w,T,E);let{mean:D,median:ee}=zn(g),O=Rn(p.gpsPoints,f.geohashPrecision,f.geohashWindowSize),k=m.length-1,A=k>=0?L(m[k]):null,j=k>=0?p.gpsPoints[k]?.timestamp??null:null;return{odometryPositions:$t(m),odometryRotations:en(h),gpsPositions:tn(g),gpsPositionsVec4:nn(_),alignmentMatrix:I(C),alignmentRotation:F(w),alignmentTranslation:P(T),alignmentRotationInDegree:P(E),...rn(i,s,c,l),gpsAccuracyMedian:ee,gpsAccuracyMean:D,currentGpsPosGeoHash:O,lastAdmittedOdomPos:A,lastAdmittedTs:j,...S.fields}},dn=(e,t)=>e!=null&&e>0?1/Math.max(e,1)**+t:1,fn=(e,t,n,r,i,s)=>{let c={...Qt,...i??{}},l=X(t);if(c.useDistanceGating&&!cn(e.lastAdmittedOdomPos,e.lastAdmittedTs,l,r.timestamp,c))return;let u=e.odometryPositions,d=e.odometryRotations,f=e.gpsPositions,p=e.gpsPositionsVec4;u.push(l),d.push(B(Z(n)));let m=Un(r);f.push(m),m.weight=dn(m.latLongAccuracy,c.gpsAccuracyExponent);let h=Vn(m);if(c.includeTimeWeight){p.push(h);let e=In(p,f,c.weightByTimeFactor);for(let t=0;t<e.length;t++)p[t]=e[t]}else p.push(h);let g=0;c.useOnlyRecentData&&(g=Pn(f,c.recentSeconds));let _=u.slice(g).map(M),v=p.slice(g),y=Dn(e,hn(_,v,Fn(f.slice(g),c),c,s??Math.random).matrix,_,v,f,d,c);e.alignmentMatrix=I(y);let b=a.create(),x=o.create(),S=o.create();pn(y,b,x,S),e.alignmentRotation=F(b),e.alignmentTranslation=P(x),e.alignmentRotationInDegree=P(S);let{mean:C,median:w}=zn(f);e.gpsAccuracyMean=C,e.gpsAccuracyMedian=w,e.currentGpsPosGeoHash=Rn(f,c.geohashPrecision,c.geohashWindowSize),e.lastAdmittedOdomPos=L(l),e.lastAdmittedTs=r.timestamp},pn=(e,t,n,r)=>{i.getRotation(t,e),a.normalize(t,t),i.getTranslation(n,e),Bn(t,r)},Q=o.create(),mn=o.create(),hn=(e,t,n,r,a)=>{if(e.length===0)return{matrix:i.create(),meanError:0};let o=e.length,c=[];for(let r=0;r<o;r+=1){let i=t[r];c.push({odom:e[r],ref:s.fromValues(i[0],i[1],i[2],i[3]),verticalWeight:n[r]??1})}let l,u,d;if(r.useRansac&&c.length>=3){let e=c.length,t=Math.max(3,Math.min(e,Math.ceil(e*r.ransacSampleRatio))),n=rt(c,Math.max(t,Math.min(e,Math.ceil(e*r.ransacInlierRatio))),t,r.ransacMaxIterations,e=>kn(e,r.ignoreYAxisForRotation),(e,t)=>An(e,t)<e.meanAlignmentError+r.ransacErrorTolerance,a);l=n.alignmentMatrix,u=n.meanAlignmentError,n.inliers!==void 0&&(d=Array.from(n.inliers))}else if(r.useOutlierRejection&&c.length>=4){let{model:e,inliers:t}=Nn(c,r);l=e.alignmentMatrix,u=e.meanAlignmentError,d=t}else{let e=kn(c,r.ignoreYAxisForRotation);l=e.alignmentMatrix,u=e.meanAlignmentError}return r.altitudeAlignmentMode===`separate1D`&&gn(l,c,d,r),{matrix:l,meanError:u}},gn=(e,t,n,r)=>{let i=r.altitudeSampleSet===`horizontalInliers`&&n!==void 0&&n.length>0?n:t;if(i.length===0)return;let a=Array(i.length),o=Array(i.length);for(let e=0;e<i.length;e+=1){let t=i[e];a[e]=t.ref[1]-t.odom[1];let n=t.verticalWeight;o[e]=n>0&&Number.isFinite(n)?n:0}e[13]=r.altitudeAggregator===`median`?vn(a,o):_n(a,o)},_n=(e,t)=>{let n=0,r=0;for(let i=0;i<e.length;i+=1)n+=e[i]*t[i],r+=t[i];if(r>0&&Number.isFinite(r))return n/r;let i=0;for(let t=0;t<e.length;t+=1)i+=e[t];return e.length===0?0:i/e.length},vn=(e,t)=>{if(e.length===0)return 0;if(e.length===1)return e[0];let n=e.map((e,t)=>t);n.sort((t,n)=>e[t]-e[n]);let r=0;for(let e=0;e<t.length;e+=1)r+=t[e];if(!(r>0)||!Number.isFinite(r)){let t=n.map(t=>e[t]);return t[Math.floor((t.length-1)/2)]}let i=r/2,a=0;for(let r=0;r<n.length;r+=1)if(a+=t[n[r]],a>=i)return e[n[r]];return e[n[n.length-1]]},yn=e=>{let t=0;for(let n=1;n<e.length;n+=1){let r=e[n].coordinates,i=e[n-1].coordinates;t+=Math.hypot(r[0]-i[0],r[2]-i[2])}return t},bn=(e,t)=>{let n=o.create(),r=0;for(let i=0;i<e.length;i+=1){let a=t[i][3];o.scaleAndAdd(n,n,e[i],a),r+=a}return r>0&&o.scale(n,n,1/r),n},xn=(e,t)=>{let n=[];for(let r=0;r<e.length;r+=1){let i=e[r].absoluteOrientation,a=t[r];if(!i||!a)continue;let o=gt(i.quaternion,a);if(o===null)continue;let s=((o+Ft(e[r].latitude,e[r].longitude,{date:e[r].timestamp||void 0}))%360+360)%360;Number.isFinite(s)&&n.push(s)}return n.length===0?null:Rt(n)},Sn=e=>({collapseThresholdDeg:e.coldStartObservabilityCollapseDeg,rampWidthDeg:e.coldStartObservabilityRampDeg,minWindow:e.coldStartObservabilityWindow,baselineMinM:e.coldStartBaselineMinM,baselineRampM:e.coldStartBaselineRampM}),Cn=(e,t,n,r,i)=>{let a=e.length,o=Math.max(1,a-r.coldStartObservabilityWindow+1),s=[];for(let c=o;c<=a;c+=1){let a=hn(e.slice(0,c),t.slice(0,c),n.slice(0,c),r,i);s.push(Y(a.matrix))}return s},wn={agreeToleranceDeg:8,minAgreeStreak:3,dropToleranceDeg:20,recalibrateAfterStreak:4},Tn=(e,t,n,r,i,a,o,s,c)=>{let l=xn(r,i);if(l===null)return{matrix:e,appliedBearingDeg:o,trust:s};let u=a.length>0?a[a.length-1]:Y(e),d=Ht(a,yn(r),Sn(c)),f,p=s;c.useCompassRotationPrior?(p=Jt(s??qt(),{gpsYawObservable:d>=.5,agreementDeltaDeg:W(l,u)},wn),f=Zt(d,Xt(p.state),{steadyStateMaxWeight:c.compassSteadyStateMaxWeight})):f=Ut(d);let m=Gt(o,Wt(l,u,f),c.coldStartSnapAlpha);return{matrix:ye(Kt(e,bn(t,n),m)),appliedBearingDeg:m,trust:p}},En=e=>e.useCompassColdStartOverride||e.useCompassRotationPrior,Dn=(e,t,n,r,i,a,o)=>{if(!En(o))return t;let s=e.coldStartYawWindowDeg?[...e.coldStartYawWindowDeg]:[];for(s.push(Y(t));s.length>o.coldStartObservabilityWindow;)s.shift();let c=Tn(t,n,r,i,a,s,e.coldStartAppliedBearingDeg??null,e.compassTrust,o);return e.coldStartYawWindowDeg=s,e.coldStartAppliedBearingDeg=c.appliedBearingDeg,c.trust!==void 0&&(e.compassTrust=c.trust),c.matrix},On=(e,t,n,r,i,a,o,s)=>{if(!En(o))return{matrix:e,fields:{}};let c=Cn(t,n,r,o,s),l=Tn(e,t,n,i,a,c,null,void 0,o),u={coldStartYawWindowDeg:c,coldStartAppliedBearingDeg:l.appliedBearingDeg};return l.trust!==void 0&&(u.compassTrust=l.trust),{matrix:l.matrix,fields:u}},kn=(e,t)=>{let n=Array.from(e),r=n.length,i=Array(r),a=Array(r);for(let e=0;e<r;e+=1)i[e]=n[e].odom,a[e]=n[e].ref;let s=et(i,a,{ignoreYAxisForRotation:t}),c=0;for(let e=0;e<r;e+=1)o.transformMat4(Q,i[e],s),c+=jn(Q,n[e].ref);return{alignmentMatrix:s,meanAlignmentError:r===0?0:c/r,totalModelError:c}},An=(e,t)=>(o.transformMat4(Q,t.odom,e.alignmentMatrix),jn(Q,t.ref)),jn=(e,t)=>(o.set(mn,t[0],t[1],t[2]),o.squaredDistance(e,mn)),Mn=(e,t)=>{o.transformMat4(Q,t.odom,e.alignmentMatrix);let n=Q[0]-t.ref[0],r=Q[2]-t.ref[2];return n*n+r*r},Nn=(e,t)=>{let n=e.length,r=Math.max(3,Math.ceil(t.outlierRetainRatio*n)),i=t.outlierThresholdMeters*t.outlierThresholdMeters,a=Array.from(e),o=kn(a,t.ignoreYAxisForRotation);for(;a.length>r;){let e=-1,n=-1;for(let t=0;t<a.length;t+=1){let r=Mn(o,a[t]);r>n&&(n=r,e=t)}if(n<=i||e<0)break;a=a.filter((t,n)=>n!==e),o=kn(a,t.ignoreYAxisForRotation)}return{model:o,inliers:a}},Pn=(e,t)=>{if(e.length===0||t<=0)return 0;let n=e[e.length-1].timestamp-t*1e3;for(let t=0;t<e.length;t+=1)if(e[t].timestamp>=n)return t;return 0},Fn=(e,t)=>{let n=t.gpsVerticalAccuracyExponent,r=e.map(e=>{let t=e.altitudeAccuracy;return dn(t!=null&&Number.isFinite(t)&&t>0?t:e.latLongAccuracy,n)});if(!t.includeTimeWeight||e.length<2)return r;let i=e[e.length-1].timestamp,a=e[0].timestamp,o=Math.max(1,i-a),s=t.weightByTimeFactor;return r.map((t,n)=>{if(!(t>0&&Number.isFinite(t)))return 0;let r=s*((i-e[n].timestamp)/o)+1;return 1/(1/t+r)})},In=(e,t,n)=>{if(e.length<2)return e.map(e=>[e[0],e[1],e[2],e[3]]);let r=t[t.length-1].timestamp,i=t[0].timestamp,a=Math.max(1,r-i);return e.map((e,i)=>{let o=n*((r-t[i].timestamp)/a)+1,s=t[i].weight;if(!(s>0&&Number.isFinite(s)))return[e[0],e[1],e[2],0];let c=1/(1/s+o);return[e[0],e[1],e[2],c]})},Ln=new Map,Rn=(e,t,n)=>{if(e.length===0)return null;let r=Math.max(0,e.length-n);Ln.clear();for(let n=r;n<e.length;n+=1){let r=e[n],i=ie(Hn(r),t),a=Ln.get(i);a?(a.count+=1,a.index=n):Ln.set(i,{count:1,index:n})}let i=null,a=-1,o=-1;for(let[e,{count:t,index:n}]of Ln)(t>a||t===a&&n>o)&&(i=e,a=t,o=n);return i},zn=e=>{let t=e.map(e=>e.latLongAccuracy).filter(e=>typeof e==`number`&&Number.isFinite(e));if(t.length===0)return{mean:null,median:null};let n=t.reduce((e,t)=>e+t,0)/t.length,r=t.slice().sort((e,t)=>e-t),i=Math.floor(r.length/2);return{mean:n,median:r.length%2==0?(r[i-1]+r[i])/2:r[i]}},Bn=(e,t)=>{let n=e[0],r=e[1],i=e[2],a=e[3],s=2*(a*n+r*i),c=1-2*(n*n+r*r),l=Math.atan2(s,c),u=2*(a*r-i*n),d;d=Math.abs(u)>=1?Math.PI/2*Math.sign(u):Math.asin(u);let f=2*(a*i+n*r),p=1-2*(r*r+i*i),m=Math.atan2(f,p),h=180/Math.PI,g=t??o.create();return g[0]=l*h,g[1]=d*h,g[2]=m*h,g},Vn=e=>[e.coordinates[0],e.coordinates[1],e.coordinates[2],e.weight>0&&Number.isFinite(e.weight)?e.weight:1],Hn=e=>({lat:e.latitude,lon:e.longitude}),Un=e=>({...e,zeroRef:{lat:e.zeroRef.lat,lon:e.zeroRef.lon},coordinates:[e.coordinates[0],e.coordinates[1],e.coordinates[2]],timestamp:e.timestamp,deviceRotation:e.deviceRotation?B(Z(e.deviceRotation)):void 0,absoluteOrientation:e.absoluteOrientation?{...e.absoluteOrientation,quaternion:[e.absoluteOrientation.quaternion[0],e.absoluteOrientation.quaternion[1],e.absoluteOrientation.quaternion[2],e.absoluteOrientation.quaternion[3]]}:void 0}),Wn=({lat:e,lon:t})=>{if(!Number.isFinite(e))throw Error(`Invalid latitude value: ${e}`);if(!Number.isFinite(t))throw Error(`Invalid longitude value: ${t}`);if(e<-90||e>90)throw Error(`Invalid latitude range: ${e}`);if(t<-180||t>180)throw Error(`Invalid longitude range: ${t}`)},Gn=o.fromValues(1,1,1),Kn=(e,t)=>[e[0]+t[0],e[1]+t[1],e[2]+t[2]],qn=(e,t,n,r)=>{let i=ce(t,{lat:e.latitude,lon:e.longitude},e.altitude??0,0),a=n?.alpha!=null&&n?.beta!=null&&n?.gamma!=null?U(Ne(n.alpha,n.beta,n.gamma)):void 0;return{...e,zeroRef:t,coordinates:P(i),weight:1,deviceRotation:a,absoluteOrientation:r}},Jn=(e,t,n)=>{if(e)return Ne(e.alpha,e.beta,e.gamma);if(t)return t;throw Error(`Sensor rotation missing for ${n}: neither raw orientation nor legacy quaternion provided`)},Yn=e=>{let t=a.invert(a.create(),e);if(!t)throw Error(`Encountered non-invertible quaternion`);return t},Xn=e=>{let t=Yn(a.normalize(a.create(),N(e.lastSensorRot))),n=a.normalize(a.create(),N(e.lastValidOdomRot)),r=a.multiply(a.create(),t,n),i=a.normalize(a.create(),N(e.newSensorRot)),o=a.multiply(a.create(),i,r),s=a.normalize(a.create(),N(e.newOdomRot)),c=a.multiply(a.create(),o,s);return a.normalize(c,c),F(c)},Zn=(e,t)=>{let n=i.create();return i.fromRotationTranslationScale(n,a.normalize(a.create(),N(t)),M(e),Gn),n},Qn=(e,t,n)=>{let r=o.create(),s=o.create(),c=a.create(),l=a.create();i.getTranslation(r,e),i.getTranslation(s,t),i.getRotation(c,e),i.getRotation(l,t);let u=o.create();o.lerp(u,r,s,n);let d=a.create();return a.slerp(d,c,l,n),i.fromRotationTranslation(i.create(),d,u)},$n=(e,t)=>{if(e.length===0)return[];let n=e[e.length-1],r=i.invert(i.create(),n);if(!r)throw Error(`End pose matrix is not invertible`);let a=i.multiply(i.create(),t,r),o=i.create(),s=e.length-1;return e.map((e,t)=>{let n=Qn(o,a,s<=0?1:t/s);return i.multiply(i.create(),n,e)})},er=(e,t,n)=>{for(let r=e.length-1;r>=0;--r)if(n(e[r],t))return r;return-1},tr=e=>e.coldStartOverrideEnabled?{useCompassColdStartOverride:!0}:void 0,nr=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2],rr=(e,t)=>e[0]===t[0]&&e[1]===t[1]&&e[2]===t[2]&&e[3]===t[3],ir=t({name:`gpsData`,initialState:null,reducers:{setZeroPos:{reducer:(e,t)=>e===null?{zero:t.payload,gpsEvents:on(),odometryPath:ge()}:e,prepare:e=>(Wn(e),{payload:e})},recordGpsEvent:(e,t)=>{if(!e)return e;let{odomPosition:n,odomRotation:r,rawGpsPoint:i,rawDeviceOrientation:a,rawAbsoluteOrientation:o}=t.payload,s=qn(i,e.zero,a,o);fn(e.gpsEvents,V(n),H(r),s,tr(e))},setColdStartOverrideEnabled:(e,t)=>{if(!e)return e;e.coldStartOverrideEnabled=t.payload},odometryTrackingRestarted:(e,t)=>{if(!e)return e;let n=Kn(e.gpsEvents.odometryPosOffset,V(t.payload.lastValidOdomPos)),r=Jn(t.payload.lastSensorOrientation,t.payload.lastSensorRot,`lastSensor`),i=Jn(t.payload.newSensorOrientation,t.payload.newSensorRot,`newSensor`);e.gpsEvents=c(un({odometryPositions:[],odometryRotations:[],gpsPoints:[],odometryPosOffset:n,odometryRotOffset:Xn({...t.payload,lastValidOdomRot:H(t.payload.lastValidOdomRot),newOdomRot:H(t.payload.newOdomRot),lastSensorRot:U(r),newSensorRot:U(i)}),latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null,alignmentConfig:tr(e)}))},arLoopClosureDetected:(e,t)=>{if(!e)return e;let n=e.gpsEvents;if(n.odometryPositions.length===0)return e;let r=n.latestLoopClosureFixPointPos?er(n.odometryPositions,n.latestLoopClosureFixPointPos,nr):0,s=n.latestLoopClosureFixPointRot?er(n.odometryRotations,n.latestLoopClosureFixPointRot,rr):0;if(r<0||s<0)throw Error(`Loop closure fix point could not be located in history`);if(r!==s)throw Error(`Loop closure fix point indices for position and rotation diverged`);let l=n.odometryPositions.slice(0,r).map(e=>L(e)),u=n.odometryRotations.slice(0,r).map(e=>B(e)),d=n.odometryPositions.slice(r).map(e=>L(e)),f=n.odometryRotations.slice(r).map(e=>B(e)),p=d.map((e,t)=>Zn(e,f[t])),m=Zn(V(t.payload.lastPos),B(H(t.payload.lastRot)));p.push(m);let h=$n(p,Zn(V(t.payload.newPos),B(H(t.payload.newRot)))),g=[],_=[],v=o.create();for(let e=0;e<h.length-1;e+=1){let t=h[e],n=o.create();i.getTranslation(n,t);let r=a.create();if(i.getRotation(r,t),a.normalize(r,r),i.getScaling(v,t),Math.hypot(v[0]-1,v[1]-1,v[2]-1)>.05)throw Error(`Unexpected scale drift encountered during loop closure correction`);g.push(P(n)),_.push(F(r))}e.gpsEvents=c(un({odometryPositions:l.concat(g),odometryRotations:u.concat(_),gpsPoints:n.gpsPositions,odometryPosOffset:n.odometryPosOffset,odometryRotOffset:n.odometryRotOffset,latestLoopClosureFixPointPos:null,latestLoopClosureFixPointRot:null,alignmentConfig:tr(e)}))},add2dImage:(e,t)=>{if(!e)return e;e.odometryPath.points.push(c({imageFile:t.payload.imageFile,screenRotation:t.payload.screenRotation,position:V(t.payload.position),rotation:B(H(t.payload.rotation)),capturedAt:t.payload.capturedAt,width:t.payload.width,height:t.payload.height}))}}}),{setZeroPos:ar,recordGpsEvent:or,odometryTrackingRestarted:sr,arLoopClosureDetected:cr,add2dImage:lr,setColdStartOverrideEnabled:ur}=ir.actions,dr=p(ar),fr=p(or),pr=p(sr),mr=p(cr),hr=p(lr),gr=p(ur),_r=m(ir.reducer),vr=t({name:`gpsElements`,initialState:_e(),reducers:{addMarker:(e,t)=>{e.gpsMarkers.push(t.payload)},addLine:(e,t)=>{e.gpsLines.push(t.payload)},addArea:(e,t)=>{e.areas.push(t.payload)},addToHeatMaps:(e,t)=>{let{category:n,tiles:r}=t.payload,i=e.heatMap[n]??{};e.heatMap[n]={...i,...r}},addHeatMapArea:(e,t)=>{let n=t.payload,r=n.category;if(e.heatAreas[r]||(e.heatAreas[r]={}),e.heatAreas[r][n.geoHash])throw Error(`HeatArea collision detected for GeoHash: ${n.geoHash}`);e.heatAreas[r][n.geoHash]=n},resetGpsElements:()=>_e()}}),{addMarker:yr,addLine:br,addArea:xr,addToHeatMaps:Sr,addHeatMapArea:Cr,resetGpsElements:wr}=vr.actions,Tr=p(yr),Er=p(br),Dr=p(xr),Or=p(Sr),kr=p(Cr),Ar=p(wr),jr=m(vr.reducer),Mr=()=>({gpsData:null,gpsElements:_e(),arElements:ve()}),Nr=e=>{let t=Array.from({length:Math.min(e.length,4)},(t,n)=>e[n].toFixed(2));e.length>4&&t.push(`…`);let n=`[${t.join(`, `)}]`;if(e.length===4){let{pitch:t,yaw:r,roll:i}=v(e);return`${n} (pitch=${t.toFixed(0)}°, yaw=${r.toFixed(0)}°, roll=${i.toFixed(0)}°)`}return n},Pr=e=>(e.length===3||e.length===4||e.length===16)&&e.every(e=>typeof e==`number`),$=(e,t=0)=>{if(t>10||e==null)return e;if(Array.isArray(e))return Pr(e)?Nr(e):e.map(e=>$(e,t+1));if(typeof e==`object`){let n={};for(let[r,i]of Object.entries(e))n[r]=$(i,t+1);return n}return e},Fr=(t={})=>{let{preloadedState:n,enableDevToolsSanitizers:r=!0,enableDevChecks:i=!0,licenseKey:a}=t;if(a==null)throw Error(`License key required. Pass options.licenseKey or use createRecorderStore() from gps-plus-slam-app-framework. See EULA §3.`);g(a);let o=Mr(),s=n?{gpsData:n.gpsData??o.gpsData,gpsElements:n.gpsElements??o.gpsElements,arElements:n.arElements??o.arElements}:void 0;return e({reducer:{gpsData:_r,gpsElements:jr,arElements:Ze},preloadedState:s,middleware:e=>e({serializableCheck:i,immutableCheck:i}),devTools:r?{actionSanitizer:$,stateSanitizer:$}:!0})},Ir=[],Lr=[],Rr=[],zr={median:null,mean:null},Br=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentMatrix}),Vr=f(function(e){let t=e.gpsData?.gpsEvents;return!t||t.gpsPositions.length===0?null:t.alignmentRotation}),Hr=f(function(e){return e.gpsData?.gpsEvents?.odometryPositions??Ir}),Ur=f(function(e){return e.gpsData?.gpsEvents?.odometryRotations??Lr}),Wr=f(function(e){return e.gpsData?.gpsEvents?.gpsPositions??Rr}),Gr=f(function(e){return e.gpsData?.zero??null}),Kr=f(function(e){let t=e.gpsData?.gpsEvents?.gpsAccuracyMedian??null,n=e.gpsData?.gpsEvents?.gpsAccuracyMean??null;return t===null&&n===null?zr:{median:t,mean:n}}),qr=`1.0.0`;export{Se as IDENTITY_MATRIX4,R as IDENTITY_QUATERNION,qr as LIB_VERSION,yt as WMM2025_EPOCH,bt as WMM2025_VALID_FROM,xt as WMM2025_VALID_TO,z as ZERO_VECTOR3,hr as add2dImage,Dr as addArea,Ke as addCornerToEventArea,kr as addHeatMapArea,Er as addLine,Tr as addMarker,We as addOrUpdateArPlane,Or as addToHeatMaps,Ye as anchorEventAreaInWorldSpace,Ze as arElementsReducer,mr as arLoopClosureDetected,gt as arNorthBearingDeg,ht as arToEnuRotation,W as bearingDeltaDeg,ie as calcGeoHash,le as calcGpsCoords,ne as calcQuadKey,ce as calcRelativeCoordsInMeters,xe as cloneMatrix4,be as cloneQuaternion,L as cloneVector3,qe as correctArDriftForEventArea,Fr as createGpsSlamStore,oe as distanceInMeters,se as distanceInMetersRelative,U as enuQuaternionToNUE,Ne as eulerToQuaternion,Je as finishEventArea,ye as fromMatrix4,N as fromQuaternion,M as fromVector3,he as fusedGpsFromOdom,ae as geoHashToLatLong,Br as getAlignmentMatrix,Vr as getAlignmentRotation,pe as getGoogleMapsDirectionsLink,fe as getGoogleMapsLink,Kr as getGpsAccuracyStats,Wr as getGpsPositions,Hr as getOdometryPositions,Ur as getOdometryRotations,me as getOpenStreetMapLink,Gr as getZeroReference,_r as gpsDataReducer,jr as gpsElementsReducer,ke as invertQuaternion,Ce as isIdentityMatrix4,Te as isIdentityQuaternion,Ee as isNearIdentityQuaternion,Ft as magneticDeclinationDeg,pt as magneticHeadingFromEnuQuat,It as magneticToTrueHeadingDeg,Oe as multiplyQuaternions,B as normalizeQuaternion,Me as nueQuaternionToENU,je as nueQuaternionToWebXR,Ae as nueToWebXR,pr as odometryTrackingRestarted,re as quadKeyToLatLong,we as quaternionMagnitude,De as quaternionsEquivalent,fr as recordGpsEvent,Ue as recordPhoneHeight,Xe as resetArElements,Ar as resetGpsElements,$ as sanitizeForDevTools,gr as setColdStartOverrideEnabled,dr as setZeroPos,Ge as startEventArea,ue as toEarthCenteredCoordinates,I as toMatrix4,F as toQuaternion,P as toVector3,g as validateLicenseKey,H as webxrQuaternionToNUE,V as webxrToNUE};
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
* @see docs/2026-04-25-private-key-security-plan.md — secret-backed renewal
|
|
36
36
|
* @see ../../GpsPlusSlamJs_Docs/docs/2026-05-01-community-key-resign-cross-repo-issue.md
|
|
37
37
|
*/
|
|
38
|
-
declare const COMMUNITY_LICENSE_KEY = "
|
|
38
|
+
declare const COMMUNITY_LICENSE_KEY = "eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODE0MDQ4ODExfQ.93uD-XHsPVXAAVkr163KYmYcRxMXpi4qkdntamr9kUWsU9q5-zb67ptgISNOOfdLDJnejod4auPokrYlF1zqBQ";
|
|
39
39
|
//#endregion
|
|
40
40
|
export { COMMUNITY_LICENSE_KEY };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! gps-plus-slam-js | (c) 2026 cs-util-com | UNLICENSED — see EULA.md */
|
|
2
|
-
const e=`
|
|
2
|
+
const e=`eyJ0eXBlIjoiY29tbXVuaXR5IiwiZXhwIjoxODE0MDQ4ODExfQ.93uD-XHsPVXAAVkr163KYmYcRxMXpi4qkdntamr9kUWsU9q5-zb67ptgISNOOfdLDJnejod4auPokrYlF1zqBQ`;export{e as COMMUNITY_LICENSE_KEY};
|