gps-plus-slam-app-framework 1.0.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/LICENSE +201 -0
- package/README.md +222 -0
- package/dist/alignment-lerper-D9BeDwZX.d.ts +29 -0
- package/dist/app-selectors-DyMzxCEC.d.ts +140 -0
- package/dist/ar/camera-blit-capture.d.ts +2 -0
- package/dist/ar/camera-blit-capture.js +253 -0
- package/dist/ar/capture-failure-tracker.d.ts +2 -0
- package/dist/ar/capture-failure-tracker.js +45 -0
- package/dist/ar/chromium-camera-access-workaround.d.ts +2 -0
- package/dist/ar/chromium-camera-access-workaround.js +32 -0
- package/dist/ar/depth-sampler.d.ts +3 -0
- package/dist/ar/depth-sampler.js +147 -0
- package/dist/ar/image-capture.d.ts +2 -0
- package/dist/ar/image-capture.js +152 -0
- package/dist/ar/index.d.ts +12 -0
- package/dist/ar/index.js +12 -0
- package/dist/ar/replay-scene.d.ts +2 -0
- package/dist/ar/replay-scene.js +325 -0
- package/dist/ar/scene-node-names.d.ts +2 -0
- package/dist/ar/scene-node-names.js +16 -0
- package/dist/ar/tracking-state.d.ts +2 -0
- package/dist/ar/tracking-state.js +164 -0
- package/dist/ar/webxr-session.d.ts +4 -0
- package/dist/ar/webxr-session.js +890 -0
- package/dist/ar/xr-camera-texture.d.ts +2 -0
- package/dist/ar/xr-camera-texture.js +39 -0
- package/dist/ar/xr-error-handler.d.ts +2 -0
- package/dist/ar/xr-error-handler.js +37 -0
- package/dist/ar-types-B-ORgk6Z.d.ts +82 -0
- package/dist/camera-blit-capture-DHuyrrxr.d.ts +108 -0
- package/dist/camera-follower-BMqPddxx.d.ts +29 -0
- package/dist/capture-failure-tracker-2Q6oI2uQ.d.ts +52 -0
- package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +61 -0
- package/dist/community-license-key-ChZLCJyO.d.ts +21 -0
- package/dist/concurrency-p6gB30fT.d.ts +24 -0
- package/dist/css3d-renderer-manager-rgLMVAvd.d.ts +23 -0
- package/dist/depth-sampler-BNCtnEoP.d.ts +108 -0
- package/dist/failure-tracker-1sQ_Cgyw.d.ts +49 -0
- package/dist/file-system-BsQgYCU3.d.ts +128 -0
- package/dist/file-system-utils-5cWO8Xyv.d.ts +24 -0
- package/dist/format-file-size-onGq0k1Q.d.ts +24 -0
- package/dist/fused-path-DtqCSqJh.d.ts +25 -0
- package/dist/geo-types-Xx-LVcnw.d.ts +29 -0
- package/dist/gps-Ddzr2KlX.d.ts +54 -0
- package/dist/gps-compass-cubes-C-6JEnpR.d.ts +22 -0
- package/dist/gps-error-handler-VPuNOuf8.d.ts +40 -0
- package/dist/gps-event-markers-DpEy2qRd.d.ts +67 -0
- package/dist/h3-ref-point-CFhayNSZ.d.ts +59 -0
- package/dist/image-capture-2pKjYUS6.d.ts +130 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +61 -0
- package/dist/leaflet-map-overlay-CvfQXTye.d.ts +114 -0
- package/dist/lerp-utils-87NWjUOD.d.ts +20 -0
- package/dist/licensing/community-license-key.d.ts +2 -0
- package/dist/licensing/community-license-key.js +21 -0
- package/dist/licensing/index.d.ts +2 -0
- package/dist/licensing/index.js +2 -0
- package/dist/list-formatter-DKQQVLcl.d.ts +14 -0
- package/dist/logger-B81iwxx0.d.ts +74 -0
- package/dist/map-overlay-DuiZuCTx.d.ts +153 -0
- package/dist/null-storage-backend-CsWvQwN8.d.ts +11 -0
- package/dist/opfs-storage-ChOzF_Wp.d.ts +181 -0
- package/dist/opfs-storage-backend-CVcmfJHN.d.ts +11 -0
- package/dist/permission-checker-jO2ziCWC.d.ts +146 -0
- package/dist/persistence-middleware-DrptY02i.d.ts +31 -0
- package/dist/recorder-slice-fcwAXrsh.d.ts +35 -0
- package/dist/recording-coordinator-DzflP07_.d.ts +81 -0
- package/dist/recording-options-BYUFX8iU.d.ts +166 -0
- package/dist/recording-replayer-oOzPITrd.d.ts +32 -0
- package/dist/ref-point-importer-B_wn5ABk.d.ts +62 -0
- package/dist/ref-point-loader-Pt1smgHS.d.ts +117 -0
- package/dist/ref-points/h3-ref-point.d.ts +2 -0
- package/dist/ref-points/h3-ref-point.js +74 -0
- package/dist/ref-points/index.d.ts +2 -0
- package/dist/ref-points/index.js +2 -0
- package/dist/ref-points-slice-tC-Wd6XJ.d.ts +48 -0
- package/dist/reference-points-CFWy8_wj.d.ts +68 -0
- package/dist/replay-engine-DuX316ae.d.ts +103 -0
- package/dist/replay-scene-DtUm6bsr.d.ts +109 -0
- package/dist/routing-slice-BlHTM8eh.d.ts +23 -0
- package/dist/scene-node-names-DPsaE2z3.d.ts +14 -0
- package/dist/sensors/gps-error-handler.d.ts +2 -0
- package/dist/sensors/gps-error-handler.js +53 -0
- package/dist/sensors/gps.d.ts +2 -0
- package/dist/sensors/gps.js +93 -0
- package/dist/sensors/index.d.ts +4 -0
- package/dist/sensors/index.js +4 -0
- package/dist/sensors/permission-checker.d.ts +2 -0
- package/dist/sensors/permission-checker.js +397 -0
- package/dist/state/app-selectors.d.ts +2 -0
- package/dist/state/app-selectors.js +35 -0
- package/dist/state/index.d.ts +17 -0
- package/dist/state/index.js +13 -0
- package/dist/state/persistence-middleware.d.ts +2 -0
- package/dist/state/persistence-middleware.js +71 -0
- package/dist/state/recorder-slice.d.ts +2 -0
- package/dist/state/recorder-slice.js +47 -0
- package/dist/state/recording-coordinator.d.ts +2 -0
- package/dist/state/recording-coordinator.js +150 -0
- package/dist/state/recording-options.d.ts +2 -0
- package/dist/state/recording-options.js +192 -0
- package/dist/state/recording-replayer.d.ts +2 -0
- package/dist/state/recording-replayer.js +39 -0
- package/dist/state/ref-points-slice.d.ts +2 -0
- package/dist/state/ref-points-slice.js +44 -0
- package/dist/state/replay-engine.d.ts +2 -0
- package/dist/state/replay-engine.js +230 -0
- package/dist/state/routing-slice.d.ts +2 -0
- package/dist/state/routing-slice.js +13 -0
- package/dist/state/store-subscribers.d.ts +3 -0
- package/dist/state/store-subscribers.js +97 -0
- package/dist/state/store.d.ts +9 -0
- package/dist/state/store.js +71 -0
- package/dist/state/subscribe-to-selector.d.ts +2 -0
- package/dist/state/subscribe-to-selector.js +26 -0
- package/dist/storage/file-system-utils.d.ts +2 -0
- package/dist/storage/file-system-utils.js +31 -0
- package/dist/storage/file-system.d.ts +2 -0
- package/dist/storage/file-system.js +229 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/null-storage-backend.d.ts +2 -0
- package/dist/storage/null-storage-backend.js +8 -0
- package/dist/storage/opfs-storage-backend.d.ts +2 -0
- package/dist/storage/opfs-storage-backend.js +15 -0
- package/dist/storage/opfs-storage.d.ts +2 -0
- package/dist/storage/opfs-storage.js +242 -0
- package/dist/storage/ref-point-importer.d.ts +2 -0
- package/dist/storage/ref-point-importer.js +163 -0
- package/dist/storage/ref-point-loader.d.ts +2 -0
- package/dist/storage/ref-point-loader.js +248 -0
- package/dist/storage/ref-point-recovery.d.ts +28 -0
- package/dist/storage/ref-point-recovery.js +159 -0
- package/dist/storage/storage-backend.d.ts +2 -0
- package/dist/storage/storage-backend.js +1 -0
- package/dist/storage/zip-export.d.ts +2 -0
- package/dist/storage/zip-export.js +214 -0
- package/dist/storage/zip-reader.d.ts +2 -0
- package/dist/storage/zip-reader.js +190 -0
- package/dist/storage-backend-BcEItnFr.d.ts +20 -0
- package/dist/store-C7PKOj8S.d.ts +96 -0
- package/dist/store-subscribers-CuoIzB5T.d.ts +77 -0
- package/dist/subscribe-to-selector-UTZ0KkAc.d.ts +26 -0
- package/dist/test-utils/browser-mocks.d.ts +294 -0
- package/dist/test-utils/browser-mocks.js +568 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +60 -0
- package/dist/test-utils/zip-round-trip-helpers.js +207 -0
- package/dist/three-dispose-CCBHjE_a.d.ts +30 -0
- package/dist/tracking-state-B9QfuZCg.d.ts +101 -0
- package/dist/types/ar-types.d.ts +2 -0
- package/dist/types/ar-types.js +1 -0
- package/dist/types/geo-types.d.ts +2 -0
- package/dist/types/geo-types.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/concurrency.d.ts +2 -0
- package/dist/utils/concurrency.js +40 -0
- package/dist/utils/failure-tracker.d.ts +2 -0
- package/dist/utils/failure-tracker.js +48 -0
- package/dist/utils/format-file-size.d.ts +2 -0
- package/dist/utils/format-file-size.js +41 -0
- package/dist/utils/fused-path.d.ts +2 -0
- package/dist/utils/fused-path.js +44 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/list-formatter.d.ts +2 -0
- package/dist/utils/list-formatter.js +17 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +226 -0
- package/dist/vis-colors-Qi2vlRGt.d.ts +63 -0
- package/dist/visualization/alignment-lerper.d.ts +2 -0
- package/dist/visualization/alignment-lerper.js +68 -0
- package/dist/visualization/camera-follower.d.ts +2 -0
- package/dist/visualization/camera-follower.js +51 -0
- package/dist/visualization/css3d-renderer-manager.d.ts +2 -0
- package/dist/visualization/css3d-renderer-manager.js +41 -0
- package/dist/visualization/gps-compass-cubes.d.ts +2 -0
- package/dist/visualization/gps-compass-cubes.js +129 -0
- package/dist/visualization/gps-event-markers.d.ts +2 -0
- package/dist/visualization/gps-event-markers.js +159 -0
- package/dist/visualization/index.d.ts +12 -0
- package/dist/visualization/index.js +12 -0
- package/dist/visualization/leaflet-map-overlay.d.ts +2 -0
- package/dist/visualization/leaflet-map-overlay.js +313 -0
- package/dist/visualization/lerp-utils.d.ts +2 -0
- package/dist/visualization/lerp-utils.js +22 -0
- package/dist/visualization/map-overlay.d.ts +2 -0
- package/dist/visualization/map-overlay.js +241 -0
- package/dist/visualization/reference-points.d.ts +2 -0
- package/dist/visualization/reference-points.js +160 -0
- package/dist/visualization/three-dispose.d.ts +2 -0
- package/dist/visualization/three-dispose.js +55 -0
- package/dist/visualization/vis-colors.d.ts +2 -0
- package/dist/visualization/vis-colors.js +74 -0
- package/dist/webxr-session-COlSrXdL.d.ts +316 -0
- package/dist/xr-camera-texture-BbukNE79.d.ts +53 -0
- package/dist/xr-error-handler-DTHfmvrl.d.ts +24 -0
- package/dist/zip-export-Rtpi75JJ.d.ts +87 -0
- package/dist/zip-reader-D7pkpX8K.d.ts +97 -0
- package/package.json +104 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
import { disposeMeshArray } from "./three-dispose.js";
|
|
3
|
+
import { getArWorldGroup, getScene } from "../ar/webxr-session.js";
|
|
4
|
+
import { VIS_COLORS } from "./vis-colors.js";
|
|
5
|
+
import * as THREE from "three";
|
|
6
|
+
//#region ../src/visualization/gps-event-markers.ts
|
|
7
|
+
/**
|
|
8
|
+
* GPS Event Markers Visualizer
|
|
9
|
+
*
|
|
10
|
+
* Creates and manages Three.js meshes for visualizing GPS events
|
|
11
|
+
* in the 3D AR scene. Shows two types of markers:
|
|
12
|
+
* - Raw GPS markers (yellow): Where GPS readings were received — added to scene root
|
|
13
|
+
* - Fused markers (cyan): AR odometry positions — added to arWorldGroup so that
|
|
14
|
+
* scene-graph propagation (arWorldGroup.matrix × odomPos) handles alignment
|
|
15
|
+
*
|
|
16
|
+
* ARCHITECTURE NOTE:
|
|
17
|
+
* - Raw GPS markers are in scene root (GPS world space), fixed forever
|
|
18
|
+
* - Fused markers are children of arWorldGroup with position = raw odom coords;
|
|
19
|
+
* when applyAlignmentMatrix() updates arWorldGroup.matrix, all fused markers'
|
|
20
|
+
* world positions update automatically via Three.js scene-graph propagation.
|
|
21
|
+
*/
|
|
22
|
+
const log = createLogger("GpsEventVisualizer");
|
|
23
|
+
/** Sphere radius in meters (8cm - smaller than reference point markers) */
|
|
24
|
+
const GPS_MARKER_RADIUS = .08;
|
|
25
|
+
/** Slightly larger radius for alignment snapshots to stand out */
|
|
26
|
+
const SNAPSHOT_MARKER_RADIUS = .1;
|
|
27
|
+
/**
|
|
28
|
+
* Manager for GPS event visualization
|
|
29
|
+
*/
|
|
30
|
+
var GpsEventVisualizer = class {
|
|
31
|
+
rawGpsMarkers = [];
|
|
32
|
+
fusedMarkers = [];
|
|
33
|
+
snapshotMarkers = [];
|
|
34
|
+
zeroRef = null;
|
|
35
|
+
eventCounter = 0;
|
|
36
|
+
snapshotCounter = 0;
|
|
37
|
+
/**
|
|
38
|
+
* Set the GPS zero reference (origin for coordinate conversion).
|
|
39
|
+
* Must be called before adding GPS events.
|
|
40
|
+
*
|
|
41
|
+
* @param zero - GPS coordinates of the origin
|
|
42
|
+
*/
|
|
43
|
+
setZeroRef(zero) {
|
|
44
|
+
this.zeroRef = zero;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the current zero reference
|
|
48
|
+
*/
|
|
49
|
+
getZeroRef() {
|
|
50
|
+
return this.zeroRef;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Add a GPS event to the visualization.
|
|
54
|
+
* Creates both a raw GPS marker and a fused alignment marker.
|
|
55
|
+
*
|
|
56
|
+
* @param gpsCoords - GPS position as [x, y, z] meters from zero reference
|
|
57
|
+
* @param odomPosition - AR odometry position at the time of GPS reading
|
|
58
|
+
*/
|
|
59
|
+
addGpsEvent(gpsCoords, odomPosition) {
|
|
60
|
+
if (!this.zeroRef) {
|
|
61
|
+
log.warn("No zero reference set");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const scene = getScene();
|
|
65
|
+
if (!scene) {
|
|
66
|
+
log.warn("Scene not available");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const eventId = this.eventCounter++;
|
|
70
|
+
const rawMesh = this.createMarkerMesh(VIS_COLORS.RAW_GPS.hex, "raw-gps", eventId);
|
|
71
|
+
rawMesh.position.set(gpsCoords[0], gpsCoords[1], gpsCoords[2]);
|
|
72
|
+
scene.add(rawMesh);
|
|
73
|
+
this.rawGpsMarkers.push(rawMesh);
|
|
74
|
+
const arWorldGroup = getArWorldGroup();
|
|
75
|
+
if (!arWorldGroup) {
|
|
76
|
+
log.warn("arWorldGroup not available, skipping fused marker");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const fusedMesh = this.createMarkerMesh(VIS_COLORS.FUSED_VIO.hex, "fused", eventId);
|
|
80
|
+
fusedMesh.position.set(odomPosition[0], odomPosition[1], odomPosition[2]);
|
|
81
|
+
arWorldGroup.add(fusedMesh);
|
|
82
|
+
this.fusedMarkers.push(fusedMesh);
|
|
83
|
+
log.debug(`Added GPS event ${eventId}: raw=[${gpsCoords.join(",")}] odom=[${odomPosition.join(",")}]`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Add an alignment snapshot marker at the given NUE position.
|
|
87
|
+
* Snapshots represent the system's best GPS estimate at each alignment update.
|
|
88
|
+
* Added to scene root (GPS world space), not arWorldGroup.
|
|
89
|
+
*/
|
|
90
|
+
addAlignmentSnapshot(nuePosition) {
|
|
91
|
+
const scene = getScene();
|
|
92
|
+
if (!scene) {
|
|
93
|
+
log.warn("Scene not available for alignment snapshot");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const id = this.snapshotCounter++;
|
|
97
|
+
const mesh = this.createMarkerMesh(VIS_COLORS.ALIGNMENT_SNAPSHOT.hex, "alignment-snapshot", id, SNAPSHOT_MARKER_RADIUS, .5);
|
|
98
|
+
mesh.position.set(nuePosition[0], nuePosition[1], nuePosition[2]);
|
|
99
|
+
scene.add(mesh);
|
|
100
|
+
this.snapshotMarkers.push(mesh);
|
|
101
|
+
log.debug(`Added alignment snapshot ${id}: pos=[${nuePosition.join(",")}]`);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the NUE positions of all alignment snapshot markers.
|
|
105
|
+
* Used at session end to convert to GPS coordinates for the summary map.
|
|
106
|
+
*/
|
|
107
|
+
getAlignmentSnapshotPositions() {
|
|
108
|
+
return this.snapshotMarkers.map((m) => [
|
|
109
|
+
m.position.x,
|
|
110
|
+
m.position.y,
|
|
111
|
+
m.position.z
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a sphere marker mesh with the given color and name.
|
|
116
|
+
*/
|
|
117
|
+
createMarkerMesh(color, namePrefix, eventId, radius = GPS_MARKER_RADIUS, opacity = .3) {
|
|
118
|
+
const geometry = new THREE.SphereGeometry(radius, 12, 12);
|
|
119
|
+
const material = new THREE.MeshBasicMaterial({
|
|
120
|
+
color,
|
|
121
|
+
transparent: true,
|
|
122
|
+
opacity,
|
|
123
|
+
depthWrite: false
|
|
124
|
+
});
|
|
125
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
126
|
+
mesh.name = `${namePrefix}-${eventId}`;
|
|
127
|
+
return mesh;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear all markers from the scene and reset state.
|
|
131
|
+
*/
|
|
132
|
+
clearAll() {
|
|
133
|
+
const scene = getScene();
|
|
134
|
+
const arWorldGroup = getArWorldGroup();
|
|
135
|
+
disposeMeshArray(this.rawGpsMarkers, scene);
|
|
136
|
+
disposeMeshArray(this.fusedMarkers, arWorldGroup);
|
|
137
|
+
disposeMeshArray(this.snapshotMarkers, scene);
|
|
138
|
+
this.zeroRef = null;
|
|
139
|
+
this.eventCounter = 0;
|
|
140
|
+
this.snapshotCounter = 0;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get count of visible markers.
|
|
144
|
+
*/
|
|
145
|
+
getCounts() {
|
|
146
|
+
return {
|
|
147
|
+
raw: this.rawGpsMarkers.length,
|
|
148
|
+
fused: this.fusedMarkers.length,
|
|
149
|
+
snapshots: this.snapshotMarkers.length
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Singleton instance for global use.
|
|
155
|
+
* Import this to visualize GPS events.
|
|
156
|
+
*/
|
|
157
|
+
const gpsEventVisualizer = new GpsEventVisualizer();
|
|
158
|
+
//#endregion
|
|
159
|
+
export { GpsEventVisualizer, gpsEventVisualizer };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { n as createCameraFollower, t as CameraFollower } from "../camera-follower-BMqPddxx.js";
|
|
2
|
+
import { n as createAlignmentLerper, t as AlignmentLerper } from "../alignment-lerper-D9BeDwZX.js";
|
|
3
|
+
import { n as createCss3dRendererManager, t as Css3dRendererManager } from "../css3d-renderer-manager-rgLMVAvd.js";
|
|
4
|
+
import { i as createGpsCompassCubes, n as COMPASS_CUBE_SIZE, r as GpsCompassCubes, t as COMPASS_CUBE_DISTANCE } from "../gps-compass-cubes-C-6JEnpR.js";
|
|
5
|
+
import { n as gpsEventVisualizer, t as GpsEventVisualizer } from "../gps-event-markers-DpEy2qRd.js";
|
|
6
|
+
import { a as DEFAULT_Z_OFFSET, n as DEFAULT_LEAFLET_MAP_SIZE_PX, o as LeafletMapOverlay, r as DEFAULT_WORLD_SIZE, s as LeafletMapOverlayOptions } from "../leaflet-map-overlay-CvfQXTye.js";
|
|
7
|
+
import { n as clampedAlpha, t as DEFAULT_LERP_RATE } from "../lerp-utils-87NWjUOD.js";
|
|
8
|
+
import { a as MapOverlayOptions, c as tileXYToLatLon, i as MapOverlay, n as DEFAULT_MAP_SIZE, o as TextureLoaderInterface, r as DEFAULT_ZOOM, s as latLonToTileXY, t as DEFAULT_HEIGHT_OFFSET } from "../map-overlay-DuiZuCTx.js";
|
|
9
|
+
import { n as refPointVisualizer, t as RefPointVisualizer } from "../reference-points-CFWy8_wj.js";
|
|
10
|
+
import { n as disposeMeshArray, r as disposeObject3D, t as DisposeOptions } from "../three-dispose-CCBHjE_a.js";
|
|
11
|
+
import { t as VIS_COLORS } from "../vis-colors-Qi2vlRGt.js";
|
|
12
|
+
export { type AlignmentLerper, COMPASS_CUBE_DISTANCE, COMPASS_CUBE_SIZE, type CameraFollower, type Css3dRendererManager, DEFAULT_HEIGHT_OFFSET, DEFAULT_LEAFLET_MAP_SIZE_PX, DEFAULT_LERP_RATE, DEFAULT_MAP_SIZE, DEFAULT_WORLD_SIZE, DEFAULT_ZOOM, DEFAULT_Z_OFFSET, type DisposeOptions, type GpsCompassCubes, GpsEventVisualizer, LeafletMapOverlay, type LeafletMapOverlayOptions, MapOverlay, type MapOverlayOptions, RefPointVisualizer, type TextureLoaderInterface, VIS_COLORS, clampedAlpha, createAlignmentLerper, createCameraFollower, createCss3dRendererManager, createGpsCompassCubes, disposeMeshArray, disposeObject3D, gpsEventVisualizer, latLonToTileXY, refPointVisualizer, tileXYToLatLon };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { disposeMeshArray, disposeObject3D } from "./three-dispose.js";
|
|
2
|
+
import { createCss3dRendererManager } from "./css3d-renderer-manager.js";
|
|
3
|
+
import { DEFAULT_LERP_RATE, clampedAlpha } from "./lerp-utils.js";
|
|
4
|
+
import { createCameraFollower } from "./camera-follower.js";
|
|
5
|
+
import { createAlignmentLerper } from "./alignment-lerper.js";
|
|
6
|
+
import { VIS_COLORS } from "./vis-colors.js";
|
|
7
|
+
import { COMPASS_CUBE_DISTANCE, COMPASS_CUBE_SIZE, createGpsCompassCubes } from "./gps-compass-cubes.js";
|
|
8
|
+
import { GpsEventVisualizer, gpsEventVisualizer } from "./gps-event-markers.js";
|
|
9
|
+
import { DEFAULT_LEAFLET_MAP_SIZE_PX, DEFAULT_WORLD_SIZE, DEFAULT_Z_OFFSET, LeafletMapOverlay } from "./leaflet-map-overlay.js";
|
|
10
|
+
import { DEFAULT_HEIGHT_OFFSET, DEFAULT_MAP_SIZE, DEFAULT_ZOOM, MapOverlay, latLonToTileXY, tileXYToLatLon } from "./map-overlay.js";
|
|
11
|
+
import { RefPointVisualizer, refPointVisualizer } from "./reference-points.js";
|
|
12
|
+
export { COMPASS_CUBE_DISTANCE, COMPASS_CUBE_SIZE, DEFAULT_HEIGHT_OFFSET, DEFAULT_LEAFLET_MAP_SIZE_PX, DEFAULT_LERP_RATE, DEFAULT_MAP_SIZE, DEFAULT_WORLD_SIZE, DEFAULT_ZOOM, DEFAULT_Z_OFFSET, GpsEventVisualizer, LeafletMapOverlay, MapOverlay, RefPointVisualizer, VIS_COLORS, clampedAlpha, createAlignmentLerper, createCameraFollower, createCss3dRendererManager, createGpsCompassCubes, disposeMeshArray, disposeObject3D, gpsEventVisualizer, latLonToTileXY, refPointVisualizer, tileXYToLatLon };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as DEFAULT_Z_OFFSET, i as DEFAULT_ZOOM, n as DEFAULT_LEAFLET_MAP_SIZE_PX, o as LeafletMapOverlay, r as DEFAULT_WORLD_SIZE, s as LeafletMapOverlayOptions, t as DEFAULT_HEIGHT_OFFSET } from "../leaflet-map-overlay-CvfQXTye.js";
|
|
2
|
+
export { DEFAULT_HEIGHT_OFFSET, DEFAULT_LEAFLET_MAP_SIZE_PX, DEFAULT_WORLD_SIZE, DEFAULT_ZOOM, DEFAULT_Z_OFFSET, LeafletMapOverlay, LeafletMapOverlayOptions };
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
import { VIS_COLORS } from "./vis-colors.js";
|
|
3
|
+
import { CSS3DObject } from "three/addons/renderers/CSS3DRenderer.js";
|
|
4
|
+
import L from "leaflet";
|
|
5
|
+
//#region ../src/visualization/leaflet-map-overlay.ts
|
|
6
|
+
const log = createLogger("LeafletMapOverlay");
|
|
7
|
+
/** Default pixel size of the Leaflet map container */
|
|
8
|
+
const DEFAULT_LEAFLET_MAP_SIZE_PX = 600;
|
|
9
|
+
/** Default world-space size in meters (matches old MapOverlay) */
|
|
10
|
+
const DEFAULT_WORLD_SIZE = 10;
|
|
11
|
+
/** Default height offset below camera (negative = below) */
|
|
12
|
+
const DEFAULT_HEIGHT_OFFSET = -4;
|
|
13
|
+
/** Default forward offset from parent (negative = forward in parent-local Z) */
|
|
14
|
+
const DEFAULT_Z_OFFSET = -1;
|
|
15
|
+
/** Default zoom level for the Leaflet map */
|
|
16
|
+
const DEFAULT_ZOOM = 17;
|
|
17
|
+
/** Minimum allowed zoom level */
|
|
18
|
+
const MIN_ZOOM = 0;
|
|
19
|
+
/** Maximum allowed zoom level */
|
|
20
|
+
const MAX_ZOOM = 19;
|
|
21
|
+
var LeafletMapOverlay = class {
|
|
22
|
+
scene;
|
|
23
|
+
camera;
|
|
24
|
+
mapParent;
|
|
25
|
+
worldSize;
|
|
26
|
+
heightOffset;
|
|
27
|
+
mapSizePx;
|
|
28
|
+
tileServerUrl;
|
|
29
|
+
onTileError;
|
|
30
|
+
offscreenRoot;
|
|
31
|
+
zoomLevel;
|
|
32
|
+
gpsPosition = null;
|
|
33
|
+
visible = false;
|
|
34
|
+
mapContainer = null;
|
|
35
|
+
leafletMap = null;
|
|
36
|
+
tileLayer = null;
|
|
37
|
+
cssObject = null;
|
|
38
|
+
rawGpsPoints = [];
|
|
39
|
+
fusedPoints = [];
|
|
40
|
+
snapshotPoints = [];
|
|
41
|
+
refPoints = [];
|
|
42
|
+
userMarker = null;
|
|
43
|
+
rawGpsPolyline = null;
|
|
44
|
+
fusedPolyline = null;
|
|
45
|
+
snapshotPolyline = null;
|
|
46
|
+
constructor(scene, camera, options = {}) {
|
|
47
|
+
this.scene = scene;
|
|
48
|
+
this.camera = camera;
|
|
49
|
+
this.mapParent = options.mapParent ?? camera;
|
|
50
|
+
this.worldSize = options.worldSize ?? 10;
|
|
51
|
+
this.heightOffset = options.heightOffset ?? -4;
|
|
52
|
+
this.mapSizePx = options.mapSizePx ?? 600;
|
|
53
|
+
this.zoomLevel = options.zoomLevel ?? 17;
|
|
54
|
+
this.tileServerUrl = options.tileServerUrl ?? "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
|
|
55
|
+
this.onTileError = options.onTileError;
|
|
56
|
+
this.offscreenRoot = options.offscreenRoot ?? document.body;
|
|
57
|
+
}
|
|
58
|
+
setGpsPosition(lat, lon) {
|
|
59
|
+
this.gpsPosition = {
|
|
60
|
+
lat,
|
|
61
|
+
lon
|
|
62
|
+
};
|
|
63
|
+
if (this.leafletMap) {
|
|
64
|
+
this.leafletMap.setView([lat, lon], this.zoomLevel);
|
|
65
|
+
this.updateUserMarker();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
getGpsPosition() {
|
|
69
|
+
return this.gpsPosition;
|
|
70
|
+
}
|
|
71
|
+
show() {
|
|
72
|
+
if (!this.gpsPosition) {
|
|
73
|
+
log.warn("Cannot show map — no GPS position set");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (this.visible) return;
|
|
77
|
+
this.createLeafletMap();
|
|
78
|
+
this.createCssObject();
|
|
79
|
+
this.applyBufferedOverlays();
|
|
80
|
+
this.visible = true;
|
|
81
|
+
log.info("Leaflet map overlay shown");
|
|
82
|
+
}
|
|
83
|
+
hide() {
|
|
84
|
+
if (!this.visible) return;
|
|
85
|
+
this.removeCssObject();
|
|
86
|
+
this.visible = false;
|
|
87
|
+
log.info("Leaflet map overlay hidden");
|
|
88
|
+
}
|
|
89
|
+
toggle() {
|
|
90
|
+
if (this.visible) this.hide();
|
|
91
|
+
else this.show();
|
|
92
|
+
}
|
|
93
|
+
isVisible() {
|
|
94
|
+
return this.visible;
|
|
95
|
+
}
|
|
96
|
+
addRawGpsPoint(lat, lon) {
|
|
97
|
+
this.rawGpsPoints.push([lat, lon]);
|
|
98
|
+
if (this.leafletMap) {
|
|
99
|
+
if (!this.rawGpsPolyline) this.rawGpsPolyline = L.polyline([], {
|
|
100
|
+
color: VIS_COLORS.RAW_GPS.css,
|
|
101
|
+
weight: 3,
|
|
102
|
+
opacity: .8
|
|
103
|
+
}).addTo(this.leafletMap);
|
|
104
|
+
this.rawGpsPolyline.addLatLng([lat, lon]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
addFusedPoint(lat, lon) {
|
|
108
|
+
this.fusedPoints.push([lat, lon]);
|
|
109
|
+
if (this.leafletMap) {
|
|
110
|
+
if (!this.fusedPolyline) this.fusedPolyline = L.polyline([], {
|
|
111
|
+
color: VIS_COLORS.FUSED_VIO.css,
|
|
112
|
+
weight: 3,
|
|
113
|
+
opacity: .8
|
|
114
|
+
}).addTo(this.leafletMap);
|
|
115
|
+
this.fusedPolyline.addLatLng([lat, lon]);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
addAlignmentSnapshot(lat, lon) {
|
|
119
|
+
this.snapshotPoints.push([lat, lon]);
|
|
120
|
+
if (this.leafletMap) {
|
|
121
|
+
if (!this.snapshotPolyline) this.snapshotPolyline = L.polyline([], {
|
|
122
|
+
color: VIS_COLORS.ALIGNMENT_SNAPSHOT.css,
|
|
123
|
+
weight: 3,
|
|
124
|
+
opacity: .8
|
|
125
|
+
}).addTo(this.leafletMap);
|
|
126
|
+
this.snapshotPolyline.addLatLng([lat, lon]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
addRefPoint(lat, lon, name) {
|
|
130
|
+
const marker = this.leafletMap ? this.createRefPointMarker(lat, lon, name, false) : null;
|
|
131
|
+
this.refPoints.push({
|
|
132
|
+
lat,
|
|
133
|
+
lng: lon,
|
|
134
|
+
name,
|
|
135
|
+
isPrior: false,
|
|
136
|
+
marker
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
addPriorRefPoint(lat, lon, name) {
|
|
140
|
+
const marker = this.leafletMap ? this.createRefPointMarker(lat, lon, name, true) : null;
|
|
141
|
+
this.refPoints.push({
|
|
142
|
+
lat,
|
|
143
|
+
lng: lon,
|
|
144
|
+
name,
|
|
145
|
+
isPrior: true,
|
|
146
|
+
marker
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
addPriorRefPoints(refPoints) {
|
|
150
|
+
for (const rp of refPoints) this.addPriorRefPoint(rp.lat, rp.lon, rp.name);
|
|
151
|
+
}
|
|
152
|
+
clearPriorRefPoints() {
|
|
153
|
+
this.refPoints = this.refPoints.filter((rp) => {
|
|
154
|
+
if (rp.isPrior) {
|
|
155
|
+
rp.marker?.remove();
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
setZoomLevel(zoom) {
|
|
162
|
+
this.zoomLevel = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom));
|
|
163
|
+
if (this.leafletMap) this.leafletMap.setZoom(this.zoomLevel);
|
|
164
|
+
}
|
|
165
|
+
getZoomLevel() {
|
|
166
|
+
return this.zoomLevel;
|
|
167
|
+
}
|
|
168
|
+
/** Increment zoom level by 1 (clamped at MAX_ZOOM). */
|
|
169
|
+
zoomIn() {
|
|
170
|
+
this.setZoomLevel(this.zoomLevel + 1);
|
|
171
|
+
}
|
|
172
|
+
/** Decrement zoom level by 1 (clamped at MIN_ZOOM). */
|
|
173
|
+
zoomOut() {
|
|
174
|
+
this.setZoomLevel(this.zoomLevel - 1);
|
|
175
|
+
}
|
|
176
|
+
getWorldSize() {
|
|
177
|
+
return this.worldSize;
|
|
178
|
+
}
|
|
179
|
+
getHeightOffset() {
|
|
180
|
+
return this.heightOffset;
|
|
181
|
+
}
|
|
182
|
+
getMapSizePx() {
|
|
183
|
+
return this.mapSizePx;
|
|
184
|
+
}
|
|
185
|
+
/** Expose the Leaflet map instance for testing and advanced usage. */
|
|
186
|
+
getLeafletMap() {
|
|
187
|
+
return this.leafletMap;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* No-op — kept for backward compatibility with the frame-loop call
|
|
191
|
+
* in main.ts. The CSS3DObject is a child of the parent, so Three.js
|
|
192
|
+
* scene graph propagation handles positioning automatically.
|
|
193
|
+
*/
|
|
194
|
+
updatePosition() {}
|
|
195
|
+
dispose() {
|
|
196
|
+
this.hide();
|
|
197
|
+
this.destroyLeafletMap();
|
|
198
|
+
this.rawGpsPoints = [];
|
|
199
|
+
this.fusedPoints = [];
|
|
200
|
+
this.snapshotPoints = [];
|
|
201
|
+
this.refPoints = [];
|
|
202
|
+
this.cssObject = null;
|
|
203
|
+
log.info("Leaflet map overlay disposed");
|
|
204
|
+
}
|
|
205
|
+
createLeafletMap() {
|
|
206
|
+
if (this.leafletMap) return;
|
|
207
|
+
this.mapContainer = document.createElement("div");
|
|
208
|
+
this.mapContainer.style.width = `${this.mapSizePx}px`;
|
|
209
|
+
this.mapContainer.style.height = `${this.mapSizePx}px`;
|
|
210
|
+
this.mapContainer.style.background = "white";
|
|
211
|
+
this.mapContainer.style.position = "fixed";
|
|
212
|
+
this.mapContainer.style.left = "-9999px";
|
|
213
|
+
this.mapContainer.style.top = "-9999px";
|
|
214
|
+
this.mapContainer.style.pointerEvents = "none";
|
|
215
|
+
this.offscreenRoot.appendChild(this.mapContainer);
|
|
216
|
+
const pos = this.gpsPosition;
|
|
217
|
+
this.leafletMap = L.map(this.mapContainer, {
|
|
218
|
+
center: [pos.lat, pos.lon],
|
|
219
|
+
zoom: this.zoomLevel,
|
|
220
|
+
zoomControl: false,
|
|
221
|
+
attributionControl: false
|
|
222
|
+
});
|
|
223
|
+
this.tileLayer = L.tileLayer(this.tileServerUrl, { maxZoom: MAX_ZOOM });
|
|
224
|
+
this.tileLayer.on("tileerror", (e) => {
|
|
225
|
+
log.error("Failed to load map tile:", e.error);
|
|
226
|
+
this.onTileError?.(e.error);
|
|
227
|
+
});
|
|
228
|
+
this.tileLayer.addTo(this.leafletMap);
|
|
229
|
+
}
|
|
230
|
+
destroyLeafletMap() {
|
|
231
|
+
this.userMarker?.remove();
|
|
232
|
+
this.userMarker = null;
|
|
233
|
+
this.rawGpsPolyline?.remove();
|
|
234
|
+
this.rawGpsPolyline = null;
|
|
235
|
+
this.fusedPolyline?.remove();
|
|
236
|
+
this.fusedPolyline = null;
|
|
237
|
+
this.snapshotPolyline?.remove();
|
|
238
|
+
this.snapshotPolyline = null;
|
|
239
|
+
for (const rp of this.refPoints) {
|
|
240
|
+
rp.marker?.remove();
|
|
241
|
+
rp.marker = null;
|
|
242
|
+
}
|
|
243
|
+
if (this.leafletMap) {
|
|
244
|
+
this.leafletMap.remove();
|
|
245
|
+
this.leafletMap = null;
|
|
246
|
+
}
|
|
247
|
+
if (this.mapContainer?.parentElement) this.mapContainer.parentElement.removeChild(this.mapContainer);
|
|
248
|
+
this.mapContainer = null;
|
|
249
|
+
this.tileLayer = null;
|
|
250
|
+
}
|
|
251
|
+
createCssObject() {
|
|
252
|
+
if (!this.mapContainer) return;
|
|
253
|
+
if (this.cssObject) {
|
|
254
|
+
this.mapParent.add(this.cssObject);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.mapContainer.style.position = "";
|
|
258
|
+
this.mapContainer.style.left = "";
|
|
259
|
+
this.mapContainer.style.top = "";
|
|
260
|
+
this.cssObject = new CSS3DObject(this.mapContainer);
|
|
261
|
+
const scale = this.worldSize / this.mapSizePx;
|
|
262
|
+
this.cssObject.scale.set(scale, scale, scale);
|
|
263
|
+
this.cssObject.position.set(0, this.heightOffset, -1);
|
|
264
|
+
this.cssObject.rotation.x = -Math.PI / 2;
|
|
265
|
+
this.mapParent.add(this.cssObject);
|
|
266
|
+
}
|
|
267
|
+
removeCssObject() {
|
|
268
|
+
if (this.cssObject) this.mapParent.remove(this.cssObject);
|
|
269
|
+
}
|
|
270
|
+
applyBufferedOverlays() {
|
|
271
|
+
if (!this.leafletMap) return;
|
|
272
|
+
this.updateUserMarker();
|
|
273
|
+
if (this.rawGpsPoints.length > 0) this.rawGpsPolyline = L.polyline(this.rawGpsPoints, {
|
|
274
|
+
color: VIS_COLORS.RAW_GPS.css,
|
|
275
|
+
weight: 3,
|
|
276
|
+
opacity: .8
|
|
277
|
+
}).addTo(this.leafletMap);
|
|
278
|
+
if (this.fusedPoints.length > 0) this.fusedPolyline = L.polyline(this.fusedPoints, {
|
|
279
|
+
color: VIS_COLORS.FUSED_VIO.css,
|
|
280
|
+
weight: 3,
|
|
281
|
+
opacity: .8
|
|
282
|
+
}).addTo(this.leafletMap);
|
|
283
|
+
if (this.snapshotPoints.length > 0) this.snapshotPolyline = L.polyline(this.snapshotPoints, {
|
|
284
|
+
color: VIS_COLORS.ALIGNMENT_SNAPSHOT.css,
|
|
285
|
+
weight: 3,
|
|
286
|
+
opacity: .8
|
|
287
|
+
}).addTo(this.leafletMap);
|
|
288
|
+
for (const rp of this.refPoints) if (!rp.marker) rp.marker = this.createRefPointMarker(rp.lat, rp.lng, rp.name, rp.isPrior);
|
|
289
|
+
}
|
|
290
|
+
updateUserMarker() {
|
|
291
|
+
if (!this.gpsPosition || !this.leafletMap) return;
|
|
292
|
+
if (this.userMarker) this.userMarker.setLatLng([this.gpsPosition.lat, this.gpsPosition.lon]);
|
|
293
|
+
else this.userMarker = L.marker([this.gpsPosition.lat, this.gpsPosition.lon], { icon: L.divIcon({
|
|
294
|
+
className: "",
|
|
295
|
+
html: `<div style="background:${VIS_COLORS.USER_POSITION.css};width:14px;height:14px;border-radius:50%;border:3px solid white;box-shadow:0 0 6px rgba(59,130,246,0.6);"></div>`,
|
|
296
|
+
iconSize: [14, 14],
|
|
297
|
+
iconAnchor: [7, 7]
|
|
298
|
+
}) }).addTo(this.leafletMap);
|
|
299
|
+
}
|
|
300
|
+
createRefPointMarker(lat, lon, name, isPrior) {
|
|
301
|
+
const color = isPrior ? VIS_COLORS.PRIOR_REF_POINT.css : VIS_COLORS.CURRENT_REF_POINT.css;
|
|
302
|
+
const opacity = isPrior ? "opacity:0.8;" : "";
|
|
303
|
+
const label = isPrior ? `📌 ${name} (prior)` : name;
|
|
304
|
+
return L.marker([lat, lon], { icon: L.divIcon({
|
|
305
|
+
className: "",
|
|
306
|
+
html: `<div style="background:${color};width:12px;height:12px;border-radius:50%;border:2px solid white;${opacity}"></div>`,
|
|
307
|
+
iconSize: [12, 12],
|
|
308
|
+
iconAnchor: [6, 6]
|
|
309
|
+
}) }).bindPopup(label).addTo(this.leafletMap);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
//#endregion
|
|
313
|
+
export { DEFAULT_HEIGHT_OFFSET, DEFAULT_LEAFLET_MAP_SIZE_PX, DEFAULT_WORLD_SIZE, DEFAULT_ZOOM, DEFAULT_Z_OFFSET, LeafletMapOverlay };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region ../src/visualization/lerp-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared lerp utilities for visualization modules.
|
|
4
|
+
*
|
|
5
|
+
* Both alignment-lerper and camera-follower need the same smoothing
|
|
6
|
+
* rate constant and clamped-alpha formula. Centralised here (R3) to
|
|
7
|
+
* keep the two modules in sync.
|
|
8
|
+
*/
|
|
9
|
+
/** Default speed multiplier — ~90 % convergence in ~0.3 s at 60 fps. */
|
|
10
|
+
const DEFAULT_LERP_RATE = 8;
|
|
11
|
+
/**
|
|
12
|
+
* Compute a frame-rate-independent lerp/slerp factor clamped to [0, 1].
|
|
13
|
+
*
|
|
14
|
+
* @param lerpRate Speed multiplier (higher = faster convergence).
|
|
15
|
+
* @param dt Delta time in seconds since last frame.
|
|
16
|
+
* @returns The interpolation alpha, guaranteed ≤ 1.0.
|
|
17
|
+
*/
|
|
18
|
+
function clampedAlpha(lerpRate, dt) {
|
|
19
|
+
return Math.min(lerpRate * dt, 1);
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { DEFAULT_LERP_RATE, clampedAlpha };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as MapOverlayOptions, c as tileXYToLatLon, i as MapOverlay, n as DEFAULT_MAP_SIZE, o as TextureLoaderInterface, r as DEFAULT_ZOOM, s as latLonToTileXY, t as DEFAULT_HEIGHT_OFFSET } from "../map-overlay-DuiZuCTx.js";
|
|
2
|
+
export { DEFAULT_HEIGHT_OFFSET, DEFAULT_MAP_SIZE, DEFAULT_ZOOM, MapOverlay, MapOverlayOptions, TextureLoaderInterface, latLonToTileXY, tileXYToLatLon };
|