mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Merge latest CCTV foundations and retain reviewed follow-up fixes
This commit is contained in:
commit
664c18d97a
3 files changed
+112
-15
No files matched your search
@@ -4464,6 +4464,11 @@ silently demoting every later lookup for the session.
|
|||||||
Tarktee owns both XML requests with one 30-second deadline and shared abort;
|
Tarktee owns both XML requests with one 30-second deadline and shared abort;
|
||||||
a failed header or body read cancels its sibling, including a response arriving
|
a failed header or body read cancels its sibling, including a response arriving
|
||||||
after cleanup.
|
after cleanup.
|
||||||
|
Both Tarktee XML catalogs refuse redirects and have a 4 MiB per-document
|
||||||
|
limit, enforced against declared length and streamed bytes. A rejected fetch,
|
||||||
|
unsuccessful response, or body-read failure releases both available response
|
||||||
|
bodies; read failures also cancel an active sibling reader. Cameras sharing
|
||||||
|
an image number are deduplicated before the Tarktee pack cap is applied.
|
||||||
- **CCTV v3 UX — viewshed + calibration gizmo** (built 2026-07-05 and field
|
- **CCTV v3 UX — viewshed + calibration gizmo** (built 2026-07-05 and field
|
||||||
validated 2026-07-21): the COVERAGE toggle is a
|
validated 2026-07-21): the COVERAGE toggle is a
|
||||||
tri-state cycle `OFF → ON → VIEWSHED`; viewshed mode renders each visible camera's frustum
|
tri-state cycle `OFF → ON → VIEWSHED`; viewshed mode renders each visible camera's frustum
|
||||||
|
|||||||
+65
-15
@@ -11,8 +11,10 @@
|
|||||||
* exactly once per camera ACTIVATION (the §9.1 obstruction probe) and
|
* exactly once per camera ACTIVATION (the §9.1 obstruction probe) and
|
||||||
* never again in steady state; shared mesh-floor sampling fires at most
|
* never again in steady state; shared mesh-floor sampling fires at most
|
||||||
* once per coarse cell during the staggered geometry-load drain, and
|
* once per coarse cell during the staggered geometry-load drain, and
|
||||||
* actually COMPLETES once tiles load (>=1 real sample — the
|
* actually COMPLETES once tiles load in photoreal mode (>=1 real sample
|
||||||
* ceiling alone would pass trivially at Δ=0 if the snap never finished),
|
* — the ceiling alone would pass trivially at Δ=0 if the snap never
|
||||||
|
* finished). Globe mode instead proves the rendered mount uses a real
|
||||||
|
* terrain height with zero mesh samples. Sampling
|
||||||
* stays flat while idle, and stays flat for non-positional pose edits.
|
* stays flat while idle, and stays flat for non-positional pose edits.
|
||||||
* 2. Geometry contract: the 5-polyline frustum wireframe + far-cap plane
|
* 2. Geometry contract: the 5-polyline frustum wireframe + far-cap plane
|
||||||
* exist for the active camera, the plane's corners coincide with the
|
* exist for the active camera, the plane's corners coincide with the
|
||||||
@@ -658,13 +660,68 @@ async function main() {
|
|||||||
// (existence, not just ceiling). The wait below tolerates the update-tick
|
// (existence, not just ceiling). The wait below tolerates the update-tick
|
||||||
// latency of the completion pass (update interval is 10s).
|
// latency of the completion pass (update interval is 10s).
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Globe terrain uses the cached DEM/prior and intentionally never calls
|
const globeMode = await page.evaluate(
|
||||||
// scene.sampleHeight. Only the photoreal regime owns a one-shot mesh snap.
|
() => window.__godsEyeView.viewer.scene.globe?.show !== false,
|
||||||
// Keep the mesh completion floor strict when that regime is active.
|
|
||||||
const photorealRegime = await page.evaluate(
|
|
||||||
() => window.__godsEyeView.viewer.scene.globe?.show === false,
|
|
||||||
);
|
);
|
||||||
if (photorealRegime) {
|
if (globeMode) {
|
||||||
|
// Globe stacks intentionally perform no scene.sampleHeight query.
|
||||||
|
// Require independently fetched DEM heights and the actual rendered
|
||||||
|
// mount, so a catalog/geoid fallback cannot turn zero samples into a pass.
|
||||||
|
console.log(
|
||||||
|
'Checking terrain-globe completion against live DEM heights...',
|
||||||
|
);
|
||||||
|
const terrain = await page.evaluate(async () => {
|
||||||
|
const camera = window.__godsEyeView.dataManager.layers
|
||||||
|
.get('cctv')
|
||||||
|
.module.getUIState().activeCamera;
|
||||||
|
const exact = `${camera.lon.toFixed(5)},${camera.lat.toFixed(5)}`;
|
||||||
|
const cell = `${camera.lon.toFixed(3)},${camera.lat.toFixed(3)}`;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/terrain/heights?points=${encodeURIComponent(`${exact};${cell}`)}`,
|
||||||
|
{ signal: AbortSignal.timeout(30000) },
|
||||||
|
);
|
||||||
|
const body = await response.json();
|
||||||
|
return { ok: response.ok, results: body.results };
|
||||||
|
});
|
||||||
|
const heights = terrain.results?.map((result) => result?.ellipsoid);
|
||||||
|
const terrainReady =
|
||||||
|
terrain.ok && heights?.length === 2 && heights.every(Number.isFinite);
|
||||||
|
const mountResolved =
|
||||||
|
terrainReady &&
|
||||||
|
(await waitForPredicate(
|
||||||
|
page,
|
||||||
|
(expected, base) => {
|
||||||
|
const { viewer, dataManager } = window.__godsEyeView;
|
||||||
|
const camera = dataManager.layers
|
||||||
|
.get('cctv')
|
||||||
|
.module.getUIState().activeCamera;
|
||||||
|
const ray = viewer.entities.getById(`cctv-${camera.id}-ray-tl`);
|
||||||
|
const mount = ray?.polyline?.positions.getValue(
|
||||||
|
viewer.clock.currentTime,
|
||||||
|
)?.[0];
|
||||||
|
const height =
|
||||||
|
mount &&
|
||||||
|
viewer.scene.globe.ellipsoid.cartesianToCartographic(mount)
|
||||||
|
.height;
|
||||||
|
return (
|
||||||
|
Number.isFinite(camera.groundPriorM) &&
|
||||||
|
Math.abs(camera.groundPriorM - expected[0]) < 0.01 &&
|
||||||
|
Number.isFinite(height) &&
|
||||||
|
expected.some(
|
||||||
|
(ground) =>
|
||||||
|
Math.abs(height - ground - camera.mountHeightM) < 0.05,
|
||||||
|
) &&
|
||||||
|
window.__qaCounters.sampleHeight === base
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ timeoutMs: 30000, args: [heights, c0.sampleHeight] },
|
||||||
|
));
|
||||||
|
record(
|
||||||
|
'terrain-globe completion uses a real DEM height at the rendered mount without mesh samples',
|
||||||
|
Boolean(mountResolved),
|
||||||
|
`terrain=${JSON.stringify(heights)}, mesh sample Δ=${(await readCounters()).sampleHeight - c0.sampleHeight}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
console.log('Waiting for tiles + the one-shot completion pass...');
|
console.log('Waiting for tiles + the one-shot completion pass...');
|
||||||
const tilesSeenLoaded = await waitForTilesLoaded(page, 45000);
|
const tilesSeenLoaded = await waitForTilesLoaded(page, 45000);
|
||||||
let sampleFloorOk = false;
|
let sampleFloorOk = false;
|
||||||
@@ -683,13 +740,6 @@ async function main() {
|
|||||||
? `total real samples since enable=${cFloor.sampleHeight - c0.sampleHeight}`
|
? `total real samples since enable=${cFloor.sampleHeight - c0.sampleHeight}`
|
||||||
: 'tilesLoaded never observed within 45s — floor not assessable this run',
|
: 'tilesLoaded never observed within 45s — floor not assessable this run',
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
const cFloor = await readCounters();
|
|
||||||
record(
|
|
||||||
'terrain-globe uses its DEM/prior without photoreal mesh sampling',
|
|
||||||
cFloor.sampleHeight === c0.sampleHeight,
|
|
||||||
`mesh samples since enable=${cFloor.sampleHeight - c0.sampleHeight}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let the completion pass finish before the steady-state window: require
|
// Let the completion pass finish before the steady-state window: require
|
||||||
|
|||||||
@@ -192,6 +192,48 @@ test('Tarktee loader refuses a redirect and cancels both response bodies', async
|
|||||||
assert.equal(cancelled.images, true, 'sibling 200 body is also cancelled');
|
assert.equal(cancelled.images, true, 'sibling 200 body is also cancelled');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Tarktee loader cancels fulfilled bodies when either fetch rejects', async (t) => {
|
||||||
|
for (const failedUrl of [TARKTEE_LOCATIONS_URL, TARKTEE_IMAGES_URL]) {
|
||||||
|
for (const delayedSibling of [false, true]) {
|
||||||
|
await t.test(
|
||||||
|
`${failedUrl}, delayed sibling: ${delayedSibling}`,
|
||||||
|
async (t) => {
|
||||||
|
quiet(t);
|
||||||
|
let cancelled = false;
|
||||||
|
const response = new Response(
|
||||||
|
new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(new TextEncoder().encode('<xml/>'));
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
cancelled = true;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
t.mock.method(globalThis, 'fetch', async (url) => {
|
||||||
|
if (String(url) === failedUrl) throw new TypeError('fetch failed');
|
||||||
|
if (delayedSibling) await new Promise(setImmediate);
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.deepEqual(await loadTarkteeSourcesFromDatex(), []);
|
||||||
|
// An aborted mock transport may still deliver its queued response.
|
||||||
|
if (delayedSibling) await new Promise(setImmediate);
|
||||||
|
assert.equal(
|
||||||
|
cancelled,
|
||||||
|
true,
|
||||||
|
'fulfilled sibling body is cancelled',
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await response.body.cancel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('Tarktee loader cancels the sibling body when one feed fails or overflows the byte cap', async (t) => {
|
test('Tarktee loader cancels the sibling body when one feed fails or overflows the byte cap', async (t) => {
|
||||||
quiet(t);
|
quiet(t);
|
||||||
let siblingCancelled = false;
|
let siblingCancelled = false;
|
||||||
|
|||||||
Reference in new issue
Block a user