mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-10 21:32:23 +00:00
fix(street-level): sequence loads, playback and nearest-photo edge cases
From gekh's review of #768, each bug with a test that fails before: - P2: a failed sequence load clears the previous sequence's cones, resets the selection and can be retried; a second click while loading starts no second lookup. sequences.js had no tests: sequences.test.mjs now covers failure and retry, a superseded late answer, the cache and clearSelection. - P2: closing a photo stops MapillaryJS sequence playback, which kept the hidden viewer stepping and downloading images. - openNearest without the panel reports it instead of loading forever, and stops if the layer (or its provider) went off during the lookup; the mesh sampler's misses are bounded and forgotten with its samples; the sequence count is the union of ids across tiles; a provider withdraws only its own error, so a sequence error cannot mask KEY REJECTED; hover stays on (throttled) under an orbiting camera and re-picks when it stops; "since N days" follows the clock in a long-open tab. - A prewarm waits for one in flight and then builds if it gave up. - New coverage for the layer core (credits, KEY REJECTED vs REQUIRED, switching a provider off), the refresh wiring of the tilted-view fix and the draped-to-cast swap completion. - Cleanup: loading is derived from pending; freshStreet(); tile ranking computes distances once and shares one path for the date line; shared metresBetween / whenIdle / redrawTile; one NEAREST_RADIUS_M and one source method list; unused contract fields, exports, getImage and the decoder's image branch removed; PROVIDER_COLORS lists only Mapillary. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
48276450ec
commit
5f98b871e8
31 files changed
+1129
-470
No files matched your search
@@ -8,15 +8,12 @@
|
||||
* @property {string|null} requiresKeyId Key-setup id the provider needs, or null when keyless.
|
||||
* @property {string} pickPrefix Every primitive id the provider creates starts with it.
|
||||
* @property {{coverage: string}} colors The source's one colour (policy.js PROVIDER_COLORS): chip, lines, points, cones and legend.
|
||||
* @property {{key: string, html: string}} credit On-globe attribution while the provider is active.
|
||||
* @property {{coverage: 'tiles'|'bbox'|'none', sequences: boolean, pano: boolean, capturedAt: boolean, creator: boolean, follow: boolean}} capabilities
|
||||
* @property {(imageId: string) => string} externalUrl Deep link to the image on the provider's site.
|
||||
* @property {{html: string}} credit On-globe attribution while the provider is active.
|
||||
* @property {(context: ProviderContext) => ProviderInstance} create
|
||||
*
|
||||
* @typedef {object} ProviderContext Handed to `create()` once by the core.
|
||||
* @property {object} services Scene services: picking, input, render, sprites, ground, terrain.
|
||||
* @property {() => object|null} getViewer The Cesium viewer once the layer is initialised.
|
||||
* @property {() => {pano: string, sinceMs: number|null}} getFilter The resolved imagery filter.
|
||||
* @property {() => {pano: string, sinceMs: number|null}} getFilter The imagery filter resolved against now: read it whenever filtering, so a "since N days" window keeps up with the clock.
|
||||
* @property {() => boolean} isActive Layer enabled and this provider switched on.
|
||||
* @property {() => 'draped'|'terrain'} getSurface 'terrain' on Google 3D at street zoom: draw on the bare earth, not the mesh top.
|
||||
* @property {GroundCaster|null} groundCaster Bare-earth heights for terrain mode (groundCast.js); null without a terrain service.
|
||||
@@ -31,7 +28,7 @@
|
||||
* @property {() => void} deactivate Detach, clear coverage and selection, hide.
|
||||
* @property {(viewer: object) => void} destroy
|
||||
* @property {() => void} refreshCoverage
|
||||
* @property {(filter: {pano: string, sinceMs: number|null}) => void} setFilter
|
||||
* @property {(filter: {pano: string, sinceMs: number|null}) => void} setFilter The filter changed: redraw (reading `getFilter()`).
|
||||
* @property {(mode: 'draped'|'terrain') => void} [setSurface] Redraw coverage and cones for the new surface mode.
|
||||
* @property {() => {count: number, zoom: number|null, kind: string|null, loading: boolean, hint: string, error: string|null, keyRequired: boolean}} coverageStats
|
||||
* @property {(pickId: string) => boolean} handlePick The id carries the provider's own prefix.
|
||||
@@ -45,7 +42,6 @@
|
||||
* @property {(points: Array<[number, number]>, options?: {signal?: AbortSignal}) => Promise<boolean>} prepare Fetch the heights around [lon, lat] points; true when all are known.
|
||||
* @property {(lines: Array<Array<[number, number]>>, options?: {signal?: AbortSignal}) => Promise<boolean>} prepareLines
|
||||
* @property {(lon: number, lat: number) => number|null} groundAt Cached bare-earth ellipsoidal height.
|
||||
* @property {(lon: number, lat: number) => number|null} heightAt groundAt plus the overlay lift.
|
||||
* @property {(coords: Array<[number, number]>) => Array<number>|null} castLine Densified [lon, lat, height, ...] or null.
|
||||
*
|
||||
* @typedef {object} ViewerAdapter
|
||||
@@ -69,7 +65,7 @@
|
||||
* @property {number|null} capturedAt Epoch milliseconds.
|
||||
* @property {string|null} creator
|
||||
* @property {string|null} sequenceId
|
||||
* @property {string} externalUrl
|
||||
* @property {string} externalUrl Deep link to the image on the provider's site.
|
||||
*/
|
||||
|
||||
const REQUIRED = Object.freeze([
|
||||
@@ -79,8 +75,6 @@ const REQUIRED = Object.freeze([
|
||||
'pickPrefix',
|
||||
'colors',
|
||||
'credit',
|
||||
'capabilities',
|
||||
'externalUrl',
|
||||
'create',
|
||||
]);
|
||||
|
||||
@@ -110,10 +104,6 @@ export function validateProviders(providers) {
|
||||
throw new TypeError(
|
||||
`Street Level provider ${label}: create is not a function`,
|
||||
);
|
||||
if (typeof provider.externalUrl !== 'function')
|
||||
throw new TypeError(
|
||||
`Street Level provider ${label}: externalUrl is not a function`,
|
||||
);
|
||||
if (typeof provider.pickPrefix !== 'string' || !provider.pickPrefix)
|
||||
throw new TypeError(`Street Level provider ${label} needs a pick prefix`);
|
||||
for (const other of prefixes)
|
||||
@@ -124,7 +114,7 @@ export function validateProviders(providers) {
|
||||
throw new TypeError(
|
||||
`Street Level provider ${label}: pick prefix ${provider.pickPrefix} overlaps ${other}`,
|
||||
);
|
||||
if (!provider.credit?.key || !provider.credit?.html)
|
||||
if (!provider.credit?.html)
|
||||
throw new TypeError(`Street Level provider ${label} needs a credit`);
|
||||
if (
|
||||
typeof provider.colors?.coverage !== 'string' ||
|
||||
|
||||
Reference in new issue
Block a user