mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
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:
1 parent
22b0009138
commit
7bfcf177ac
35 files changed
+1108
-388
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { passesImageryFilter } from '../../filter.js';
|
||||
import { FOLLOW_MAP_STACK_ID } from '../../policy.js';
|
||||
import { createMapsLoader } from './mapsLoader.js';
|
||||
import { createGoogleViewer } from './viewer.js';
|
||||
import {
|
||||
@@ -8,13 +9,14 @@ import {
|
||||
GOOGLE_LABEL,
|
||||
GOOGLE_NAME,
|
||||
GOOGLE_PROVIDER_ID,
|
||||
FLAT_FILTER_HINT,
|
||||
GROUND_CLICK_HINT,
|
||||
KEY_REJECTED_MESSAGE,
|
||||
NEAREST_RADIUS_M,
|
||||
NEAREST_TIMEOUT_MS,
|
||||
NO_ANSWER_MESSAGE,
|
||||
PICK_PREFIX,
|
||||
imageDateMs,
|
||||
imageMonthEndMs,
|
||||
} from './policy.js';
|
||||
|
||||
/**
|
||||
@@ -69,6 +71,8 @@ export function createGoogleProvider({
|
||||
colors: COLORS,
|
||||
credit: Object.freeze({ html: GOOGLE_CREDIT_HTML }),
|
||||
groundClick: true,
|
||||
// Google's terms forbid Street View beside non-Google maps.
|
||||
requiresMapStack: FOLLOW_MAP_STACK_ID,
|
||||
// Every panorama is billed to the Maps key: the user lights the chip.
|
||||
defaultOn: false,
|
||||
|
||||
@@ -76,12 +80,21 @@ export function createGoogleProvider({
|
||||
let keyRequired = !getApiKey();
|
||||
let active = false;
|
||||
let service = null;
|
||||
/** The one StreetViewService the lookups and the viewer share. */
|
||||
async function getService() {
|
||||
const lib = await loader.importLibrary('streetView');
|
||||
service ||= new lib.StreetViewService();
|
||||
return service;
|
||||
}
|
||||
const viewer = createGoogleViewer({
|
||||
loader,
|
||||
getService,
|
||||
render: context.services?.render,
|
||||
});
|
||||
const stopAuthWatch = loader.onAuthFailure(() => {
|
||||
context.actions.reportError(KEY_REJECTED_MESSAGE);
|
||||
// Switched off since: its chip says so; no error over another photo.
|
||||
if (context.isActive?.())
|
||||
context.actions.reportError(KEY_REJECTED_MESSAGE);
|
||||
context.notify();
|
||||
});
|
||||
|
||||
@@ -120,7 +133,11 @@ export function createGoogleProvider({
|
||||
kind: null,
|
||||
loading: false,
|
||||
hint:
|
||||
active && !keyRequired && !keyRejected ? GROUND_CLICK_HINT : '',
|
||||
!active || keyRequired || keyRejected
|
||||
? ''
|
||||
: context.getFilter().pano === 'flat'
|
||||
? FLAT_FILTER_HINT
|
||||
: GROUND_CLICK_HINT,
|
||||
error: keyRejected ? KEY_REJECTED_MESSAGE : null,
|
||||
keyRequired,
|
||||
keyRejected,
|
||||
@@ -140,13 +157,14 @@ export function createGoogleProvider({
|
||||
if (filter.pano === 'flat') return null;
|
||||
const lookup = (async () => {
|
||||
const lib = await loader.importLibrary('streetView');
|
||||
service ||= new lib.StreetViewService();
|
||||
await getService();
|
||||
const { GOOGLE, OUTDOOR } = lib.StreetViewSource;
|
||||
try {
|
||||
const { data } = await service.getPanorama({
|
||||
location: { lat, lng: lon },
|
||||
radius: NEAREST_RADIUS_M,
|
||||
sources: GOOGLE ? [GOOGLE, OUTDOOR] : [OUTDOOR],
|
||||
// Both: Google's own imagery, outdoors (sources intersect).
|
||||
sources: [GOOGLE, OUTDOOR],
|
||||
preference: lib.StreetViewPreference.NEAREST,
|
||||
});
|
||||
return data;
|
||||
@@ -163,10 +181,12 @@ export function createGoogleProvider({
|
||||
const panoId = data?.location?.pano;
|
||||
if (!panoId) return null;
|
||||
const passes = passesImageryFilter(
|
||||
{ isPano: true, capturedAt: imageDateMs(data.imageDate) ?? 0 },
|
||||
{ isPano: true, capturedAt: imageMonthEndMs(data.imageDate) ?? 0 },
|
||||
filter,
|
||||
);
|
||||
return passes ? panoId : null;
|
||||
if (!passes) return null;
|
||||
viewer.remember(panoId, data);
|
||||
return panoId;
|
||||
},
|
||||
|
||||
viewer,
|
||||
|
||||
Reference in new issue
Block a user