fix(street-level): IC8 review of the Street View provider

Google's Maps Platform terms forbid Street View imagery beside non-Google
maps, so the provider declares requiresMapStack: 'photoreal': off Google
3D its chip is dashed with the reason, lookups and opens skip it, and
leaving Google 3D closes its panorama.

- Ground clicks: only a click on the map itself (scenePick.isSurfacePick:
  nothing picked or Google 3D tile content) at street zoom opens Street
  View, and the camera stays put; higher up the hint says to zoom in.
  scenePick.pickGroundPosition replaces the copy in Directions.
- Viewer: poses go out at once and again when the date and address
  arrive, so an open never hangs on that lookup; one lookup per panorama,
  seeded by the nearest lookup; a close settles a pending open; failed
  lookups are asked again; poses follow position changes, not pano_changed.
- Loader: the gm_authFailure handler is installed once, so a retried load
  reports a refusal once.
- Viewer host: a prewarm that lands after another provider took the host
  is released; FIT/FILL availability is set on every open.
- Provider: a refusal while switched off reports no error over another
  photo; SINCE compares against the end of Google's month; messages name
  the referrer and network causes; FLAT says Street View is 360° only.
- The layer row names the key of the providers that are on; the MCP panel
  CSP admits Street View's tiles, control images and photos.
- Tests for each, against fakes that now follow Google's event order; the
  browser gate restores its state, waits instead of sleeping, aims its
  panel clicks and corner drags, and asserts the camera stays put.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-08 02:59:51 +01:00
1 parent 22b0009138
commit 7bfcf177ac
35 files changed
+1108 -388

No files matched your search

@@ -20,16 +20,24 @@ export const COLORS = Object.freeze({
export const GOOGLE_CREDIT_HTML =
'Street View imagery © <a href="https://www.google.com/streetview/" target="_blank" rel="noopener">Google</a>';
/** Shown when Google refuses the key for the Maps JavaScript API. */
/**
* Shown when Google refuses the key (gm_authFailure): the Maps JavaScript API
* is not enabled for it, this site is not an allowed referrer, or billing is off.
*/
export const KEY_REJECTED_MESSAGE =
'Google rejected GOOGLE_MAPS_API_KEY for Street View — enable the Maps JavaScript API for it';
'Google refused GOOGLE_MAPS_API_KEY for Street View — check that the Maps JavaScript API is enabled for it and this site is an allowed referrer';
/** A nearest-panorama lookup that takes longer has failed. */
export const NEAREST_TIMEOUT_MS = 10_000;
/** A panorama that has not shown by then has failed to open. */
export const OPEN_TIMEOUT_MS = 20_000;
/** Google answered nothing (most often: the Maps JavaScript API is not enabled). */
/** Google answered nothing: the network, or a key Google ignores. */
export const NO_ANSWER_MESSAGE =
'Street View did not answer — check that the Maps JavaScript API is enabled for GOOGLE_MAPS_API_KEY';
'Street View did not answer — check the network, and that the Maps JavaScript API is enabled for GOOGLE_MAPS_API_KEY';
/** Under FLAT, in place of the click hint: every Street View photo is 360°. */
export const FLAT_FILTER_HINT = 'Street View has 360° photos only';
/** Shown while the provider is on, in place of coverage lines it does not have. */
export const GROUND_CLICK_HINT = 'click a street to open Street View';
@@ -55,6 +63,17 @@ export function imageDateMs(imageDate) {
return Date.UTC(Number(match[1]), month - 1, 1);
}
/**
* The last millisecond of an image's month: Google dates by month, so a
* "since" window keeps a panorama taken any time in its cut-off month.
*/
export function imageMonthEndMs(imageDate) {
const start = imageDateMs(imageDate);
if (start === null) return null;
const date = new Date(start);
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1) - 1;
}
/** The photographer from a copyright line ('© 2024 Google' → 'Google'). */
export function creatorFrom(copyright) {
const text = String(copyright ?? '')