Files
gods-eye-view/src/data/scenePick.js
T
Said TurkseverandClaude Opus 5.5 b1efd22f02 feat(street-level): one imagery source at a time; IC8 review fixes
- One provider is lit at a time: lighting a chip darkens the others, and a
  chip click records every registered provider's switch in one update so
  the share link keeps the swap (also off Google 3D, where Street View is
  not listed). The layer starts with the first default-on provider
  (Mapillary, free); params that light several keep the one already on,
  so reapplying them never flips (litByParams). The now-unreachable
  "several lit" chip paths are gone.
- Leaving Google 3D during a Street View lookup ends it without an error
  or a stuck loading state (openImage's early return resets it; the
  lookup loop rechecks availability).
- A source swap during a lookup no longer reports "no imagery".
- Street View lit off Google 3D says the panel needs the Google 3D map.
- Comments trimmed to the why; keySetupCore.test.mjs back to upstream's
  formatting plus the one new test (was a 551-line reformat).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:10:58 +01:00

99 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Validation for positions that came back from a Cesium screen pick.
*
* `scene.pickPosition()` reads the depth buffer. Over empty sky — high-altitude
* views where the pick ray misses everything renderable — that read can produce
* a Cartesian that is not a place on the globe. Feeding one to
* `Cartographic.fromCartesian` either throws
* `DeveloperError: normalized result is not a number`, returns undefined, or —
* worst of all — quietly succeeds and hands back somewhere underground.
*
* A pick that does not name a real place on the globe is a MISSED pick, so
* callers treat a degenerate result exactly as they already treat a miss.
*/
/**
* FLOOR. WGS84's polar radius is 6,356,752 m and the deepest bathymetry is
* ~11 km below the geoid, so nothing this app renders can sit nearer the
* ellipsoid's center than ~6,346,000 m. Six million keeps ~346 km of margin
* below that while still rejecting every core-interior value — and those are
* the dangerous ones, because they are finite and non-zero and so convert
* WITHOUT complaint: `(500, 0, 0)` becomes a point 6,378 km underground that
* reverse-geocodes as 0°, 0°.
*/
const MIN_PICK_MAGNITUDE_M = 6_000_000;
/**
* CEILING. The highest thing this app draws is a geostationary satellite,
* 42,164 km from the center. A million kilometres is ~24× that (and well past
* the Moon), so it cannot reject a real contact — but it does reject the absurd
* finite magnitudes that overflow Cesium's geodetic iteration into NaN, which
* throws exactly like a NaN input does.
*/
const MAX_PICK_MAGNITUDE_M = 1_000_000_000;
/**
* Whether a picked Cartesian names a real position on or above the globe, and
* so can be converted to a Cartographic both safely and meaningfully.
*
* @param {{x: number, y: number, z: number}|null|undefined} position
* @returns {boolean}
*/
export function isPickedWorldPosition(position) {
if (!position) return false;
const { x, y, z } = position;
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z))
return false;
const magnitude = Math.hypot(x, y, z);
return magnitude >= MIN_PICK_MAGNITUDE_M && magnitude <= MAX_PICK_MAGNITUDE_M;
}
/**
* The world position under a screen point: the rendered surface where the
* depth buffer has it, else the ellipsoid; null over the sky.
*
* @param {object} viewer A Cesium viewer.
* @param {{x: number, y: number}} screenPosition
* @returns {{x: number, y: number, z: number}|null} A Cartesian3.
*/
export function pickGroundPosition(viewer, screenPosition) {
const scene = viewer?.scene;
let position = null;
if (
scene?.pickPositionSupported &&
typeof scene.pickPosition === 'function'
) {
try {
position = scene.pickPosition(screenPosition);
} catch {
position = null;
}
}
if (
!isPickedWorldPosition(position) &&
typeof viewer?.camera?.pickEllipsoid === 'function'
) {
try {
position = viewer.camera.pickEllipsoid(screenPosition);
} catch {
position = null;
}
}
return isPickedWorldPosition(position) ? position : null;
}
/**
* Whether a `scene.pick()` result is the map itself: nothing, or 3D tile
* content (which has no id).
*
* @param {object|null|undefined} picked
* @returns {boolean}
*/
export function isSurfacePick(picked) {
if (!picked) return true;
if (picked.id !== undefined) return false;
return (
picked.content !== undefined || picked.primitive?.isCesium3DTileset === true
);
}