Files
gods-eye-view/src/data/geoImportPins.js
T
Mazeyar Moeini Feizabadi 5ea6d81310 fix(import): correct clamping and bound local-file rendering
Normalize GeoJSON ground footprints, preserve explicit KML track heights,
and tessellate ground paths before native loading. Validate coordinates,
geometry types and decoded image budgets before allocating native resources;
strip loader-sensitive metadata and settle deferred failures and cancellation.

Render static pins in owned billboard collections instead of walking tens
of thousands of point entities each frame. Refine nearby ground heights in
batches, isolate imported pins from height picks, and release owned resources
and point selections on removal. Use safe ray origins above a hidden coarse
globe and retain valid mesh heights outside the current footprint.

Frame complete geometry with viewport-aware nadir arrivals, including walls
and rotated overlays; respect camera gestures and accept document file drops.
Strengthen real-loader, native 3D Tiles, paint, clock and lifecycle coverage.
Addresses both P1 and all 25 P2 findings in the follow-up review of #1122.
2026-10-15 04:10:41 +02:00

129 lines
3.8 KiB
JavaScript

import * as Cesium from 'cesium';
const STYLE = [
'image',
'scale',
'rotation',
'alignedAxis',
'horizontalOrigin',
'verticalOrigin',
'color',
'eyeOffset',
'pixelOffset',
'width',
'height',
'scaleByDistance',
'translucencyByDistance',
'pixelOffsetScaleByDistance',
'distanceDisplayCondition',
'sizeInMeters',
'disableDepthTestDistance',
'imageSubRegion',
];
class ImportedBillboards extends Cesium.BillboardCollection {
update(frameState) {
// Height queries should find the map, never another imported marker.
// Ordinary click picks and rendered screenshots still include the pins.
if (frameState.passes.pick && frameState.passes.offscreen) return;
super.update(frameState);
}
}
/** Own static imported pins without a per-frame Entity visualizer walk. */
export function createImportedPins(dataSource, viewer) {
const all = dataSource.entities.values.slice();
const now = Cesium.JulianDate.now();
const pins = all.filter((entity) => entity.billboard && entity.position);
if (!pins.length) return { mount() {}, sync() {}, destroy() {} };
const collection = new ImportedBillboards({ scene: viewer.scene });
const billboards = [];
try {
for (const entity of pins) {
const position = entity.position.getValue(now);
if (
!position ||
![position.x, position.y, position.z].every(Number.isFinite)
)
throw new Error('Invalid imported point position.');
const options = {
id: entity,
position,
show:
entity.isShowing && entity.billboard.show?.getValue(now) !== false,
heightReference: Cesium.HeightReference.NONE,
};
for (const key of STYLE) {
const value = entity.billboard[key]?.getValue(now);
if (value !== undefined) options[key] = value;
}
billboards.push(collection.add(options));
}
} catch (error) {
collection.destroy();
throw error;
}
// Rebuild once before the source is added to DataSourceDisplay. Removing
// one entity at a time would itself make this transition quadratic.
dataSource.entities.suspendEvents();
try {
dataSource.entities.removeAll();
for (const entity of all)
if (!entity.billboard || !entity.position)
dataSource.entities.add(entity);
} finally {
dataSource.entities.resumeEvents();
}
Object.defineProperty(dataSource, 'importedEntities', {
value: all,
configurable: true,
});
Object.defineProperty(dataSource, 'importedBillboards', {
value: collection,
configurable: true,
});
let show = dataSource.show,
mounted = false,
destroyed = false;
Object.defineProperty(dataSource, 'show', {
configurable: true,
get: () => show,
set(value) {
show = Boolean(value);
dataSource.entities.show = show;
collection.show = show;
},
});
collection.show = show;
return {
mount() {
if (destroyed || mounted) return;
viewer.scene.primitives?.add(collection);
mounted = true;
},
sync() {
if (destroyed) return;
for (let i = 0; i < pins.length; i++) {
billboards[i].position = pins[i].position.getValue(now);
billboards[i].show =
pins[i].isShowing && pins[i].billboard.show?.getValue(now) !== false;
}
viewer.scene.requestRender?.();
},
destroy() {
if (destroyed) return;
destroyed = true;
if (viewer.trackedEntity && pins.includes(viewer.trackedEntity))
viewer.trackedEntity = undefined;
if (viewer.selectedEntity && pins.includes(viewer.selectedEntity))
viewer.selectedEntity = undefined;
if (mounted && !viewer.scene.primitives?.isDestroyed?.())
viewer.scene.primitives?.remove(collection);
if (!collection.isDestroyed()) collection.destroy();
all.length = 0;
pins.length = 0;
billboards.length = 0;
},
};
}