mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
refactor(street-level): drop hover, legend and byte-budget cache; flatten the provider directory
- No pointer cursor over coverage lines (no other layer has one); a click test replaces the hover tests. - No legend: one source, one colour. - The on-globe credit lives in index.js; credits.js is gone. - Billboard glyphs are SVG data URLs with the same geometry, so tests need no canvas stub. - The tile cache keeps the 120 most recently used tiles, same 24 h TTL, instead of a byte budget; listTileLayers was test-only. - providers/mapillary/ is flattened into the layer and its policy merged into one policy.js; the source's method list is a literal in the catalog. - One fakeCesiumViewer for the layer tests. - The fixture gate keeps the steps the unit tests do not cover: coverage and credit, a rejected key, line -> cone -> photo, close, a keyless page. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
34d43c7e3f
commit
e214129e45
41 files changed
+335
-909
No files matched your search
@@ -18,9 +18,8 @@ const FETCH_TIMEOUT_MS = 60_000;
|
||||
export const TILE_UPSTREAM_CONCURRENCY = 6;
|
||||
/** Tiles change only when new imagery is processed. */
|
||||
export const TILE_TTL_MS = 24 * 60 * 60 * 1000;
|
||||
export const TILE_MEMORY_BUDGET_BYTES = 96 * 1024 * 1024;
|
||||
/** Per-entry cost on top of its bytes, so empty tiles still count. */
|
||||
const ENTRY_OVERHEAD_BYTES = 1024;
|
||||
/** Trimmed tiles are ~80 KB: a dozen 9-tile views, well under 100 MB. */
|
||||
export const TILE_MEMORY_MAX_TILES = 120;
|
||||
|
||||
/** The client token lives in the browser by design; the server adds it to tile URLs too. */
|
||||
export function mapillaryToken() {
|
||||
@@ -139,39 +138,30 @@ function unlessAborted(promise, signal) {
|
||||
});
|
||||
}
|
||||
|
||||
/** @type {Map<string, {bytes: Buffer, at: number}>} insertion-ordered LRU */
|
||||
/** @type {Map<string, {bytes: Buffer, at: number}>} least recently used first */
|
||||
const memory = new Map();
|
||||
let memoryBytes = 0;
|
||||
/**
|
||||
* Upstream fetches shared per tile, with the requests still waiting on each.
|
||||
* @type {Map<string, {promise: Promise<Buffer>, controller: AbortController, waiters: number}>}
|
||||
*/
|
||||
const inFlight = new Map();
|
||||
const cost = (bytes) => bytes.length + ENTRY_OVERHEAD_BYTES;
|
||||
|
||||
function forget(key) {
|
||||
const hit = memory.get(key);
|
||||
if (!hit) return null;
|
||||
memory.delete(key);
|
||||
memoryBytes -= cost(hit.bytes);
|
||||
return hit;
|
||||
}
|
||||
|
||||
function remember(key, bytes, at = Date.now()) {
|
||||
// One huge (untrimmed) tile must not flush everything else.
|
||||
if (cost(bytes) > TILE_MEMORY_BUDGET_BYTES / 2) return;
|
||||
forget(key);
|
||||
memory.delete(key);
|
||||
memory.set(key, { bytes, at });
|
||||
memoryBytes += cost(bytes);
|
||||
for (const [oldest] of memory) {
|
||||
if (memoryBytes <= TILE_MEMORY_BUDGET_BYTES) break;
|
||||
forget(oldest);
|
||||
for (const oldest of memory.keys()) {
|
||||
if (memory.size <= TILE_MEMORY_MAX_TILES) break;
|
||||
memory.delete(oldest);
|
||||
}
|
||||
}
|
||||
|
||||
function recall(key) {
|
||||
const hit = forget(key);
|
||||
if (!hit || Date.now() - hit.at > TILE_TTL_MS) return null;
|
||||
const hit = memory.get(key);
|
||||
if (!hit) return null;
|
||||
if (Date.now() - hit.at > TILE_TTL_MS) {
|
||||
memory.delete(key);
|
||||
return null;
|
||||
}
|
||||
remember(key, hit.bytes, hit.at); // most recently used again
|
||||
return hit.bytes;
|
||||
}
|
||||
@@ -225,11 +215,5 @@ export async function fetchTile(address, { signal } = {}) {
|
||||
/** Test seam: forget every cached and in-flight tile. */
|
||||
export function _resetTileCacheForTest() {
|
||||
memory.clear();
|
||||
memoryBytes = 0;
|
||||
inFlight.clear();
|
||||
}
|
||||
|
||||
/** Test seam: how many tiles memory holds and the bytes charged for them. */
|
||||
export function _tileMemoryForTest() {
|
||||
return { entries: memory.size, bytes: memoryBytes };
|
||||
}
|
||||
@@ -41,16 +41,3 @@ export function stripTileLayers(bytes, dropNames) {
|
||||
}, null);
|
||||
return dropped ? Buffer.from(writer.finish()) : bytes;
|
||||
}
|
||||
|
||||
/** Names of the layers inside a vector tile, in order. */
|
||||
export function listTileLayers(bytes) {
|
||||
const names = [];
|
||||
if (!bytes?.length) return names;
|
||||
new PbfReader(bytes).readFields((tag, _result, pbf) => {
|
||||
if (tag !== TILE_LAYER_FIELD) return;
|
||||
const end = pbf.readVarint() + pbf.pos;
|
||||
names.push(layerName(bytes.subarray(pbf.pos, end)));
|
||||
pbf.pos = end;
|
||||
}, null);
|
||||
return names;
|
||||
}
|
||||
Reference in new issue
Block a user