mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +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,60 @@
|
||||
// DOM stand-ins for the KartaView viewer's tests (no browser in Node).
|
||||
|
||||
/** A DOM element with what the viewer touches: children, events, attributes. */
|
||||
export function fakeElement(tag) {
|
||||
const listeners = new Map();
|
||||
const element = {
|
||||
tagName: tag.toUpperCase(),
|
||||
children: [],
|
||||
parentNode: null,
|
||||
style: {},
|
||||
dataset: {},
|
||||
attributes: {},
|
||||
hidden: false,
|
||||
disabled: false,
|
||||
clientWidth: 400,
|
||||
clientHeight: 250,
|
||||
classList: {
|
||||
values: new Set(),
|
||||
add: (name) => element.classList.values.add(name),
|
||||
remove: (name) => element.classList.values.delete(name),
|
||||
},
|
||||
append(...nodes) {
|
||||
for (const node of nodes) {
|
||||
node.parentNode = element;
|
||||
element.children.push(node);
|
||||
}
|
||||
},
|
||||
remove() {
|
||||
const list = element.parentNode?.children;
|
||||
if (list) list.splice(list.indexOf(element), 1);
|
||||
element.parentNode = null;
|
||||
},
|
||||
setAttribute: (name, value) => (element.attributes[name] = value),
|
||||
removeAttribute: (name) => {
|
||||
delete element.attributes[name];
|
||||
if (name === 'src') element.src = '';
|
||||
},
|
||||
addEventListener: (type, handler) => listeners.set(type, handler),
|
||||
removeEventListener: (type) => listeners.delete(type),
|
||||
setPointerCapture() {},
|
||||
/** Fire an event at the element's listener. */
|
||||
fire(type, event = {}) {
|
||||
return listeners.get(type)?.({
|
||||
preventDefault() {
|
||||
this.defaultPrevented = true;
|
||||
},
|
||||
stopPropagation() {},
|
||||
...event,
|
||||
});
|
||||
},
|
||||
};
|
||||
return element;
|
||||
}
|
||||
|
||||
export function fakeHost() {
|
||||
const doc = { createElement: (tag) => fakeElement(tag) };
|
||||
const host = fakeElement('div');
|
||||
host.ownerDocument = doc;
|
||||
return host;
|
||||
}
|
||||
Reference in new issue
Block a user