mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
feat(street-level): KartaView as a third, keyless imagery provider
Coverage lines from KartaView's z13-14 JSON tiles (OSM ways with photos, chained end to start), the photo nearest a clicked line, cones along its sequence, and a viewer for flat photos and 360° panoramas (a small WebGL equirectangular renderer, no new dependency) with ‹ › along the sequence. Mapillary's camera-driven line coverage and sequence cones move to the provider-neutral core (lineCoverage.js, sequenceCones.js) with the tile fetch, zoom ladder, colours and pick prefixes injected; Mapillary keeps thin wrappers and its tests pass unchanged. KartaView quirks handled: api.kartaview.org (the documented api.openstreetcam.org serves empty tiles), photo URLs that name storage1 for photos stored elsewhere, and a photo API that answers 429 to more than about three requests at once (two in flight, retried with backoff). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
9beadbda63
commit
cb01014e36
33 files changed
+4046
-1196
No files matched your search
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Decode a KartaView coverage tile (`{lines: [{track: [{lat, lng}]}]}`) into
|
||||
* line records for the shared coverage. The tile's lines are OSM way
|
||||
* segments with photos; segments that meet end to start are chained, which
|
||||
* cuts the line count about fourfold. Lines carry no sequence, date or
|
||||
* projection, so nothing can filter them.
|
||||
* @param {object} payload
|
||||
* @param {{x: number, y: number, z: number}} address
|
||||
* @returns {{sequences: Array<{id: string, parts: Array<Array<[number, number]>>, capturedAt: number, isPano: boolean}>, overview: Array<never>}}
|
||||
*/
|
||||
export function decodeCoverageTile(payload, { x, y, z }) {
|
||||
const segments = [];
|
||||
for (const line of Array.isArray(payload?.lines) ? payload.lines : []) {
|
||||
const points = [];
|
||||
for (const point of Array.isArray(line?.track) ? line.track : []) {
|
||||
const lon = Number(point?.lng);
|
||||
const lat = Number(point?.lat);
|
||||
if (!Number.isFinite(lon) || !Number.isFinite(lat)) continue;
|
||||
const last = points[points.length - 1];
|
||||
if (last && last[0] === lon && last[1] === lat) continue;
|
||||
points.push([lon, lat]);
|
||||
}
|
||||
if (points.length >= 2) segments.push(points);
|
||||
}
|
||||
const key = ([lon, lat]) => `${lon},${lat}`;
|
||||
const startingAt = new Map();
|
||||
segments.forEach((segment, index) => {
|
||||
const at = key(segment[0]);
|
||||
const list = startingAt.get(at);
|
||||
if (list) list.push(index);
|
||||
else startingAt.set(at, [index]);
|
||||
});
|
||||
const used = new Uint8Array(segments.length);
|
||||
const sequences = [];
|
||||
segments.forEach((segment, index) => {
|
||||
if (used[index]) return;
|
||||
used[index] = 1;
|
||||
const chain = [...segment];
|
||||
for (;;) {
|
||||
const next = startingAt
|
||||
.get(key(chain[chain.length - 1]))
|
||||
?.find((candidate) => !used[candidate]);
|
||||
if (next === undefined) break;
|
||||
used[next] = 1;
|
||||
chain.push(...segments[next].slice(1));
|
||||
}
|
||||
sequences.push({
|
||||
id: `${z}/${x}/${y}/${sequences.length}`,
|
||||
parts: [chain],
|
||||
capturedAt: 0,
|
||||
isPano: false,
|
||||
});
|
||||
});
|
||||
return { sequences, overview: [] };
|
||||
}
|
||||
Reference in new issue
Block a user