mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Street Level: Panoramax review follow-ups (gekh, #1134)
Image loading (photoViewer.js) - One AbortController per open: a new open, close or unmount stops the previous photo's first and sharper downloads (an <img> by src = ''). A collapsed viewer never falls back to the original; Panoramax's flat fallback is the thumbnail. A superseded open resolves quietly. - Flat HD tracked as "loading" and "shown" apart: a discarded response no longer blocks later upgrades, and resizes while it loads ask once. - No upgrade of the photo still shown while another opens. - Size limits before decoding: imageSize.js reads JPEG/PNG/WebP/GIF headers; more than 64 MP or 16,384 px a side is refused undecoded, an <img> that decodes larger is refused and let go, flat HD needs a known width of at most 8,192 px, and panoramas always decode with a resize. Panoramax provider - Retry-After parsed as seconds or an HTTP date; the browser cannot read it here, so busy answers back off 5 s doubling to 300 s, reset once answered. - Headers timed, then the body by an idle timeout (15 s of silence), so a slow link still gets its megabyte tiles. - Cones centre only on the picture shown (onShowing), never on a late lookup; lookups nobody waits for are aborted (shared, refcounted). - onReopen: a GPU reset redraws in the viewer, not through the core. - Cones draw the home z15 tile at once and add neighbours as they arrive; a neighbour that failed is not counted as drawn. - Coverage tiles decode in ~8 ms slices (a 2 MB z10 tile over Paris was one ~85-200 ms task); sorted by measured length without a Map. - Box search asks for 20, not 100 (the API returns them nearest first). - looksLikeEmail checks every '@'; creatorOf de-duplicates with a Set. lineCoverage.js - A redraw while a swap still builds keeps what is on screen (the swap's old lines and its ready new ones) and drops only the unready ones. Tests for each (mutation-checked), two vacuous tests made real, the cones' pictures lane and readable()'s busy/timeout paths covered; the fixture's box search is nearest first. Docs: README filters and share links, CHANGELOG HD wording, CURRENT-STATE. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e3bc431f9a
commit
b645a767c8
25 files changed
+1362
-135
No files matched your search
@@ -66,6 +66,13 @@ export const COVERAGE_TUNING = Object.freeze({
|
||||
* within GPS noise), and 6–10% of the points GPS tracks carry.
|
||||
*/
|
||||
export const SIMPLIFY_TOLERANCE_UNITS = 4;
|
||||
|
||||
/**
|
||||
* Coverage tiles decode in slices of this many ms, the page getting a turn
|
||||
* between them (decode.js `decodeCoverageTileSliced`): under a frame.
|
||||
*/
|
||||
export const DECODE_SLICE_MS = 8;
|
||||
|
||||
/**
|
||||
* Grid cells per overview tile, at most: a z0 tile holds ~70,000 cells
|
||||
* (1.9 MB), drawn as this many binned points.
|
||||
@@ -89,6 +96,14 @@ export const PICTURE_TILE_ZOOM = 15;
|
||||
export const PICTURE_TILE_EDGE_M = 150;
|
||||
|
||||
export const TILE_TIMEOUT_MS = 20_000;
|
||||
/**
|
||||
* Once an answer has started, it may take as long as it keeps coming: a
|
||||
* 2 MB tile on a slow link outlasts any whole-request timeout, so only a
|
||||
* stream silent this long is given up.
|
||||
*/
|
||||
export const STREAM_IDLE_TIMEOUT_MS = 15_000;
|
||||
/** Busy (429/503) without a Retry-After the browser can read: back off from this. */
|
||||
export const BUSY_BACKOFF_BASE_SEC = 5;
|
||||
export const API_TIMEOUT_MS = 12_000;
|
||||
/** Real tiles are under 6 MB (z6 over France); refuse anything absurd. */
|
||||
export const MAX_RESPONSE_BYTES = 16 * 1024 * 1024;
|
||||
@@ -111,8 +126,12 @@ export const NEAREST_LIMIT = 10;
|
||||
* beside it has those).
|
||||
*/
|
||||
export const NEAREST_FOV_TOLERANCE_DEG = 180;
|
||||
/** Pictures in the bounding box around a click, at most. */
|
||||
export const NEAREST_BOX_LIMIT = 100;
|
||||
/**
|
||||
* Pictures in the bounding box around a click, at most. The API returns
|
||||
* them roughly nearest first (the first ten of 100 are the first ten of
|
||||
* 10), and the browser ranks them exactly: 20 is ~60 kB, 100 ~600 kB.
|
||||
*/
|
||||
export const NEAREST_BOX_LIMIT = 20;
|
||||
/**
|
||||
* A line clicked from high up may be tens of metres off the click: its
|
||||
* sequence is searched within the camera height / 100 (50–400 m).
|
||||
@@ -194,10 +213,12 @@ function linkedId(item, rel) {
|
||||
}
|
||||
}
|
||||
|
||||
/** An email address anywhere in a name (linear: names come from any instance). */
|
||||
/**
|
||||
* An email address anywhere in a name, at any of its '@'s. Linear: names
|
||||
* come from any instance, and each run between '@'s is scanned once.
|
||||
*/
|
||||
function looksLikeEmail(name) {
|
||||
const at = name.indexOf('@');
|
||||
return at > 0 && name.indexOf('.', at + 2) !== -1;
|
||||
return /[^\s@]@[^\s@]+\.[^\s@]/.test(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,12 +238,14 @@ function creatorOf(item) {
|
||||
// Every producer is credited (an uploading app and its photographer),
|
||||
// each name once, none that is an email address.
|
||||
const credited = [];
|
||||
const seen = new Set();
|
||||
for (const value of names) {
|
||||
if (typeof value !== 'string') continue;
|
||||
const name = value.trim().slice(0, 80);
|
||||
if (!name || looksLikeEmail(value)) continue;
|
||||
if (credited.some((other) => other.toLowerCase() === name.toLowerCase()))
|
||||
continue;
|
||||
const key = name.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
credited.push(name);
|
||||
}
|
||||
return credited.length ? credited.join(', ').slice(0, 120) : null;
|
||||
|
||||
Reference in new issue
Block a user