mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
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]>
44 lines
1.5 KiB
JavaScript
44 lines
1.5 KiB
JavaScript
import { createSequenceCones, thinImages } from '../../sequenceCones.js';
|
|
import { COLORS, PICK_PREFIX } from './policy.js';
|
|
|
|
export { thinImages } from '../../sequenceCones.js';
|
|
|
|
/** Normalize a graph image record into the shape the cones use. */
|
|
export function normalizeSequenceImage(record) {
|
|
const coordinates = record?.geometry?.coordinates;
|
|
if (!Array.isArray(coordinates) || coordinates.length < 2) return null;
|
|
return {
|
|
id: String(record.id),
|
|
lon: Number(coordinates[0]),
|
|
lat: Number(coordinates[1]),
|
|
compassAngle: Number(record.compass_angle) || 0,
|
|
capturedAt: Number(record.captured_at) || 0,
|
|
isPano: record.is_pano === true,
|
|
altitude: Number.isFinite(record.computed_altitude)
|
|
? record.computed_altitude
|
|
: null,
|
|
};
|
|
}
|
|
|
|
/** Image cones for the selected Mapillary sequence, its line highlighted. */
|
|
export function createSequences({ state, source, parts }) {
|
|
return createSequenceCones({
|
|
state,
|
|
async loadImages(sequenceId, { signal }) {
|
|
const records = await source.getSequenceImages(sequenceId, { signal });
|
|
return thinImages(
|
|
records
|
|
.map(normalizeSequenceImage)
|
|
.filter(Boolean)
|
|
.sort((a, b) => a.capturedAt - b.capturedAt),
|
|
);
|
|
},
|
|
colors: COLORS,
|
|
pickPrefix: PICK_PREFIX.image,
|
|
spriteId: 'street-level:mapillary-cones',
|
|
renderKey: 'mapillary-sequence',
|
|
onHighlight: (sequenceId, selected) =>
|
|
parts.coverage.recolorSequence(sequenceId, selected),
|
|
});
|
|
}
|