Merge branch 'main' into pr/mapillary-layer

This commit is contained in:
Said Turksever authored and GitHub committed 2026-10-06 23:40:25 +01:00
commit 1e2f6675a0
5 files changed
+1193 -238

No files matched your search

+1069 -236
View File
File diff suppressed because it is too large. Load diff
+17 -1
View File
@@ -179,8 +179,24 @@ export function createFlightSnapshotRenderer({
// as the enrichment path — the class's GLB/scale may have changed.
if (prevMeta?.klass !== meta.klass) rendering._syncModelToClass(icao24);
} else {
// Start where the fleet pass will draw it: the fleet renders behind
// real time, so a fresh contact's displayed position is its fix
// projected back to that delayed time, and a grounded contact is then
// lifted onto the floor under that delayed position. Creating it at
// the raw fix drew it ahead until the next fleet tick snapped it back;
// skipping the floor started a taxiing contact low and then raised it.
const delayed = motion._deadReckon(icao24, new Cesium.Cartesian3());
const displayed =
(delayed &&
motion._floorGroundedDisplayPosition(
icao24,
flightState.records.data.get(icao24) || meta,
delayed,
rendering._modelOwnsVisual(icao24),
)) ||
position;
const bb = flightState._billboardCollection.add({
position,
position: displayed,
image: aircraftIcon(rendering._iconKind(icao24, meta.klass)),
width: isTracked ? 24 : 20,
height: isTracked ? 24 : 20,
+1
View File
@@ -33,6 +33,7 @@ export function createMilitaryFlightLayer({
militaryRegistry: services.militaryRegistry,
rendering: parts.rendering,
tracking: parts.tracking,
motion: parts.motion,
queries: parts.queries,
});
parts.ingestion = createIngestion({
+98
View File
@@ -113,3 +113,101 @@ test('a normalized source can retain its stale reason without changing standalon
assert.equal(layer.getStats().error, null);
assert.equal(layer.getStats().stale, true);
});
test('a newly seen military contact appears at its delayed position, not ahead of it', async () => {
// The military fleet renders RENDER_DELAY_SEC behind real time, so a fresh
// contact's first displayed position is its fix projected back to that
// delayed time. Creating the billboard at the raw fix drew it ahead until
// the next fleet tick jumped it back.
const Cesium = await import('cesium');
const { RENDER_DELAY_SEC } = await import('./policy.js');
// The application's own scene services, as src/app/layers/militaryFlights.js
// composes them; only the surface samplers and registry are stubbed.
const supplied = {
picking: await import('../../data/pickRegistry.js'),
sprites: await import('../../data/spriteOrder.js'),
trails: await import('../../data/trailRenderer.js'),
aircraftPresentation: await import('../../data/tr3bRegistry.js'),
camera: await import('../../data/trackedCamera.js'),
labels: await import('../../data/detectionDraw.js'),
geoid: await import('../../data/geoid.js'),
focus: await import('../../data/focusDeemphasis.js'),
readout: await import('../../data/trackedReadout.js'),
context: await import('../../data/contextStore.js'),
render: await import('../../renderGovernor.js'),
recession: await import('../../data/aircraftRecession.js'),
groundFloor: await import('../../data/groundFloor.js'),
groundSnap: await import('../../data/groundSnap.js'),
meshFloor: { sampleMeshFloorCells: () => {} },
militaryRegistry: { registerMilitaryIcaos: () => {} },
};
const fixLon = -97.6;
const fixLat = 30.3;
const speedMps = 200;
const fixAgeMs = 2_000;
const nowMs = Date.now();
const layer = createMilitaryFlightLayer({
services: supplied,
source: {
label: 'Fixture aircraft',
async getSnapshot() {
return {
source: 'Fixture aircraft',
complete: true,
observedAtMs: nowMs,
records: [
{
id: 'ae0001',
latitude: fixLat,
longitude: fixLon,
onGround: false,
baroAltitudeM: 9_000,
courseDeg: 90,
speedMps,
positionTimeMs: nowMs - fixAgeMs,
contactTimeMs: nowMs - fixAgeMs,
},
],
};
},
},
});
const added = [];
layer.testing._setTrackedMilitaryRefreshStateForTest({
icao24: 'seed00',
entity: null,
meta: { rawLat: 0, rawLon: 0, onGround: false },
billboard: {
show: false,
position: Cesium.Cartesian3.fromDegrees(0, 0, 0),
},
billboardCollection: {
show: false,
add(options) {
const billboard = { ...options };
added.push(billboard);
return billboard;
},
remove() {},
},
viewer: { camera: { positionCartographic: null }, scene: {} },
tracked: false,
});
await layer.update({ camera: { positionCartographic: null }, scene: {} });
const billboard = added.find((entry) => entry.id === 'ae0001');
assert.ok(billboard, 'the new contact gets a billboard');
const shown = Cesium.Cartographic.fromCartesian(billboard.position);
const behindM = Cesium.Cartesian3.distance(
Cesium.Cartesian3.fromRadians(shown.longitude, shown.latitude, 0),
Cesium.Cartesian3.fromDegrees(fixLon, fixLat, 0),
);
const expectedM = speedMps * (RENDER_DELAY_SEC - fixAgeMs / 1000);
assert.ok(
Cesium.Math.toDegrees(shown.longitude) < fixLon,
'drawn behind the fix',
);
assert.ok(
Math.abs(behindM - expectedM) < speedMps * 3,
`expected about ${Math.round(expectedM)} m behind the fix, got ${Math.round(behindM)} m`,
);
});
+8 -1
View File
@@ -23,6 +23,7 @@ export function createMilitarySnapshotRenderer({
militaryRegistry,
rendering,
tracking,
motion,
queries,
}) {
const { warmGroundFloor, cachedGroundFloor, GROUND_FLOOR_LIFT_M } =
@@ -142,8 +143,14 @@ export function createMilitarySnapshotRenderer({
// Round 5: depth policy is uniform (always depth-test-free, see
// _groundDepthDistance) — nothing to flip on landing/takeoff.
} else {
// Start where the fleet pass will draw it: the fleet renders behind
// real time, so a fresh contact's displayed position is its fix
// projected back to that delayed time. Creating it at the raw fix drew
// it ahead until the next fleet tick snapped it back.
const displayed =
motion._deadReckon(icao24, new Cesium.Cartesian3()) || position;
const bb = flightState._billboardCollection.add({
position,
position: displayed,
image: aircraftIcon(rendering._iconKind(icao24, meta.klass)),
width: isTracked ? 24 : 20,
height: isTracked ? 24 : 20,