Keep cable pick info and the prewarm flag off Cesium objects

Submarine cables wrote each pick's info both to a per-load WeakMap and to a
__gevTeleGeography expando, always together; one cablePicks store replaces
both and resolves the same objects. The voice view-target prewarm marks its
viewer in a WeakSet instead of a __gevViewTargetPrewarmInstalled property.
This commit is contained in:
Sameh Khamis committed 2026-11-03 10:01:12 -08:00
1 parent f86dcc9179
commit a40e5c334c
9 files changed
+31 -23

No files matched your search

+4
View File
@@ -2045,6 +2045,8 @@
"src/layers/submarineCables/index.js",
"src/layers/submarineCables/ingestion.js",
"src/layers/submarineCables/interaction.js",
"src/layers/submarineCables/pickState.js",
"src/data/entityRecords.js",
"src/layers/submarineCables/lifecycle.js",
"src/layers/submarineCables/overlay.js",
"src/layers/submarineCables/policy.js",
@@ -2622,6 +2624,7 @@
"src/layers/submarineCables/index.js",
"src/layers/submarineCables/ingestion.js",
"src/layers/submarineCables/interaction.js",
"src/layers/submarineCables/pickState.js",
"src/layers/submarineCables/lifecycle.js",
"src/layers/submarineCables/overlay.js",
"src/layers/submarineCables/policy.js",
@@ -3529,6 +3532,7 @@
"src/layers/submarineCables/index.js",
"src/layers/submarineCables/ingestion.js",
"src/layers/submarineCables/interaction.js",
"src/layers/submarineCables/pickState.js",
"src/layers/submarineCables/lifecycle.js",
"src/layers/submarineCables/overlay.js",
"src/layers/submarineCables/policy.js",
@@ -22,6 +22,7 @@ import {
selectCableReferenceLabelWinners,
updateCableReferenceStem,
} from './telegeographySubmarineCables.js';
import { cablePicks } from '../layers/submarineCables/pickState.js';
test('cable reference winners remain nearest-first and hard-capped', () => {
const records = Array.from({ length: 300 }, (_, index) => ({
@@ -1840,7 +1841,7 @@ test('multipart association accepts frozen provider collections without modifyin
for (const dataSource of env.dataSources.slice(0, 2)) {
assert.deepEqual(
dataSource.entities.values.map(
(entity) => entity.__gevTeleGeography?.label,
(entity) => cablePicks.read(entity)?.info.label,
),
['Alpha cable', 'Alpha cable', 'Alpha cable', 'Beta cable'],
);
@@ -1866,7 +1867,7 @@ test('multipart cables retain their own styling, picking and culling on every se
assert.equal(entities.length, 4);
for (const [index, entity] of entities.entries()) {
const feature = features[index < 3 ? 0 : 1];
const info = entity.__gevTeleGeography;
const info = cablePicks.read(entity)?.info;
assert.equal(info?.label, feature.properties.name, entity.id);
assert.deepEqual(info.reference, {
lon: feature.properties.coordinates[0],
@@ -1922,7 +1923,7 @@ test('multipart association supports missing and repeated IDs without guessing C
['alpha', 'alpha_2', 'alpha_3', 'alpha_2_2', 'cable-2', 'alpha_4'],
);
assert.deepEqual(
entities.map((entity) => entity.__gevTeleGeography?.label),
entities.map((entity) => cablePicks.read(entity)?.info.label),
[
'Alpha cable',
'Alpha cable',
@@ -1934,7 +1935,7 @@ test('multipart association supports missing and repeated IDs without guessing C
);
assert.deepEqual(
env.dataSources[2].entities.values.map(
(entity) => entity.__gevTeleGeography.featureId,
(entity) => cablePicks.read(entity)?.info.featureId,
),
['alpha', 'alpha_2', 'cable-2', 'alpha'],
);
@@ -1951,12 +1952,12 @@ test('multipart landing geometry keeps every pick with one reference per feature
try {
const entities = env.dataSources[1].entities.values;
assert.deepEqual(
entities.map((entity) => entity.__gevTeleGeography?.label),
entities.map((entity) => cablePicks.read(entity)?.info.label),
['Alpha cable', 'Alpha cable', 'Alpha cable', 'Beta cable'],
);
assert.ok(
entities.every(
(entity) => entity.__gevTeleGeography.kind === 'landing-point',
(entity) => cablePicks.read(entity)?.info.kind === 'landing-point',
),
);
assert.equal(env.dataSources[2].entities.values.length, 2);
@@ -1989,11 +1990,11 @@ test('every bundled cable segment is associated with its source feature', async
for (const [index, entity] of entities.entries()) {
const feature = expandedFeatures[index];
assert.equal(
entity.__gevTeleGeography?.label,
cablePicks.read(entity)?.info.label,
feature.properties.name,
entity.id,
);
assert.deepEqual(entity.__gevTeleGeography.reference, {
assert.deepEqual(cablePicks.read(entity)?.info.reference, {
lon: feature.properties.coordinates[0],
lat: feature.properties.coordinates[1],
});
-1
View File
@@ -142,7 +142,6 @@ export function createIngestion({ state, parts, source }) {
const cableEntities = state._cableDataSource.entities.values;
const landingEntities = state._landingDataSource.entities.values;
state._pickByEntity = new WeakMap();
state._referenceRecords = [];
state._surfaceRecords = [];
+4 -6
View File
@@ -1,10 +1,10 @@
import * as Cesium from 'cesium';
import { isPointerFree } from '../../data/inputOwnership.js';
import { cablePicks } from './pickState.js';
export function createInteraction({ state, screenSpaceEventHandlerFactory }) {
function registerPickEntity(entity, info) {
entity.__gevTeleGeography = info;
state._pickByEntity.set(entity, info);
cablePicks.write(entity).info = info;
}
function beginInteraction(viewer) {
@@ -25,9 +25,8 @@ export function createInteraction({ state, screenSpaceEventHandlerFactory }) {
if (!picked) return null;
const primitive = picked.primitive;
if (primitive) {
const primitiveInfo = state._pickByEntity.get(primitive);
const primitiveInfo = cablePicks.read(primitive)?.info;
if (primitiveInfo) return primitiveInfo;
if (primitive.__gevTeleGeography) return primitive.__gevTeleGeography;
if (
primitive.id &&
typeof primitive.id === 'object' &&
@@ -39,9 +38,8 @@ export function createInteraction({ state, screenSpaceEventHandlerFactory }) {
const entity = picked.id;
if (entity) {
const entityInfo = state._pickByEntity.get(entity);
const entityInfo = cablePicks.read(entity)?.info;
if (entityInfo) return entityInfo;
if (entity.__gevTeleGeography) return entity.__gevTeleGeography;
if (entity.id && typeof entity.id === 'object' && entity.id.reference) {
return entity.id;
}
-1
View File
@@ -133,7 +133,6 @@ export function createLifecycle({ state, parts, source, mapStackEventTarget }) {
state._referenceDataSource = null;
state._referenceRecords = [];
state._surfaceRecords = [];
state._pickByEntity = new WeakMap();
state._referenceLabelCount = 0;
state._publishScratch.length = 0;
state._markerBlendDone = false;
+7
View File
@@ -0,0 +1,7 @@
import { createEntityStore } from '../../data/entityRecords.js';
/**
* The reference a picked cable, landing point or reference marker flies to:
* `info` is `{ label, kind, featureId, reference: { lon, lat } }`.
*/
export const cablePicks = createEntityStore();
+2 -3
View File
@@ -10,6 +10,7 @@ import {
selectCableReferenceLabelWinners,
cableReferencePriority,
} from './overlay.js';
import { cablePicks } from './pickState.js';
export function createRendering({ state }) {
function resetPublishSignature() {
@@ -40,7 +41,6 @@ export function createRendering({ state }) {
state._referenceDataSource = null;
state._referenceRecords = [];
state._surfaceRecords = [];
state._pickByEntity = new WeakMap();
state._referenceLabelCount = 0;
state._publishScratch.length = 0;
// The blend pass targets the collections being dropped; the rebuilt
@@ -180,8 +180,7 @@ export function createRendering({ state }) {
},
});
entity.__gevTeleGeography = info;
state._pickByEntity.set(entity, info);
cablePicks.write(entity).info = info;
const record = {
id: entity.id,
entity,
-2
View File
@@ -69,8 +69,6 @@ export function createState({ sweepClock, overlayHost }) {
state._markerBlendInvariantWarned = false;
state._pickByEntity = new WeakMap();
state._referenceLabelCount = 0;
state._referenceSweepGate = createCableReferenceSweepGate({
+5 -2
View File
@@ -2869,9 +2869,12 @@ async function basemapLabelsAt(
};
}
/** Viewers that already prewarm view targets on camera settle. */
const prewarmedViewers = new WeakSet();
function installViewTargetPrewarm(viewer) {
if (viewer.__gevViewTargetPrewarmInstalled) return;
viewer.__gevViewTargetPrewarmInstalled = true;
if (prewarmedViewers.has(viewer)) return;
prewarmedViewers.add(viewer);
let timer = null;
let reportedPrewarmFailure = false;
viewer.camera.moveEnd.addEventListener(() => {