fix(street-level): camera height over Google 3D, cast only when it helps, gate token

From the code review of #768:

- view.js measured the camera's height above ground with globe.getHeight,
  but Google 3D hides the globe, so coverage picked its zoom from the
  ellipsoidal height. Over a high city (Denver, ~1,600 m) a camera 500 m up
  read 2,100 m: coverage stayed at z12/z13, whose tiles are too big to cast,
  and lines stayed draped on the roofs. With the globe hidden, the bare-earth
  height from the ground caster now answers; coverage re-picks its zoom when
  the surface mode changes. Verified in Denver: terrain mode, z14, cast lines.
  The core's surface check uses the same helper (and no longer subtracts the
  2 m lift from the camera height).
- castTile rebuilt a tile even when no line could be cast (terrain proxy
  down, tile too big); it now leaves such tiles as they are.
- The feature gate read Street Level's share token as a literal `0`; it now
  takes it from LAYER_STATE_REGISTRY, since a rebase can reassign it.

New view.test.mjs covers the shown-globe, hidden-globe and no-sample cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-02 01:01:40 +01:00
1 parent e01ffb3cad
commit abbf69a512
6 files changed
+96 -22

No files matched your search

@@ -172,9 +172,12 @@ export function createCoverage({ state, source }) {
const lines = entry.sequenceList
.filter((sequence) => passesImageryFilter(sequence, filter()))
.map((sequence) => sequence.coordinates);
state.context.groundCaster.prepareLines(lines, { signal }).then(() => {
const caster = state.context.groundCaster;
caster.prepareLines(lines, { signal }).then(() => {
const attached = [...state.coverage.tiles.values()].includes(entry);
if (signal.aborted || !attached || !terrainMode()) return;
// Nothing new to cast (terrain proxy down, tile too big): keep it draped.
if (!lines.some((coords) => caster.castLine(coords))) return;
// Keep the draped lines until the cast ones are built, so nothing blinks.
const previous = entry.primitives;
entry.primitives = [];
@@ -393,7 +396,9 @@ export function createCoverage({ state, source }) {
function refresh() {
const viewer = state.viewer;
if (!viewer || !state.context.isActive() || state.keyRequired) return;
const height = cameraHeightAboveGround(viewer);
const height = cameraHeightAboveGround(viewer, {
groundAt: state.context.groundCaster?.groundAt,
});
const sequenceZoom = coverageZoomForHeight(height);
const overviewZoom = sequenceZoom ? null : overviewZoomForHeight(height);
const zoom = sequenceZoom ?? overviewZoom;
@@ -481,10 +486,15 @@ export function createCoverage({ state, source }) {
notify();
}
/** Redraw loaded tiles draped or cast after the core's surface mode changes. */
/**
* Redraw loaded tiles draped or cast after the core's surface mode changes,
* then re-pick the zoom: the bare-earth height under the camera that the
* change brings in can move it (a high city reads much closer to the street).
*/
function setSurface() {
if (!state.context.isActive()) return;
rebuild();
refresh();
}
/** Look a sequence up across loaded tiles. */