@faclon-labs/app-sdk 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -1
- package/dist/IOSenseApp.js +26 -6
- package/dist/SignIn.js +2 -2
- package/dist/auth.d.ts +7 -0
- package/dist/auth.js +35 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/map/globe.d.ts +5 -0
- package/dist/map/globe.js +5 -0
- package/dist/map/index.d.ts +21 -0
- package/dist/map/index.js +21 -0
- package/dist/map/source.d.ts +48 -0
- package/dist/map/source.js +73 -0
- package/dist/map/testing.d.ts +5 -0
- package/dist/map/testing.js +5 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/package.json +55 -5
- package/vendor/SOURCE.txt +1 -0
- package/vendor/map-engine/cesium/camera.d.ts +10 -0
- package/vendor/map-engine/cesium/camera.js +25 -0
- package/vendor/map-engine/cesium/index.d.ts +2 -0
- package/vendor/map-engine/cesium/index.js +2 -0
- package/vendor/map-engine/cesium/renderer.d.ts +149 -0
- package/vendor/map-engine/cesium/renderer.js +837 -0
- package/vendor/map-engine/commands/index.d.ts +2 -0
- package/vendor/map-engine/commands/index.js +2 -0
- package/vendor/map-engine/commands/post-message.d.ts +32 -0
- package/vendor/map-engine/commands/post-message.js +77 -0
- package/vendor/map-engine/commands/webmcp.d.ts +17 -0
- package/vendor/map-engine/commands/webmcp.js +59 -0
- package/vendor/map-engine/core/commands.d.ts +82 -0
- package/vendor/map-engine/core/commands.js +46 -0
- package/vendor/map-engine/core/emitter.d.ts +9 -0
- package/vendor/map-engine/core/emitter.js +38 -0
- package/vendor/map-engine/core/equal.d.ts +2 -0
- package/vendor/map-engine/core/equal.js +28 -0
- package/vendor/map-engine/core/errors.d.ts +20 -0
- package/vendor/map-engine/core/errors.js +34 -0
- package/vendor/map-engine/core/geometry-family.d.ts +3 -0
- package/vendor/map-engine/core/geometry-family.js +13 -0
- package/vendor/map-engine/core/index.d.ts +4 -0
- package/vendor/map-engine/core/index.js +3 -0
- package/vendor/map-engine/core/types.d.ts +709 -0
- package/vendor/map-engine/core/types.js +7 -0
- package/vendor/map-engine/data/annotations.d.ts +3 -0
- package/vendor/map-engine/data/annotations.js +63 -0
- package/vendor/map-engine/data/contracts.d.ts +172 -0
- package/vendor/map-engine/data/contracts.js +12 -0
- package/vendor/map-engine/data/index.d.ts +4 -0
- package/vendor/map-engine/data/index.js +3 -0
- package/vendor/map-engine/data/schema.d.ts +7 -0
- package/vendor/map-engine/data/schema.js +40 -0
- package/vendor/map-engine/geofence/evaluator.d.ts +35 -0
- package/vendor/map-engine/geofence/evaluator.js +172 -0
- package/vendor/map-engine/geofence/history.d.ts +9 -0
- package/vendor/map-engine/geofence/history.js +19 -0
- package/vendor/map-engine/geofence/index.d.ts +2 -0
- package/vendor/map-engine/geofence/index.js +2 -0
- package/vendor/map-engine/index.d.ts +27 -0
- package/vendor/map-engine/index.js +14 -0
- package/vendor/map-engine/layers/index.d.ts +1 -0
- package/vendor/map-engine/layers/index.js +1 -0
- package/vendor/map-engine/layers/layers.d.ts +49 -0
- package/vendor/map-engine/layers/layers.js +316 -0
- package/vendor/map-engine/maplibre/deck-host.d.ts +105 -0
- package/vendor/map-engine/maplibre/deck-host.js +401 -0
- package/vendor/map-engine/maplibre/icons.d.ts +17 -0
- package/vendor/map-engine/maplibre/icons.js +65 -0
- package/vendor/map-engine/maplibre/index.d.ts +3 -0
- package/vendor/map-engine/maplibre/index.js +2 -0
- package/vendor/map-engine/maplibre/map-like.d.ts +56 -0
- package/vendor/map-engine/maplibre/map-like.js +1 -0
- package/vendor/map-engine/maplibre/renderer.d.ts +173 -0
- package/vendor/map-engine/maplibre/renderer.js +889 -0
- package/vendor/map-engine/maplibre/style.d.ts +84 -0
- package/vendor/map-engine/maplibre/style.js +493 -0
- package/vendor/map-engine/present/decorations.d.ts +25 -0
- package/vendor/map-engine/present/decorations.js +115 -0
- package/vendor/map-engine/present/index.d.ts +2 -0
- package/vendor/map-engine/present/index.js +2 -0
- package/vendor/map-engine/present/present.d.ts +47 -0
- package/vendor/map-engine/present/present.js +149 -0
- package/vendor/map-engine/query/index.d.ts +1 -0
- package/vendor/map-engine/query/index.js +1 -0
- package/vendor/map-engine/query/planner.d.ts +26 -0
- package/vendor/map-engine/query/planner.js +47 -0
- package/vendor/map-engine/react/MapView.d.ts +87 -0
- package/vendor/map-engine/react/MapView.js +270 -0
- package/vendor/map-engine/react/context.d.ts +39 -0
- package/vendor/map-engine/react/context.js +94 -0
- package/vendor/map-engine/react/index.d.ts +2 -0
- package/vendor/map-engine/react/index.js +2 -0
- package/vendor/map-engine/react/store.d.ts +19 -0
- package/vendor/map-engine/react/store.js +18 -0
- package/vendor/map-engine/realtime/apply.d.ts +74 -0
- package/vendor/map-engine/realtime/apply.js +155 -0
- package/vendor/map-engine/realtime/index.d.ts +1 -0
- package/vendor/map-engine/realtime/index.js +1 -0
- package/vendor/map-engine/render/index.d.ts +1 -0
- package/vendor/map-engine/render/index.js +1 -0
- package/vendor/map-engine/render/offline.d.ts +8 -0
- package/vendor/map-engine/render/offline.js +47 -0
- package/vendor/map-engine/render/renderer.d.ts +74 -0
- package/vendor/map-engine/render/renderer.js +1 -0
- package/vendor/map-engine/render/terrarium.d.ts +48 -0
- package/vendor/map-engine/render/terrarium.js +110 -0
- package/vendor/map-engine/runtime/drawing.d.ts +28 -0
- package/vendor/map-engine/runtime/drawing.js +58 -0
- package/vendor/map-engine/runtime/index.d.ts +5 -0
- package/vendor/map-engine/runtime/index.js +2 -0
- package/vendor/map-engine/runtime/offline.d.ts +24 -0
- package/vendor/map-engine/runtime/offline.js +37 -0
- package/vendor/map-engine/runtime/overlays.d.ts +25 -0
- package/vendor/map-engine/runtime/overlays.js +65 -0
- package/vendor/map-engine/runtime/runtime.d.ts +4 -0
- package/vendor/map-engine/runtime/runtime.js +2105 -0
- package/vendor/map-engine/runtime/types.d.ts +407 -0
- package/vendor/map-engine/runtime/types.js +1 -0
- package/vendor/map-engine/scene/index.d.ts +1 -0
- package/vendor/map-engine/scene/index.js +1 -0
- package/vendor/map-engine/scene/scene.d.ts +31 -0
- package/vendor/map-engine/scene/scene.js +216 -0
- package/vendor/map-engine/selection/index.d.ts +1 -0
- package/vendor/map-engine/selection/index.js +1 -0
- package/vendor/map-engine/selection/selection.d.ts +12 -0
- package/vendor/map-engine/selection/selection.js +28 -0
- package/vendor/map-engine/spatial/geojson.d.ts +15 -0
- package/vendor/map-engine/spatial/geojson.js +83 -0
- package/vendor/map-engine/spatial/geometry.d.ts +13 -0
- package/vendor/map-engine/spatial/geometry.js +206 -0
- package/vendor/map-engine/spatial/import.d.ts +27 -0
- package/vendor/map-engine/spatial/import.js +96 -0
- package/vendor/map-engine/spatial/index.d.ts +38 -0
- package/vendor/map-engine/spatial/index.js +37 -0
- package/vendor/map-engine/spatial/line-of-sight.d.ts +35 -0
- package/vendor/map-engine/spatial/line-of-sight.js +43 -0
- package/vendor/map-engine/spatial/measure.d.ts +28 -0
- package/vendor/map-engine/spatial/measure.js +90 -0
- package/vendor/map-engine/spatial/operations.d.ts +21 -0
- package/vendor/map-engine/spatial/operations.js +171 -0
- package/vendor/map-engine/spatial/query.d.ts +17 -0
- package/vendor/map-engine/spatial/query.js +88 -0
- package/vendor/map-engine/spatial/spatial-index.d.ts +24 -0
- package/vendor/map-engine/spatial/spatial-index.js +36 -0
- package/vendor/map-engine/spatial/volume-mesh.d.ts +19 -0
- package/vendor/map-engine/spatial/volume-mesh.js +56 -0
- package/vendor/map-engine/temporal/index.d.ts +3 -0
- package/vendor/map-engine/temporal/index.js +3 -0
- package/vendor/map-engine/temporal/journal.d.ts +20 -0
- package/vendor/map-engine/temporal/journal.js +34 -0
- package/vendor/map-engine/temporal/playback.d.ts +41 -0
- package/vendor/map-engine/temporal/playback.js +80 -0
- package/vendor/map-engine/temporal/tracks.d.ts +33 -0
- package/vendor/map-engine/temporal/tracks.js +119 -0
- package/vendor/map-engine/testing/annotation-store.d.ts +24 -0
- package/vendor/map-engine/testing/annotation-store.js +43 -0
- package/vendor/map-engine/testing/contracts.d.ts +41 -0
- package/vendor/map-engine/testing/contracts.js +328 -0
- package/vendor/map-engine/testing/fixtures-monitoring.d.ts +22 -0
- package/vendor/map-engine/testing/fixtures-monitoring.js +103 -0
- package/vendor/map-engine/testing/fixtures.d.ts +18 -0
- package/vendor/map-engine/testing/fixtures.js +28 -0
- package/vendor/map-engine/testing/index.d.ts +7 -0
- package/vendor/map-engine/testing/index.js +7 -0
- package/vendor/map-engine/testing/mock-ontology.d.ts +13 -0
- package/vendor/map-engine/testing/mock-ontology.js +24 -0
- package/vendor/map-engine/testing/mock-provider.d.ts +97 -0
- package/vendor/map-engine/testing/mock-provider.js +369 -0
- package/vendor/map-engine/testing/test-renderer.d.ts +72 -0
- package/vendor/map-engine/testing/test-renderer.js +153 -0
- package/vendor/map-engine/viewport/index.d.ts +1 -0
- package/vendor/map-engine/viewport/index.js +1 -0
- package/vendor/map-engine/viewport/viewport.d.ts +19 -0
- package/vendor/map-engine/viewport/viewport.js +102 -0
- package/vendor/map-provider-uns/client.d.ts +75 -0
- package/vendor/map-provider-uns/client.js +111 -0
- package/vendor/map-provider-uns/index.d.ts +2 -0
- package/vendor/map-provider-uns/index.js +2 -0
- package/vendor/map-provider-uns/provider.d.ts +131 -0
- package/vendor/map-provider-uns/provider.js +561 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A headless Renderer that records what it is told and lets tests play user input back.
|
|
3
|
+
* Its `objects` map is what a real renderer would be drawing: render() plus every update() applied.
|
|
4
|
+
*/
|
|
5
|
+
export class TestRenderer {
|
|
6
|
+
initialized = false;
|
|
7
|
+
destroyed = false;
|
|
8
|
+
options;
|
|
9
|
+
renders = [];
|
|
10
|
+
patches = [];
|
|
11
|
+
viewportsSet = [];
|
|
12
|
+
objects = new Map();
|
|
13
|
+
layers = [];
|
|
14
|
+
size;
|
|
15
|
+
/** When true, setViewport echoes back through onViewportChange, like MapLibre's moveend. */
|
|
16
|
+
echoViewport;
|
|
17
|
+
failInitialize = null;
|
|
18
|
+
listeners = {
|
|
19
|
+
viewport: new Set(),
|
|
20
|
+
pick: new Set(),
|
|
21
|
+
click: new Set(),
|
|
22
|
+
hover: new Set(),
|
|
23
|
+
error: new Set(),
|
|
24
|
+
};
|
|
25
|
+
capabilities;
|
|
26
|
+
/** Projections and terrains set after initialize (null = terrain off). */
|
|
27
|
+
projections = [];
|
|
28
|
+
terrains = [];
|
|
29
|
+
ground;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.size = options.size ?? { width: 800, height: 600 };
|
|
32
|
+
this.echoViewport = options.echoViewport ?? false;
|
|
33
|
+
this.capabilities = { ...(options.capabilities ?? {}), ...(options.ground ? { elevation: true } : {}) };
|
|
34
|
+
this.ground = options.ground;
|
|
35
|
+
if (options.setBasemap !== false) {
|
|
36
|
+
// Like a real style swap: the engine's layers and objects are gone until the next render().
|
|
37
|
+
this.setBasemap = async (basemap) => {
|
|
38
|
+
this.basemaps.push(structuredClone(basemap));
|
|
39
|
+
this.objects.clear();
|
|
40
|
+
this.layers = [];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
initializeCount = 0;
|
|
45
|
+
/** Basemaps swapped in with setBasemap(). */
|
|
46
|
+
basemaps = [];
|
|
47
|
+
/** Present unless constructed with `setBasemap: false` (then the runtime has to remount to change basemap). */
|
|
48
|
+
setBasemap;
|
|
49
|
+
async initialize(_container, options) {
|
|
50
|
+
if (this.failInitialize)
|
|
51
|
+
throw this.failInitialize;
|
|
52
|
+
this.initializeCount++;
|
|
53
|
+
this.options = structuredClone(options);
|
|
54
|
+
this.initialized = true;
|
|
55
|
+
this.destroyed = false;
|
|
56
|
+
this.objects.clear();
|
|
57
|
+
this.layers = [];
|
|
58
|
+
}
|
|
59
|
+
setViewport(viewport) {
|
|
60
|
+
this.viewportsSet.push(structuredClone(viewport));
|
|
61
|
+
if (this.echoViewport)
|
|
62
|
+
this.simulateViewportChange(viewport);
|
|
63
|
+
}
|
|
64
|
+
render(scene) {
|
|
65
|
+
this.renders.push(structuredClone(scene));
|
|
66
|
+
this.objects.clear();
|
|
67
|
+
for (const o of scene.objects)
|
|
68
|
+
this.objects.set(o.key, structuredClone(o));
|
|
69
|
+
this.layers = structuredClone(scene.layers);
|
|
70
|
+
}
|
|
71
|
+
update(patch) {
|
|
72
|
+
this.patches.push(structuredClone(patch));
|
|
73
|
+
for (const k of patch.removed ?? [])
|
|
74
|
+
this.objects.delete(k);
|
|
75
|
+
for (const o of [...(patch.added ?? []), ...(patch.updated ?? [])])
|
|
76
|
+
this.objects.set(o.key, structuredClone(o));
|
|
77
|
+
if (patch.layers)
|
|
78
|
+
this.layers = structuredClone(patch.layers);
|
|
79
|
+
}
|
|
80
|
+
resizeCount = 0;
|
|
81
|
+
resize() {
|
|
82
|
+
this.resizeCount++;
|
|
83
|
+
}
|
|
84
|
+
getSize() {
|
|
85
|
+
return this.size;
|
|
86
|
+
}
|
|
87
|
+
/** Test projection: the last viewport set, 1 degree = 1000 px from the centre (not a real projection). */
|
|
88
|
+
project(position) {
|
|
89
|
+
const v = this.viewportsSet.at(-1) ?? this.options?.viewport;
|
|
90
|
+
if (!v)
|
|
91
|
+
return null;
|
|
92
|
+
return { x: this.size.width / 2 + (position.longitude - v.center.longitude) * 1000, y: this.size.height / 2 - (position.latitude - v.center.latitude) * 1000 };
|
|
93
|
+
}
|
|
94
|
+
setProjection(projection) {
|
|
95
|
+
this.projections.push(projection);
|
|
96
|
+
}
|
|
97
|
+
setTerrain(terrain) {
|
|
98
|
+
this.terrains.push(terrain ? structuredClone(terrain) : null);
|
|
99
|
+
}
|
|
100
|
+
/** Heights from the `ground` function given to the constructor (a test terrain). */
|
|
101
|
+
async sampleElevation(positions) {
|
|
102
|
+
return positions.map((p) => this.ground?.(p) ?? null);
|
|
103
|
+
}
|
|
104
|
+
destroy() {
|
|
105
|
+
this.destroyed = true;
|
|
106
|
+
this.initialized = false;
|
|
107
|
+
for (const set of Object.values(this.listeners))
|
|
108
|
+
set.clear();
|
|
109
|
+
}
|
|
110
|
+
onViewportChange(l) {
|
|
111
|
+
return this.add(this.listeners.viewport, l);
|
|
112
|
+
}
|
|
113
|
+
onPick(l) {
|
|
114
|
+
return this.add(this.listeners.pick, l);
|
|
115
|
+
}
|
|
116
|
+
onMapClick(l) {
|
|
117
|
+
return this.add(this.listeners.click, l);
|
|
118
|
+
}
|
|
119
|
+
onHover(l) {
|
|
120
|
+
return this.add(this.listeners.hover, l);
|
|
121
|
+
}
|
|
122
|
+
onError(l) {
|
|
123
|
+
return this.add(this.listeners.error, l);
|
|
124
|
+
}
|
|
125
|
+
// --- simulated user input
|
|
126
|
+
simulateViewportChange(v) {
|
|
127
|
+
for (const l of this.listeners.viewport)
|
|
128
|
+
l(structuredClone(v));
|
|
129
|
+
}
|
|
130
|
+
simulatePick(objectId, layerId, position = { latitude: 0, longitude: 0 }) {
|
|
131
|
+
for (const l of this.listeners.pick)
|
|
132
|
+
l({ objectId, layerId, position });
|
|
133
|
+
}
|
|
134
|
+
simulateMapClick(position) {
|
|
135
|
+
for (const l of this.listeners.click)
|
|
136
|
+
l({ position });
|
|
137
|
+
}
|
|
138
|
+
simulateHover(e) {
|
|
139
|
+
for (const l of this.listeners.hover)
|
|
140
|
+
l(e);
|
|
141
|
+
}
|
|
142
|
+
simulateError(e) {
|
|
143
|
+
for (const l of this.listeners.error)
|
|
144
|
+
l(e);
|
|
145
|
+
}
|
|
146
|
+
listenerCount() {
|
|
147
|
+
return Object.values(this.listeners).reduce((n, s) => n + s.size, 0);
|
|
148
|
+
}
|
|
149
|
+
add(set, l) {
|
|
150
|
+
set.add(l);
|
|
151
|
+
return () => set.delete(l);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { computeBounds, normalizeViewport, validateViewport, zoomToFit, MAX_MERCATOR_LATITUDE, MAX_PITCH, MAX_ZOOM, MIN_ZOOM, TILE_SIZE } from './viewport.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { computeBounds, normalizeViewport, validateViewport, zoomToFit, MAX_MERCATOR_LATITUDE, MAX_PITCH, MAX_ZOOM, MIN_ZOOM, TILE_SIZE } from './viewport.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BoundingBox, MapViewport, Padding, Size, ValidationResult } from '../core/types.js';
|
|
2
|
+
/** Web Mercator cannot show the poles; MapLibre uses the same limit. */
|
|
3
|
+
export declare const MAX_MERCATOR_LATITUDE = 85.051129;
|
|
4
|
+
export declare const MIN_ZOOM = 0;
|
|
5
|
+
export declare const MAX_ZOOM = 24;
|
|
6
|
+
export declare const MAX_PITCH = 85;
|
|
7
|
+
/** MapLibre's tile size, so headless bounds match what the renderer shows. */
|
|
8
|
+
export declare const TILE_SIZE = 512;
|
|
9
|
+
export declare function validateViewport(value: unknown): ValidationResult;
|
|
10
|
+
/** Clamps latitude to the Mercator range and bearing to [0, 360). Call after validateViewport. */
|
|
11
|
+
export declare function normalizeViewport(v: MapViewport): MapViewport;
|
|
12
|
+
/**
|
|
13
|
+
* Visible bounds of a viewport on a container of `size` pixels.
|
|
14
|
+
* Accounts for bearing; ignores pitch (pitched views report their real bounds through the renderer).
|
|
15
|
+
* MVP does not wrap the antimeridian: longitudes are clamped to [-180, 180].
|
|
16
|
+
*/
|
|
17
|
+
export declare function computeBounds(viewport: MapViewport, size: Size, tileSize?: number): BoundingBox;
|
|
18
|
+
/** The zoom at which `bounds` fits in `size` pixels, with `padding` pixels on each side. Bearing 0. */
|
|
19
|
+
export declare function zoomToFit(bounds: BoundingBox, size: Size, padding?: Padding, tileSize?: number): MapViewport;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { validatePosition } from '../spatial/geometry.js';
|
|
2
|
+
/** Web Mercator cannot show the poles; MapLibre uses the same limit. */
|
|
3
|
+
export const MAX_MERCATOR_LATITUDE = 85.051129;
|
|
4
|
+
export const MIN_ZOOM = 0;
|
|
5
|
+
export const MAX_ZOOM = 24;
|
|
6
|
+
export const MAX_PITCH = 85;
|
|
7
|
+
/** MapLibre's tile size, so headless bounds match what the renderer shows. */
|
|
8
|
+
export const TILE_SIZE = 512;
|
|
9
|
+
export function validateViewport(value) {
|
|
10
|
+
if (typeof value !== 'object' || value === null)
|
|
11
|
+
return { valid: false, reason: 'viewport is not an object' };
|
|
12
|
+
const v = value;
|
|
13
|
+
const c = validatePosition(v.center, 'viewport.center');
|
|
14
|
+
if (!c.valid)
|
|
15
|
+
return c;
|
|
16
|
+
if (typeof v.zoom !== 'number' || !Number.isFinite(v.zoom))
|
|
17
|
+
return { valid: false, reason: 'viewport.zoom is not a finite number' };
|
|
18
|
+
if (v.zoom < MIN_ZOOM || v.zoom > MAX_ZOOM)
|
|
19
|
+
return { valid: false, reason: `viewport.zoom ${v.zoom} is outside [${MIN_ZOOM}, ${MAX_ZOOM}]` };
|
|
20
|
+
if (v.bearing !== undefined && !Number.isFinite(v.bearing))
|
|
21
|
+
return { valid: false, reason: 'viewport.bearing is not finite' };
|
|
22
|
+
if (v.pitch !== undefined && (!Number.isFinite(v.pitch) || v.pitch < 0 || v.pitch > MAX_PITCH)) {
|
|
23
|
+
return { valid: false, reason: `viewport.pitch must be within [0, ${MAX_PITCH}]` };
|
|
24
|
+
}
|
|
25
|
+
return { valid: true };
|
|
26
|
+
}
|
|
27
|
+
/** Clamps latitude to the Mercator range and bearing to [0, 360). Call after validateViewport. */
|
|
28
|
+
export function normalizeViewport(v) {
|
|
29
|
+
const out = {
|
|
30
|
+
center: {
|
|
31
|
+
latitude: Math.max(-MAX_MERCATOR_LATITUDE, Math.min(MAX_MERCATOR_LATITUDE, v.center.latitude)),
|
|
32
|
+
longitude: v.center.longitude,
|
|
33
|
+
},
|
|
34
|
+
zoom: v.zoom,
|
|
35
|
+
};
|
|
36
|
+
if (v.bearing !== undefined)
|
|
37
|
+
out.bearing = ((v.bearing % 360) + 360) % 360;
|
|
38
|
+
if (v.pitch !== undefined)
|
|
39
|
+
out.pitch = v.pitch;
|
|
40
|
+
if (v.bounds)
|
|
41
|
+
out.bounds = v.bounds;
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
function project(p, worldSize) {
|
|
45
|
+
const lat = (Math.max(-MAX_MERCATOR_LATITUDE, Math.min(MAX_MERCATOR_LATITUDE, p.latitude)) * Math.PI) / 180;
|
|
46
|
+
return {
|
|
47
|
+
x: ((p.longitude + 180) / 360) * worldSize,
|
|
48
|
+
y: ((1 - Math.log(Math.tan(Math.PI / 4 + lat / 2)) / Math.PI) / 2) * worldSize,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function unproject(x, y, worldSize) {
|
|
52
|
+
const longitude = (x / worldSize) * 360 - 180;
|
|
53
|
+
const n = Math.PI - (2 * Math.PI * y) / worldSize;
|
|
54
|
+
const latitude = (180 / Math.PI) * Math.atan(Math.sinh(n));
|
|
55
|
+
return { latitude, longitude };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Visible bounds of a viewport on a container of `size` pixels.
|
|
59
|
+
* Accounts for bearing; ignores pitch (pitched views report their real bounds through the renderer).
|
|
60
|
+
* MVP does not wrap the antimeridian: longitudes are clamped to [-180, 180].
|
|
61
|
+
*/
|
|
62
|
+
export function computeBounds(viewport, size, tileSize = TILE_SIZE) {
|
|
63
|
+
const worldSize = tileSize * Math.pow(2, viewport.zoom);
|
|
64
|
+
const c = project(viewport.center, worldSize);
|
|
65
|
+
const theta = ((viewport.bearing ?? 0) * Math.PI) / 180;
|
|
66
|
+
const cos = Math.cos(theta);
|
|
67
|
+
const sin = Math.sin(theta);
|
|
68
|
+
const hw = size.width / 2;
|
|
69
|
+
const hh = size.height / 2;
|
|
70
|
+
let west = Infinity;
|
|
71
|
+
let east = -Infinity;
|
|
72
|
+
let south = Infinity;
|
|
73
|
+
let north = -Infinity;
|
|
74
|
+
for (const [dx, dy] of [[-hw, -hh], [hw, -hh], [hw, hh], [-hw, hh]]) {
|
|
75
|
+
const p = unproject(c.x + dx * cos - dy * sin, c.y + dx * sin + dy * cos, worldSize);
|
|
76
|
+
west = Math.min(west, p.longitude);
|
|
77
|
+
east = Math.max(east, p.longitude);
|
|
78
|
+
south = Math.min(south, p.latitude);
|
|
79
|
+
north = Math.max(north, p.latitude);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
west: Math.max(-180, west),
|
|
83
|
+
east: Math.min(180, east),
|
|
84
|
+
south: Math.max(-MAX_MERCATOR_LATITUDE, south),
|
|
85
|
+
north: Math.min(MAX_MERCATOR_LATITUDE, north),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** The zoom at which `bounds` fits in `size` pixels, with `padding` pixels on each side. Bearing 0. */
|
|
89
|
+
export function zoomToFit(bounds, size, padding = 0, tileSize = TILE_SIZE) {
|
|
90
|
+
const pad = typeof padding === 'number' ? { top: padding, right: padding, bottom: padding, left: padding } : padding;
|
|
91
|
+
const w = Math.max(1, size.width - pad.left - pad.right);
|
|
92
|
+
const h = Math.max(1, size.height - pad.top - pad.bottom);
|
|
93
|
+
const a = project({ latitude: bounds.north, longitude: bounds.west }, 1);
|
|
94
|
+
const b = project({ latitude: bounds.south, longitude: bounds.east }, 1);
|
|
95
|
+
const spanX = Math.max(1e-12, Math.abs(b.x - a.x));
|
|
96
|
+
const spanY = Math.max(1e-12, Math.abs(b.y - a.y));
|
|
97
|
+
const zoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, Math.log2(Math.min(w / (spanX * tileSize), h / (spanY * tileSize)))));
|
|
98
|
+
// The bounds sit in the middle of the clear area, not of the container: shift the camera by half the imbalance.
|
|
99
|
+
const worldPx = tileSize * 2 ** zoom;
|
|
100
|
+
const center = unproject((a.x + b.x) / 2 - (pad.left - pad.right) / 2 / worldPx, (a.y + b.y) / 2 - (pad.top - pad.bottom) / 2 / worldPx, 1);
|
|
101
|
+
return { center, zoom };
|
|
102
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The UNS backend over HTTP (uns-backend-server; one per region). Nodes, search, bindings, and the UNS's own
|
|
3
|
+
* time-series reads — `/timeseries/latest` and `/timeseries/range` take UNS node ids, never device or sensor ids.
|
|
4
|
+
* The caller's IOSense bearer is forwarded; the UNS authorizes (requirement §29: the browser is not the boundary).
|
|
5
|
+
*/
|
|
6
|
+
export interface UnsNode {
|
|
7
|
+
id: string;
|
|
8
|
+
/** Workspace id (`ws_…`). */
|
|
9
|
+
unsId: string;
|
|
10
|
+
type: string;
|
|
11
|
+
name: string;
|
|
12
|
+
/** Slug path, e.g. `fleet/uav-12/latitude`. */
|
|
13
|
+
path: string;
|
|
14
|
+
parentId: string | null;
|
|
15
|
+
childCount: number;
|
|
16
|
+
metadata: Record<string, unknown>;
|
|
17
|
+
tags: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface UnsWorkspace {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
}
|
|
23
|
+
export interface UnsLatest {
|
|
24
|
+
nodeId: string;
|
|
25
|
+
ok: boolean;
|
|
26
|
+
value?: number;
|
|
27
|
+
/** Epoch ms. */
|
|
28
|
+
time?: number;
|
|
29
|
+
unit?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface UnsRange {
|
|
33
|
+
nodeId: string;
|
|
34
|
+
unit?: string;
|
|
35
|
+
points: Array<{
|
|
36
|
+
time: number;
|
|
37
|
+
value: number;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
export interface UnsClientOptions {
|
|
41
|
+
/** e.g. https://uns-backend-server.us.iosense.io */
|
|
42
|
+
baseUrl: string;
|
|
43
|
+
/** The IOSense bearer (without "Bearer "), read on every call so a refreshed session is used at once. */
|
|
44
|
+
token: () => string | null;
|
|
45
|
+
fetch?: typeof fetch;
|
|
46
|
+
}
|
|
47
|
+
export declare class UnsHttpError extends Error {
|
|
48
|
+
readonly status: number;
|
|
49
|
+
readonly path: string;
|
|
50
|
+
constructor(status: number, path: string, message: string);
|
|
51
|
+
}
|
|
52
|
+
export declare function toNode(raw: unknown, wsId: string): UnsNode | null;
|
|
53
|
+
export declare class UnsClient {
|
|
54
|
+
private readonly opts;
|
|
55
|
+
private readonly f;
|
|
56
|
+
/** A token handed over by the map engine (setAuth); wins over the session's own while set. */
|
|
57
|
+
private tokenOverride;
|
|
58
|
+
constructor(opts: UnsClientOptions);
|
|
59
|
+
setToken(token: string | null): void;
|
|
60
|
+
private call;
|
|
61
|
+
workspaces(signal?: AbortSignal): Promise<UnsWorkspace[]>;
|
|
62
|
+
children(wsId: string, parentId?: string, signal?: AbortSignal): Promise<UnsNode[]>;
|
|
63
|
+
search(wsId: string, q: string, opts?: {
|
|
64
|
+
type?: string;
|
|
65
|
+
limit?: number;
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
}): Promise<UnsNode[]>;
|
|
68
|
+
node(wsId: string, nodeId: string, signal?: AbortSignal): Promise<UnsNode | null>;
|
|
69
|
+
/** A node and everything under it, full rows with metadata, root first (one call for a whole branch). */
|
|
70
|
+
subtree(wsId: string, rootId: string, signal?: AbortSignal): Promise<UnsNode[]>;
|
|
71
|
+
/** Latest values of tags, by UNS node id. */
|
|
72
|
+
latest(wsId: string, nodeIds: string[], signal?: AbortSignal): Promise<UnsLatest[]>;
|
|
73
|
+
/** A tag's points over a window (epoch ms). */
|
|
74
|
+
range(wsId: string, nodeId: string, startMs: number, endMs: number, signal?: AbortSignal): Promise<UnsRange>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The UNS backend over HTTP (uns-backend-server; one per region). Nodes, search, bindings, and the UNS's own
|
|
3
|
+
* time-series reads — `/timeseries/latest` and `/timeseries/range` take UNS node ids, never device or sensor ids.
|
|
4
|
+
* The caller's IOSense bearer is forwarded; the UNS authorizes (requirement §29: the browser is not the boundary).
|
|
5
|
+
*/
|
|
6
|
+
export class UnsHttpError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
path;
|
|
9
|
+
constructor(status, path, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.path = path;
|
|
13
|
+
this.name = 'UnsHttpError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** The envelopes differ across endpoints (`items`, `data.items`, `data`, a bare array). */
|
|
17
|
+
function rows(json) {
|
|
18
|
+
if (Array.isArray(json))
|
|
19
|
+
return json;
|
|
20
|
+
const b = json;
|
|
21
|
+
if (Array.isArray(b?.items))
|
|
22
|
+
return b.items;
|
|
23
|
+
if (Array.isArray(b?.nodes))
|
|
24
|
+
return b.nodes;
|
|
25
|
+
if (Array.isArray(b?.data))
|
|
26
|
+
return b.data;
|
|
27
|
+
const d = b?.data;
|
|
28
|
+
return Array.isArray(d?.items) ? d.items : [];
|
|
29
|
+
}
|
|
30
|
+
export function toNode(raw, wsId) {
|
|
31
|
+
const n = raw;
|
|
32
|
+
if (!n || typeof n.id !== 'string' || typeof n.path !== 'string' || !n.path)
|
|
33
|
+
return null;
|
|
34
|
+
return {
|
|
35
|
+
id: n.id,
|
|
36
|
+
unsId: n.unsId ?? n.workspaceId ?? wsId,
|
|
37
|
+
type: typeof n.type === 'string' ? n.type : '',
|
|
38
|
+
name: n.name || n.path.split('/').pop() || n.id,
|
|
39
|
+
path: n.path,
|
|
40
|
+
parentId: n.parentId ?? null,
|
|
41
|
+
childCount: typeof n.childCount === 'number' ? n.childCount : Array.isArray(n.children) ? n.children.length : 0,
|
|
42
|
+
metadata: n.metadata && typeof n.metadata === 'object' ? n.metadata : {},
|
|
43
|
+
tags: Array.isArray(n.tags) ? n.tags.filter((t) => typeof t === 'string') : [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export class UnsClient {
|
|
47
|
+
opts;
|
|
48
|
+
f;
|
|
49
|
+
/** A token handed over by the map engine (setAuth); wins over the session's own while set. */
|
|
50
|
+
tokenOverride = null;
|
|
51
|
+
constructor(opts) {
|
|
52
|
+
this.opts = opts;
|
|
53
|
+
this.f = opts.fetch ?? ((...a) => fetch(...a));
|
|
54
|
+
}
|
|
55
|
+
setToken(token) {
|
|
56
|
+
this.tokenOverride = token;
|
|
57
|
+
}
|
|
58
|
+
async call(path, init) {
|
|
59
|
+
const token = this.tokenOverride ?? this.opts.token();
|
|
60
|
+
const res = await this.f(`${this.opts.baseUrl}${path}`, {
|
|
61
|
+
method: init?.method ?? 'GET',
|
|
62
|
+
headers: { ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(init?.body !== undefined ? { 'Content-Type': 'application/json' } : {}) },
|
|
63
|
+
...(init?.body !== undefined ? { body: JSON.stringify(init.body) } : {}),
|
|
64
|
+
...(init?.signal ? { signal: init.signal } : {}),
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const text = await res.text().catch(() => '');
|
|
68
|
+
throw new UnsHttpError(res.status, path, `UNS ${path}: HTTP ${res.status}${text ? ` — ${text.slice(0, 200)}` : ''}`);
|
|
69
|
+
}
|
|
70
|
+
return (await res.json());
|
|
71
|
+
}
|
|
72
|
+
async workspaces(signal) {
|
|
73
|
+
const json = await this.call('/workspaces?limit=200', signal ? { signal } : undefined);
|
|
74
|
+
return rows(json)
|
|
75
|
+
.map((it) => it.workspace ?? it)
|
|
76
|
+
.filter((w) => typeof w?.id === 'string' && typeof w?.name === 'string')
|
|
77
|
+
.map((w) => ({ id: w.id, name: w.name }));
|
|
78
|
+
}
|
|
79
|
+
async children(wsId, parentId, signal) {
|
|
80
|
+
const json = await this.call(`/workspaces/${encodeURIComponent(wsId)}/nodes/skeleton?parent=${encodeURIComponent(parentId ?? 'root')}`, signal ? { signal } : undefined);
|
|
81
|
+
return rows(json).map((r) => toNode(r, wsId)).filter((n) => n !== null);
|
|
82
|
+
}
|
|
83
|
+
async search(wsId, q, opts = {}) {
|
|
84
|
+
const params = new URLSearchParams({ q, limit: String(opts.limit ?? 200) });
|
|
85
|
+
if (opts.type)
|
|
86
|
+
params.set('type', opts.type);
|
|
87
|
+
const json = await this.call(`/workspaces/${encodeURIComponent(wsId)}/nodes/search?${params}`, opts.signal ? { signal: opts.signal } : undefined);
|
|
88
|
+
return rows(json).map((r) => toNode(r, wsId)).filter((n) => n !== null);
|
|
89
|
+
}
|
|
90
|
+
async node(wsId, nodeId, signal) {
|
|
91
|
+
const json = await this.call(`/workspaces/${encodeURIComponent(wsId)}/nodes/${encodeURIComponent(nodeId)}`, signal ? { signal } : undefined);
|
|
92
|
+
return toNode(json.node ?? json.data ?? json, wsId);
|
|
93
|
+
}
|
|
94
|
+
/** A node and everything under it, full rows with metadata, root first (one call for a whole branch). */
|
|
95
|
+
async subtree(wsId, rootId, signal) {
|
|
96
|
+
const json = await this.call(`/workspaces/${encodeURIComponent(wsId)}/nodes/${encodeURIComponent(rootId)}/subtree`, signal ? { signal } : undefined);
|
|
97
|
+
return rows(json).map((r) => toNode(r, wsId)).filter((n) => n !== null);
|
|
98
|
+
}
|
|
99
|
+
/** Latest values of tags, by UNS node id. */
|
|
100
|
+
async latest(wsId, nodeIds, signal) {
|
|
101
|
+
if (!nodeIds.length)
|
|
102
|
+
return [];
|
|
103
|
+
const res = await this.call('/timeseries/latest', { method: 'POST', body: { unsId: wsId, nodeIds }, ...(signal ? { signal } : {}) });
|
|
104
|
+
return res.values ?? [];
|
|
105
|
+
}
|
|
106
|
+
/** A tag's points over a window (epoch ms). */
|
|
107
|
+
async range(wsId, nodeId, startMs, endMs, signal) {
|
|
108
|
+
const res = await this.call('/timeseries/range', { method: 'POST', body: { unsId: wsId, nodeId, startMs, endMs }, ...(signal ? { signal } : {}) });
|
|
109
|
+
return { nodeId, ...(res.unit ? { unit: res.unit } : {}), points: Array.isArray(res.points) ? res.points : [] };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { UnsClient, UnsHttpError, toNode, type UnsClientOptions, type UnsLatest, type UnsNode, type UnsRange, type UnsWorkspace } from './client.js';
|
|
2
|
+
export { UnsSpatialProvider, indexSpatialNodes, pluralLabel, type SpatialNode, type UnsProviderOptions } from './provider.js';
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The UNS as a Map Engine data source (requirement §26–28, §52). UNS ONLY: nodes come from the workspace tree,
|
|
3
|
+
* live values from `/timeseries/latest`, history from `/timeseries/range` — always by UNS node id. Legacy device /
|
|
4
|
+
* sensor identifiers in metadata (deviceId, sensorId, latitudeSensor, …) are deprecated and never used.
|
|
5
|
+
*
|
|
6
|
+
* Spatiality is a capability (§9), declared on the node as `metadata.spatial`:
|
|
7
|
+
* - `SpatialGeometry` → an area: `spatial.geometry` (GeoJSON, EPSG:4326)
|
|
8
|
+
* - `SpatialPoint` → a point: `spatial.geometry` or `spatial.latitude/longitude`
|
|
9
|
+
* - `MovingSpatial` → a point whose position comes from the node's child tags whose `metadata.spatialRole` is
|
|
10
|
+
* `latitude` / `longitude` (heading, speed, altitude roles enrich it)
|
|
11
|
+
* - `SpatialTrack` → history; served through getTracks() from the moving nodes' position tags
|
|
12
|
+
* Every other child tag becomes a live property named by its `metadata.semantic` (else its name).
|
|
13
|
+
*
|
|
14
|
+
* The visual plane (docs 28/29) from what the UNS states, never guessed: provenance (workspace, node path, times,
|
|
15
|
+
* version and marking from metadata, a time per property), confidence and an accuracy radius from tags with those
|
|
16
|
+
* spatialRoles, a validity window from metadata, links from `metadata.links` and the spatial parent/child tree, and
|
|
17
|
+
* an event journal from each node's tag history.
|
|
18
|
+
*/
|
|
19
|
+
import { MapEngineError } from '../map-engine/index.js';
|
|
20
|
+
import type { AltitudeReference, AuthAware, AuthContext, JournalEntry, JournalProvider, Link, LinkProvider, DataCapabilities, DatasetCatalog, DatasetInfo, Geometry, OntologySpatialResolver, RealtimeProvider, SpatialDataProvider, SpatialObject, SpatialPatch, SpatialQuery, TimeRange, Track, TrackProvider, Unsubscribe } from '../map-engine/index.js';
|
|
21
|
+
import type { UnsClient, UnsNode } from './client.js';
|
|
22
|
+
type Kind = 'area' | 'fixed' | 'moving' | 'point';
|
|
23
|
+
interface TagInfo {
|
|
24
|
+
id: string;
|
|
25
|
+
key: string;
|
|
26
|
+
/** The tag's UNS path, for provenance. */
|
|
27
|
+
path?: string;
|
|
28
|
+
unit?: string;
|
|
29
|
+
role?: string;
|
|
30
|
+
enumValues?: string[];
|
|
31
|
+
valueType?: string;
|
|
32
|
+
/** For heights: what they are measured from, as the UNS states it (metadata.datum). */
|
|
33
|
+
datum?: string;
|
|
34
|
+
}
|
|
35
|
+
/** A UNS datum as an engine altitude reference (ADR-028); unknown datums give none — never guessed. */
|
|
36
|
+
export declare function referenceOf(datum: string | undefined): AltitudeReference | undefined;
|
|
37
|
+
/** One spatial UNS node and what it takes to draw it. */
|
|
38
|
+
export interface SpatialNode {
|
|
39
|
+
node: UnsNode;
|
|
40
|
+
ontologyType: string;
|
|
41
|
+
kind: Kind;
|
|
42
|
+
/** Areas and fixed points. */
|
|
43
|
+
geometry?: Geometry;
|
|
44
|
+
tags: TagInfo[];
|
|
45
|
+
latTag?: TagInfo;
|
|
46
|
+
lngTag?: TagInfo;
|
|
47
|
+
/** The tag with `spatialRole: altitude`, used only when its datum gives a reference. */
|
|
48
|
+
altTag?: TagInfo;
|
|
49
|
+
altitudeReference?: AltitudeReference;
|
|
50
|
+
/** Static properties from metadata (no legacy identifiers). */
|
|
51
|
+
props: Record<string, unknown>;
|
|
52
|
+
/** A time the node itself carries (detections, observations…). */
|
|
53
|
+
timestamp?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface UnsProviderOptions {
|
|
56
|
+
client: UnsClient;
|
|
57
|
+
workspace: string;
|
|
58
|
+
/** Shown as the datasets' source. */
|
|
59
|
+
workspaceName?: string;
|
|
60
|
+
/** How often live values are read for subscribers. Default 3000 ms. */
|
|
61
|
+
pollMs?: number;
|
|
62
|
+
/** Re-read the tree after this long. Default 5 min. */
|
|
63
|
+
discoveryTtlMs?: number;
|
|
64
|
+
/** Latest values are shared between calls for this long. Default 1500 ms. */
|
|
65
|
+
latestTtlMs?: number;
|
|
66
|
+
now?: () => number;
|
|
67
|
+
/** Timer for polling (tests). */
|
|
68
|
+
setInterval?: (fn: () => void, ms: number) => unknown;
|
|
69
|
+
clearInterval?: (handle: unknown) => void;
|
|
70
|
+
}
|
|
71
|
+
/** "AirPlatform" → "Air platforms", "RFStation" → "RF stations" (acronyms keep their capitals). */
|
|
72
|
+
export declare const pluralLabel: (cls: string) => string;
|
|
73
|
+
/** Builds the spatial index of a workspace from its full node rows. Exported for tests. */
|
|
74
|
+
export declare function indexSpatialNodes(rows: UnsNode[]): SpatialNode[];
|
|
75
|
+
export declare class UnsSpatialProvider implements SpatialDataProvider, RealtimeProvider, TrackProvider, DatasetCatalog, JournalProvider, LinkProvider, AuthAware {
|
|
76
|
+
private readonly opts;
|
|
77
|
+
readonly capabilities: DataCapabilities;
|
|
78
|
+
private nodes;
|
|
79
|
+
private nodesAt;
|
|
80
|
+
private latestCache;
|
|
81
|
+
/** A read in flight, shared by every caller that needs any of its tags (the UNS answers in seconds). */
|
|
82
|
+
private latestInFlight;
|
|
83
|
+
private readonly subs;
|
|
84
|
+
private timer;
|
|
85
|
+
private readonly now;
|
|
86
|
+
constructor(opts: UnsProviderOptions);
|
|
87
|
+
/**
|
|
88
|
+
* Who is looking (doc 28 G13): the token goes with every UNS call and everything cached is read again, since
|
|
89
|
+
* another user may see other nodes and values. A refused read (HTTP 403) reaches the map as a denied layer.
|
|
90
|
+
*/
|
|
91
|
+
setAuth(auth: AuthContext | null): void;
|
|
92
|
+
/** The spatial nodes of the workspace (cached; re-read after discoveryTtlMs). */
|
|
93
|
+
discover(signal?: AbortSignal): Promise<SpatialNode[]>;
|
|
94
|
+
private readTree;
|
|
95
|
+
/**
|
|
96
|
+
* Latest values for tags. The UNS answers a batch in seconds, so a cold read fetches EVERY live tag of the
|
|
97
|
+
* workspace at once (in parallel chunks) and every layer's query is served from that one round; a read already
|
|
98
|
+
* in flight is shared rather than repeated.
|
|
99
|
+
*/
|
|
100
|
+
private latest;
|
|
101
|
+
/** One spatial node as a SpatialObject, with its live values. Null when a moving node has no position yet. */
|
|
102
|
+
private toObject;
|
|
103
|
+
private liveTagIds;
|
|
104
|
+
private select;
|
|
105
|
+
query(query: SpatialQuery, signal?: AbortSignal): Promise<SpatialObject[]>;
|
|
106
|
+
getCurrentState(objectIds: string[], signal?: AbortSignal): Promise<SpatialObject[]>;
|
|
107
|
+
subscribe(query: SpatialQuery, onPatch: (p: SpatialPatch) => void, onError?: (e: Error) => void): Unsubscribe;
|
|
108
|
+
/** One polling round for every subscriber. Exposed for tests. */
|
|
109
|
+
poll(): Promise<void>;
|
|
110
|
+
getTracks(query: SpatialQuery, range: TimeRange, signal?: AbortSignal): Promise<Track[]>;
|
|
111
|
+
/**
|
|
112
|
+
* The event journal from the UNS's own history: each node's property tags over the range, merged by time into
|
|
113
|
+
* a snapshot at the range start and one entry per change. Positions are left to tracks; areas and fixed points
|
|
114
|
+
* without tags have no history (they are static). A node created inside the range (metadata.createdAt) is not
|
|
115
|
+
* there before it.
|
|
116
|
+
*/
|
|
117
|
+
getJournal(query: SpatialQuery, range: TimeRange, signal?: AbortSignal): Promise<JournalEntry[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Links the UNS states: `metadata.links` (or `relations`) entries `{ to, type }` naming another node by id or path,
|
|
120
|
+
* and the tree itself — a spatial node under another spatial node is `part-of` it.
|
|
121
|
+
*/
|
|
122
|
+
getLinks(objectIds: string[], types: string[] | undefined, signal?: AbortSignal): Promise<Link[]>;
|
|
123
|
+
listDatasets(signal?: AbortSignal): Promise<DatasetInfo[]>;
|
|
124
|
+
/** Ontology binding from the declared capabilities (§9, §12): what each class is drawn as. */
|
|
125
|
+
readonly ontology: OntologySpatialResolver;
|
|
126
|
+
/** Stops polling. */
|
|
127
|
+
dispose(): void;
|
|
128
|
+
}
|
|
129
|
+
/** Thrown shape for callers that want a typed failure. */
|
|
130
|
+
export declare const unsError: (message: string, cause?: unknown) => MapEngineError;
|
|
131
|
+
export {};
|