mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 04:14:02 +00:00
Keep ALPR marker state in a layer store instead of on Cesium entities
The ALPR layer's private per-entity fields (record, clamp, canvas and display anchors, wedge, native appearance, pick position, shown, surface check) move from gevAlpr* expandos into alprMarkers, a store made by the new createEntityStore(). The shared entity records are built on the same factory.
This commit is contained in:
1 parent
95c0070412
commit
07c8d7a17c
9 files changed
+144
-75
No files matched your search
@@ -3638,7 +3638,8 @@ down | auth-failed` (plus the unchanged `missing-key`/`unsupported`) with
|
||||
> and mapped installations write explicit `labelModel` objects and
|
||||
> `displayPosition` getters, backed by their layer-owned frame/display cache,
|
||||
> into Core's entity records (`src/data/entityRecords.js`), never onto the
|
||||
> Cesium entity.
|
||||
> Cesium entity. A layer's private per-entity state goes in its own store from
|
||||
> `createEntityStore()` (ALPR markers: `src/layers/alpr/markerState.js`).
|
||||
> Selected AIS vessels continue using the vessel source's protected selected
|
||||
> card, so they do not create a duplicate tracked readout. The host registers
|
||||
> the active readout in the protected tracked lane with a zero ambient quota;
|
||||
|
||||
@@ -1957,6 +1957,7 @@
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/alpr/visuals.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/markerState.js",
|
||||
"src/layers/alpr/source.js",
|
||||
"src/data/inputOwnership.js",
|
||||
"src/layers/alpr/records.js",
|
||||
@@ -2442,6 +2443,7 @@
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/alpr/visuals.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/markerState.js",
|
||||
"src/layers/alpr/source.js",
|
||||
"src/layers/awareness/controls.js",
|
||||
"src/layers/awareness/dependencies.js",
|
||||
@@ -3347,6 +3349,7 @@
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/alpr/visuals.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/markerState.js",
|
||||
"src/layers/alpr/source.js",
|
||||
"src/layers/awareness/controls.js",
|
||||
"src/layers/awareness/dependencies.js",
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
normalizeAlprNode,
|
||||
} from './alprCameras.js';
|
||||
import { readEntityRecord } from './entityRecords.js';
|
||||
import { alprMarkers } from '../layers/alpr/markerState.js';
|
||||
|
||||
test('normalizeAlprNode: maps a full OSM node to a plain record', () => {
|
||||
const record = normalizeAlprNode({
|
||||
@@ -1445,12 +1446,12 @@ test('map-source changes reposition all ALPR entities and saved native appearanc
|
||||
await layer.update();
|
||||
const entities = [...h.source.entities.values];
|
||||
const saved = entities[1];
|
||||
saved.gevAlprNativeAppearance = {
|
||||
alprMarkers.write(saved).nativeAppearance = {
|
||||
position: saved.position,
|
||||
heightReference: saved.billboard.heightReference,
|
||||
};
|
||||
saved.position = Cesium.Cartesian3.fromDegrees(-97.7431, 30.26721, 190);
|
||||
saved.gevAlprPickPosition = saved.position;
|
||||
alprMarkers.write(saved).pickPosition = saved.position;
|
||||
for (const next of [150, 120, null, 180]) {
|
||||
floor = next;
|
||||
// Includes a provider switch with the same globe.show value.
|
||||
@@ -1471,10 +1472,10 @@ test('map-source changes reposition all ALPR entities and saved native appearanc
|
||||
? Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
: Cesium.HeightReference.NONE,
|
||||
);
|
||||
assert.equal(entity.gevAlprCanvasPosition, null);
|
||||
assert.equal(alprMarkers.read(entity)?.canvasPosition, null);
|
||||
}
|
||||
assert.equal(
|
||||
saved.gevAlprNativeAppearance,
|
||||
alprMarkers.read(saved)?.nativeAppearance,
|
||||
null,
|
||||
'saved appearance restores at the new floor',
|
||||
);
|
||||
|
||||
@@ -12,9 +12,34 @@
|
||||
* - `visualPosition()`: where a 3D model is actually drawn, for the tracked card.
|
||||
* - `labelModel`: the tracked card's `{ title, details, accent, … }`.
|
||||
* - `selectionOrigin`: what started the selection (`'user'`, …).
|
||||
*
|
||||
* A layer's private per-entity state goes in its own store ({@link createEntityStore}).
|
||||
*/
|
||||
|
||||
const records = new WeakMap();
|
||||
/**
|
||||
* A store of one record per renderer object, held by Core and dropped with the object.
|
||||
* @returns {{ write: (entity: Object) => Object, read: (entity: unknown) => Object|null }}
|
||||
* `write` makes the record empty on first use; `read` gives null when there is none.
|
||||
*/
|
||||
export function createEntityStore() {
|
||||
const records = new WeakMap();
|
||||
return {
|
||||
write(entity) {
|
||||
let record = records.get(entity);
|
||||
if (!record) {
|
||||
record = {};
|
||||
records.set(entity, record);
|
||||
}
|
||||
return record;
|
||||
},
|
||||
read(entity) {
|
||||
if (!entity || typeof entity !== 'object') return null;
|
||||
return records.get(entity) ?? null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const shared = createEntityStore();
|
||||
|
||||
/**
|
||||
* The record for `entity`, made empty on first use. For layers writing their own state.
|
||||
@@ -22,12 +47,7 @@ const records = new WeakMap();
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function entityRecord(entity) {
|
||||
let record = records.get(entity);
|
||||
if (!record) {
|
||||
record = {};
|
||||
records.set(entity, record);
|
||||
}
|
||||
return record;
|
||||
return shared.write(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,8 +56,7 @@ export function entityRecord(entity) {
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
export function readEntityRecord(entity) {
|
||||
if (!entity || typeof entity !== 'object') return null;
|
||||
return records.get(entity) ?? null;
|
||||
return shared.read(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { entityRecord, readEntityRecord, withEntityRecord } from './entityRecords.js';
|
||||
import {
|
||||
createEntityStore,
|
||||
entityRecord,
|
||||
readEntityRecord,
|
||||
withEntityRecord,
|
||||
} from './entityRecords.js';
|
||||
|
||||
test('an entity record is made on first write and read back by the same entity', () => {
|
||||
const entity = {};
|
||||
@@ -29,3 +34,15 @@ test('withEntityRecord writes fields into the record and returns the entity', ()
|
||||
assert.equal(readEntityRecord(entity)?.trackedId, 'alpr:42');
|
||||
assert.equal(Object.hasOwn(entity, 'trackedId'), false);
|
||||
});
|
||||
|
||||
test('a layer store is separate from the shared records and from other stores', () => {
|
||||
const entity = {};
|
||||
const a = createEntityStore();
|
||||
const b = createEntityStore();
|
||||
a.write(entity).canvasPosition = 1;
|
||||
assert.equal(a.read(entity)?.canvasPosition, 1);
|
||||
assert.equal(b.read(entity), null);
|
||||
assert.equal(readEntityRecord(entity), null);
|
||||
assert.equal(a.read(null), null);
|
||||
assert.deepEqual(Object.keys(entity), []);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @module alpr/markerState
|
||||
* @description The ALPR layer's own per-marker state, held in Core rather than on the
|
||||
* Cesium entities it draws:
|
||||
* - `record`: the camera record the marker shows.
|
||||
* - `clamped`: whether the marker sits on the surface rather than at its height.
|
||||
* - `canvasPosition`, `displayPosition`: the canvas anchor and the position the card reads.
|
||||
* - `wedge`: the direction wedge's positions.
|
||||
* - `nativeAppearance`, `pickPosition`: the native look saved while the canvas draws it.
|
||||
* - `shown`, `surfaceCheckedFor`: the last visibility applied and the anchor last checked.
|
||||
*/
|
||||
import { createEntityStore } from '../../data/entityRecords.js';
|
||||
|
||||
export const alprMarkers = createEntityStore();
|
||||
+32
-28
@@ -18,6 +18,7 @@ import {
|
||||
paintDirectionWedge,
|
||||
validAlprGroundHeight,
|
||||
} from './visuals.js';
|
||||
import { alprMarkers } from './markerState.js';
|
||||
|
||||
/** Per-layer presentation on a caller-owned overlay; Cesium remains the fallback. */
|
||||
export function createAlprOverlay({
|
||||
@@ -48,29 +49,30 @@ export function createAlprOverlay({
|
||||
function canvasOwned(entity, owned) {
|
||||
const visible = !owned;
|
||||
const billboard = entity.billboard;
|
||||
const marker = alprMarkers.write(entity);
|
||||
// Keep a faint native pick target under the canvas badge so sibling layer
|
||||
// handlers recognize ALPR ownership instead of treating the click as empty.
|
||||
if (!visible && !entity.gevAlprNativeAppearance) {
|
||||
entity.gevAlprNativeAppearance = {
|
||||
if (!visible && !marker.nativeAppearance) {
|
||||
marker.nativeAppearance = {
|
||||
position: entity.position,
|
||||
heightReference: billboard.heightReference,
|
||||
scaleByDistance: billboard.scaleByDistance,
|
||||
color: billboard.color,
|
||||
};
|
||||
entity.position = entity.gevAlprCanvasPosition;
|
||||
entity.gevAlprPickPosition = entity.position;
|
||||
entity.position = marker.canvasPosition;
|
||||
marker.pickPosition = entity.position;
|
||||
billboard.heightReference = Cesium.HeightReference.NONE;
|
||||
billboard.scaleByDistance = undefined;
|
||||
billboard.color = Cesium.Color.WHITE.withAlpha(0.01);
|
||||
} else if (visible && entity.gevAlprNativeAppearance) {
|
||||
const saved = entity.gevAlprNativeAppearance;
|
||||
if (entity.position === entity.gevAlprPickPosition)
|
||||
} else if (visible && marker.nativeAppearance) {
|
||||
const saved = marker.nativeAppearance;
|
||||
if (entity.position === marker.pickPosition)
|
||||
entity.position = saved.position;
|
||||
billboard.heightReference = saved.heightReference;
|
||||
billboard.scaleByDistance = saved.scaleByDistance;
|
||||
billboard.color = saved.color;
|
||||
entity.gevAlprNativeAppearance = null;
|
||||
entity.gevAlprPickPosition = null;
|
||||
marker.nativeAppearance = null;
|
||||
marker.pickPosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,8 +80,9 @@ export function createAlprOverlay({
|
||||
// and raises definitionChanged, which paint used to do for every overlay
|
||||
// camera on every frame.
|
||||
function setShown(entity, visible) {
|
||||
if (entity.gevAlprShown === visible) return;
|
||||
entity.gevAlprShown = visible;
|
||||
const marker = alprMarkers.write(entity);
|
||||
if (marker.shown === visible) return;
|
||||
marker.shown = visible;
|
||||
entity.billboard.show = visible;
|
||||
if (entity.polyline) entity.polyline.show = visible;
|
||||
if (entity.polygon) entity.polygon.show = visible;
|
||||
@@ -87,9 +90,10 @@ export function createAlprOverlay({
|
||||
|
||||
function resetAnchors() {
|
||||
for (const entity of state.dataSource.entities.values) {
|
||||
entity.gevAlprCanvasPosition = null;
|
||||
entity.gevAlprWedge = null;
|
||||
entity.gevAlprDisplayPosition = null;
|
||||
const marker = alprMarkers.write(entity);
|
||||
marker.canvasPosition = null;
|
||||
marker.wedge = null;
|
||||
marker.displayPosition = null;
|
||||
nativeVisible(entity, true);
|
||||
}
|
||||
state.lastAnchorSampleAt = 0;
|
||||
@@ -98,7 +102,8 @@ export function createAlprOverlay({
|
||||
}
|
||||
|
||||
function anchorFor(record, entity, budget) {
|
||||
if (entity.gevAlprCanvasPosition) return entity.gevAlprCanvasPosition;
|
||||
const cached = alprMarkers.read(entity)?.canvasPosition;
|
||||
if (cached) return cached;
|
||||
const scene = state.viewer.scene;
|
||||
const location = Cesium.Cartographic.fromDegrees(
|
||||
record.longitude,
|
||||
@@ -126,7 +131,7 @@ export function createAlprOverlay({
|
||||
height = scene.globe.getHeight?.(location);
|
||||
if (!validAlprGroundHeight(height)) return null;
|
||||
setAnchor(record, entity, height);
|
||||
return entity.gevAlprCanvasPosition;
|
||||
return alprMarkers.read(entity)?.canvasPosition;
|
||||
}
|
||||
|
||||
function floorHeightAt(latitude, longitude) {
|
||||
@@ -135,16 +140,13 @@ export function createAlprOverlay({
|
||||
}
|
||||
|
||||
function setAnchor(record, entity, height) {
|
||||
entity.gevAlprCanvasPosition = Cesium.Cartesian3.fromDegrees(
|
||||
const marker = alprMarkers.write(entity);
|
||||
marker.canvasPosition = Cesium.Cartesian3.fromDegrees(
|
||||
record.longitude,
|
||||
record.latitude,
|
||||
height,
|
||||
);
|
||||
entity.gevAlprWedge = directionWedgePositions(
|
||||
record,
|
||||
height,
|
||||
floorHeightAt,
|
||||
);
|
||||
marker.wedge = directionWedgePositions(record, height, floorHeightAt);
|
||||
}
|
||||
|
||||
/** Whether the visible photoreal tiles (or globe) have finished streaming. */
|
||||
@@ -192,8 +194,9 @@ export function createAlprOverlay({
|
||||
let pending = false;
|
||||
for (const record of records) {
|
||||
const entity = state.dataSource.entities.getById(record.id);
|
||||
const anchor = entity?.gevAlprCanvasPosition;
|
||||
if (!anchor || entity.gevAlprSurfaceCheckedFor === anchor) continue;
|
||||
const marker = alprMarkers.read(entity);
|
||||
const anchor = marker?.canvasPosition;
|
||||
if (!anchor || marker.surfaceCheckedFor === anchor) continue;
|
||||
if (!still || !surfaceSettled(scene) || budget.samples <= 0) {
|
||||
pending = true;
|
||||
continue;
|
||||
@@ -220,9 +223,10 @@ export function createAlprOverlay({
|
||||
sample <= floor + SURFACE_ABOVE_FLOOR_M));
|
||||
if (accepted && Math.abs(sample + MARKER_FLOOR_LIFT_M - current) > 1)
|
||||
setAnchor(record, entity, sample + MARKER_FLOOR_LIFT_M);
|
||||
entity.gevAlprSurfaceCheckedFor = entity.gevAlprCanvasPosition;
|
||||
// setAnchor rewrote the same record, so this is the new anchor.
|
||||
marker.surfaceCheckedFor = marker.canvasPosition;
|
||||
if (entity.id === state.selectedId)
|
||||
entity.gevAlprDisplayPosition = entity.gevAlprCanvasPosition;
|
||||
marker.displayPosition = marker.canvasPosition;
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
@@ -272,7 +276,7 @@ export function createAlprOverlay({
|
||||
setShown(entity, false);
|
||||
continue;
|
||||
}
|
||||
const wedge = entity.gevAlprWedge;
|
||||
const wedge = alprMarkers.read(entity)?.wedge;
|
||||
if (wedge) {
|
||||
const left = Cesium.SceneTransforms.worldToWindowCoordinates(
|
||||
scene,
|
||||
@@ -309,7 +313,7 @@ export function createAlprOverlay({
|
||||
Cesium.Cartesian3.distance(state.viewer.camera.positionWC, anchor),
|
||||
),
|
||||
});
|
||||
if (selected) entity.gevAlprDisplayPosition = anchor;
|
||||
if (selected) alprMarkers.write(entity).displayPosition = anchor;
|
||||
}
|
||||
// Paint all glyphs after the wedges so one camera's cone cannot wash out another.
|
||||
for (const { record, origin, selected, image, alpha, scale } of painted) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
alprLabelDetails,
|
||||
markerScale,
|
||||
} from './visuals.js';
|
||||
import { alprMarkers } from './markerState.js';
|
||||
|
||||
function harness(t, count = 2, { floor = null } = {}) {
|
||||
const previousImage = globalThis.Image;
|
||||
@@ -144,7 +145,9 @@ test('the overlay bounds its cohort and promotes selection while leaving farther
|
||||
h.state.selectedId = 'camera:69';
|
||||
h.overlay.sync(h.records);
|
||||
h.paint();
|
||||
const hidden = h.entities.values.filter((e) => e.gevAlprNativeAppearance);
|
||||
const hidden = h.entities.values.filter(
|
||||
(e) => alprMarkers.read(e)?.nativeAppearance,
|
||||
);
|
||||
assert.equal(hidden.length, 64);
|
||||
assert.ok(hidden.some((e) => e.id === 'camera:69'));
|
||||
assert.equal(
|
||||
@@ -161,16 +164,16 @@ test('surface changes invalidate cached anchors, and disable/disposal release pa
|
||||
h.overlay.sync(h.records);
|
||||
h.paint();
|
||||
const entity = h.entities.getById('camera:0');
|
||||
const first = entity.gevAlprCanvasPosition;
|
||||
const first = alprMarkers.read(entity)?.canvasPosition;
|
||||
h.paint();
|
||||
assert.equal(
|
||||
entity.gevAlprCanvasPosition,
|
||||
alprMarkers.read(entity)?.canvasPosition,
|
||||
first,
|
||||
'stable anchor across frames',
|
||||
);
|
||||
h.state.viewer.scene.globe.show = false;
|
||||
h.mapChanged();
|
||||
assert.equal(entity.gevAlprCanvasPosition, null);
|
||||
assert.equal(alprMarkers.read(entity)?.canvasPosition, null);
|
||||
h.state.enabled = false;
|
||||
h.overlay.clear();
|
||||
assert.equal(h.active, false);
|
||||
@@ -260,7 +263,7 @@ test('a still view replaces floor anchors with the rendered surface, within the
|
||||
h.paint();
|
||||
const height = (id) =>
|
||||
Cesium.Cartographic.fromCartesian(
|
||||
h.entities.getById(id).gevAlprCanvasPosition,
|
||||
alprMarkers.read(h.entities.getById(id))?.canvasPosition,
|
||||
).height;
|
||||
assert.equal(
|
||||
samples,
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
validAlprGroundHeight,
|
||||
} from './visuals.js';
|
||||
import { entityRecord, readEntityRecord } from '../../data/entityRecords.js';
|
||||
import { alprMarkers } from './markerState.js';
|
||||
|
||||
export function createAlprPresentation({ state, services, source }) {
|
||||
const { governorRequestRender } = services.render;
|
||||
@@ -171,7 +172,7 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
const heightReference = clamped
|
||||
? Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
: Cesium.HeightReference.NONE;
|
||||
const saved = entity.gevAlprNativeAppearance;
|
||||
const saved = alprMarkers.read(entity)?.nativeAppearance;
|
||||
if (saved) {
|
||||
saved.position = position;
|
||||
saved.heightReference = heightReference;
|
||||
@@ -179,7 +180,7 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
entity.position = position;
|
||||
entity.billboard.heightReference = heightReference;
|
||||
}
|
||||
entity.gevAlprClamped = clamped;
|
||||
alprMarkers.write(entity).clamped = clamped;
|
||||
}
|
||||
|
||||
/** Refresh all native placements, including the appearances held by the canvas. */
|
||||
@@ -187,7 +188,9 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
if (!state.enabled || !state.dataSource) return;
|
||||
clampedMarkers.clear();
|
||||
for (const entity of state.dataSource.entities.values) {
|
||||
const { position, clamped } = markerPlacement(entity.gevAlprRecord);
|
||||
const { position, clamped } = markerPlacement(
|
||||
alprMarkers.read(entity)?.record,
|
||||
);
|
||||
setMarkerPosition(entity, position, clamped);
|
||||
if (clamped) clampedMarkers.set(entity.id, entity);
|
||||
}
|
||||
@@ -203,10 +206,10 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
const batch = [...clampedMarkers.values()];
|
||||
const request = Promise.resolve(
|
||||
floors.resolve(
|
||||
batch.map((entity) => ({
|
||||
lat: entity.gevAlprRecord.latitude,
|
||||
lon: entity.gevAlprRecord.longitude,
|
||||
})),
|
||||
batch.map((entity) => {
|
||||
const { record } = alprMarkers.read(entity);
|
||||
return { lat: record.latitude, lon: record.longitude };
|
||||
}),
|
||||
),
|
||||
)
|
||||
.catch(() => {})
|
||||
@@ -218,7 +221,9 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
for (const entity of batch) {
|
||||
if (clampedMarkers.get(entity.id) !== entity) continue;
|
||||
clampedMarkers.delete(entity.id);
|
||||
const { position, clamped } = markerPlacement(entity.gevAlprRecord);
|
||||
const { position, clamped } = markerPlacement(
|
||||
alprMarkers.read(entity)?.record,
|
||||
);
|
||||
if (clamped) continue;
|
||||
setMarkerPosition(entity, position, false);
|
||||
moved = true;
|
||||
@@ -370,7 +375,7 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
removeEntityContextsForLayer(LAYER_ID, { retainIds: visibleIds });
|
||||
for (const record of visible) {
|
||||
const existing = state.dataSource.entities.getById(record.id);
|
||||
if (existing?.gevAlprRecord === record) {
|
||||
if (alprMarkers.read(existing)?.record === record) {
|
||||
updateAppearance(existing, record.id === state.selectedId);
|
||||
continue;
|
||||
}
|
||||
@@ -401,37 +406,39 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
let entity = existing;
|
||||
// Keep the Cesium entity and its ground-clamping subscription while its
|
||||
// geometry is unchanged. Updating metadata must not rebuild the marker.
|
||||
const previous = entity?.gevAlprRecord;
|
||||
const previous = alprMarkers.read(entity)?.record;
|
||||
if (!entity) {
|
||||
entity = state.dataSource.entities.add(entityDef);
|
||||
entity.gevAlprClamped = clamped;
|
||||
changed = true;
|
||||
}
|
||||
const marker = alprMarkers.write(entity);
|
||||
if (!existing) {
|
||||
marker.clamped = clamped;
|
||||
} else {
|
||||
if (
|
||||
previous.latitude !== record.latitude ||
|
||||
previous.longitude !== record.longitude
|
||||
) {
|
||||
setMarkerPosition(entity, position, clamped);
|
||||
entity.gevAlprCanvasPosition = null;
|
||||
entity.gevAlprDisplayPosition = null;
|
||||
marker.canvasPosition = null;
|
||||
marker.displayPosition = null;
|
||||
}
|
||||
if (
|
||||
previous.latitude !== record.latitude ||
|
||||
previous.longitude !== record.longitude ||
|
||||
previous.directionDeg !== record.directionDeg
|
||||
) {
|
||||
entity.gevAlprCanvasPosition = null;
|
||||
entity.gevAlprWedge = null;
|
||||
marker.canvasPosition = null;
|
||||
marker.wedge = null;
|
||||
}
|
||||
}
|
||||
entity.gevAlprRecord = record;
|
||||
marker.record = record;
|
||||
entityRecord(entity).trackedId = record.id;
|
||||
// The mapped camera datum has no elevation; it is not the clamped marker's
|
||||
// visual anchor. Only the selected marker samples the rendered surface,
|
||||
// at most once per second, through Cesium's public height APIs.
|
||||
entity.gevAlprDisplayPosition ??= null;
|
||||
entityRecord(entity).displayPosition = () =>
|
||||
entity.gevAlprDisplayPosition;
|
||||
marker.displayPosition ??= null;
|
||||
entityRecord(entity).displayPosition = () => marker.displayPosition;
|
||||
entityRecord(entity).labelModel = {
|
||||
title: alprDisplayId(record),
|
||||
details: alprLabelDetails(record, source),
|
||||
@@ -473,7 +480,8 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
if (changed) state.renderRevision += 1;
|
||||
state.onScreen = countOnScreen();
|
||||
for (const entity of state.dataSource.entities.values)
|
||||
if (entity.gevAlprClamped) clampedMarkers.set(entity.id, entity);
|
||||
if (alprMarkers.read(entity)?.clamped)
|
||||
clampedMarkers.set(entity.id, entity);
|
||||
resolveClampedFloors();
|
||||
const selectedEntity = state.selectedId
|
||||
? state.dataSource.entities.getById(state.selectedId)
|
||||
@@ -577,8 +585,9 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
}
|
||||
const entity = state.dataSource?.entities.getById(state.selectedId);
|
||||
const record = state.recordById.get(state.selectedId);
|
||||
if (entity?.gevAlprCanvasPosition) {
|
||||
entity.gevAlprDisplayPosition = entity.gevAlprCanvasPosition;
|
||||
const marker = alprMarkers.read(entity);
|
||||
if (marker?.canvasPosition) {
|
||||
marker.displayPosition = marker.canvasPosition;
|
||||
return;
|
||||
}
|
||||
if (!entity || !record || Date.now() - state.lastAnchorSampleAt < 1000)
|
||||
@@ -607,11 +616,9 @@ export function createAlprPresentation({ state, services, source }) {
|
||||
record.latitude,
|
||||
height,
|
||||
);
|
||||
if (
|
||||
!entity.gevAlprDisplayPosition ||
|
||||
Cesium.Cartesian3.distance(next, entity.gevAlprDisplayPosition) > 0.1
|
||||
) {
|
||||
entity.gevAlprDisplayPosition = next;
|
||||
const shown = marker?.displayPosition;
|
||||
if (!shown || Cesium.Cartesian3.distance(next, shown) > 0.1) {
|
||||
alprMarkers.write(entity).displayPosition = next;
|
||||
governorRequestRender('alpr-anchor');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user