mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Main gained three imagery providers (Google Street View, KartaView,
Panoramax), shared line-coverage, cone and photo-viewer modules, one source
lit at a time and the translated interface. Their shared modules still
carried what this branch removes, so the merge ports them onto it:
- lineCoverage.js is this branch's coverage.js made provider-neutral
(draped lines near the ground only, the selection drawn over them, key
and rate-limit blocking); sequenceCones.js is sequences.js the same way.
Mapillary, KartaView and Panoramax all build on them; Mapillary moves
back to providers/mapillary/ beside the others.
- No ground casting, mesh sampling, surface modes, FOLLOW, legend, prewarm,
orbit overview points (Panoramax's density dots included), stale-tile
swaps or water-filled point budget come back.
- Kept from main because the new providers need them: requiresMapStack and
attachMapStackController (Street View on Google 3D only), ground clicks,
openNearest with an optional provider nearestImage, openImage {frame},
reportError, the view-cone marker (now an SVG glyph), filters /
coverageLabel / sinceBasis, OPEN STREET VIEW, keyAlternatives and i18n.
- photoViewer.js treats the viewer as expanded when its wrap is full screen
or the iPhone popover, as EXPAND now is.
- The fixture browser gate keeps its 8 steps and adds one each for
KartaView and Panoramax; catalogs, docs, boundaries and format scope
follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
139 lines
4.5 KiB
JavaScript
139 lines
4.5 KiB
JavaScript
// Panoramax stand-ins for tests and the hermetic QA gate: vector tiles in
|
|
// the meta-catalogue's shape, and STAC items.
|
|
import { PbfWriter } from 'pbf';
|
|
import {
|
|
layerWriter,
|
|
lineGeometry,
|
|
zigzag,
|
|
} from '../layers/streetLevel/providers/mapillary/coverageFixture.mjs';
|
|
|
|
/** Tile-local [x, y] (0..extent) of a lon/lat inside tile (x, y, z). */
|
|
function lonLatToTileLocal([lon, lat], { x, y, z }, extent = 4096) {
|
|
const n = 2 ** z;
|
|
const rad = (lat * Math.PI) / 180;
|
|
const tx = ((lon + 180) / 360) * n;
|
|
const ty =
|
|
((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2) * n;
|
|
return [Math.round((tx - x) * extent), Math.round((ty - y) * extent)];
|
|
}
|
|
|
|
/**
|
|
* Encode a Panoramax `/api/map/{z}/{x}/{y}.mvt` tile from lon/lat features.
|
|
* @param {{x: number, y: number, z: number}} tile
|
|
* @param {{sequences?: Array<{id: string, date?: string, type?: string, parts: Array<Array<[number, number]>>}>, grid?: Array<{lon: number, lat: number, pano?: number, flat?: number}>, pictures?: Array<{id: string, lon: number, lat: number, sequenceId: string, heading?: number, ts?: string, type?: string}>, extent?: number}} [content]
|
|
*/
|
|
export function encodePanoramaxTile(
|
|
tile,
|
|
{ sequences = [], grid = [], pictures = [], extent = 4096 } = {},
|
|
) {
|
|
const pbf = new PbfWriter();
|
|
if (sequences.length) {
|
|
const layer = layerWriter('sequences', extent);
|
|
for (const sequence of sequences)
|
|
layer.add(
|
|
2,
|
|
lineGeometry(
|
|
sequence.parts.map((part) =>
|
|
part.map((point) => lonLatToTileLocal(point, tile, extent)),
|
|
),
|
|
),
|
|
{
|
|
id: sequence.id,
|
|
type: sequence.type ?? 'flat',
|
|
date: sequence.date ?? '2025-06-01',
|
|
model: 'Test camera',
|
|
},
|
|
);
|
|
pbf.writeMessage(3, (_, out) => layer.write(out), null);
|
|
}
|
|
const point = (lon, lat) => {
|
|
const [px, py] = lonLatToTileLocal([lon, lat], tile, extent);
|
|
return [9, zigzag(px), zigzag(py)];
|
|
};
|
|
if (grid.length) {
|
|
const layer = layerWriter('grid', extent);
|
|
for (const cell of grid) {
|
|
const pano = cell.pano ?? 0;
|
|
const flat = cell.flat ?? 1;
|
|
layer.add(1, point(cell.lon, cell.lat), {
|
|
nb_pictures: pano + flat,
|
|
nb_360_pictures: pano,
|
|
nb_flat_pictures: flat,
|
|
});
|
|
}
|
|
pbf.writeMessage(3, (_, out) => layer.write(out), null);
|
|
}
|
|
if (pictures.length) {
|
|
const layer = layerWriter('pictures', extent);
|
|
for (const picture of pictures)
|
|
layer.add(1, point(picture.lon, picture.lat), {
|
|
id: picture.id,
|
|
ts: picture.ts ?? '2025-06-01 10:00:00+00',
|
|
heading: picture.heading ?? 90,
|
|
sequences: JSON.stringify([picture.sequenceId]),
|
|
first_sequence: picture.sequenceId,
|
|
type: picture.type ?? 'flat',
|
|
});
|
|
pbf.writeMessage(3, (_, out) => layer.write(out), null);
|
|
}
|
|
return new Uint8Array(pbf.finish());
|
|
}
|
|
|
|
/**
|
|
* A STAC item as the meta-catalogue returns it.
|
|
* @param {{id: string, lon: number, lat: number, sequenceId?: string, heading?: number, pano?: boolean, datetime?: string, producer?: string, license?: string, width?: number, prevId?: string|null, nextId?: string|null, host?: string}} options
|
|
*/
|
|
export function stacItem({
|
|
id,
|
|
lon,
|
|
lat,
|
|
sequenceId = 'seq-1',
|
|
heading = 90,
|
|
pano = false,
|
|
datetime = '2025-06-01T10:00:00+00:00',
|
|
producer = 'tester',
|
|
license = 'CC-BY-SA-4.0',
|
|
width = pano ? 5760 : 4032,
|
|
prevId = null,
|
|
nextId = null,
|
|
host = 'https://panoramax.example.org',
|
|
}) {
|
|
const api = 'https://api.panoramax.xyz/api';
|
|
const links = [
|
|
{ rel: 'self', href: `${api}/collections/${sequenceId}/items/${id}` },
|
|
];
|
|
if (prevId)
|
|
links.push({
|
|
rel: 'prev',
|
|
href: `${api}/collections/${sequenceId}/items/${prevId}`,
|
|
});
|
|
if (nextId)
|
|
links.push({
|
|
rel: 'next',
|
|
href: `${api}/collections/${sequenceId}/items/${nextId}`,
|
|
});
|
|
return {
|
|
type: 'Feature',
|
|
id,
|
|
collection: sequenceId,
|
|
geometry: { type: 'Point', coordinates: [lon, lat] },
|
|
providers: [{ name: producer, roles: ['producer'] }],
|
|
properties: {
|
|
datetime,
|
|
license,
|
|
'view:azimuth': heading,
|
|
'geovisio:producer': producer,
|
|
'pers:interior_orientation': {
|
|
...(pano ? { field_of_view: 360 } : {}),
|
|
sensor_array_dimensions: [width, pano ? width / 2 : 3024],
|
|
},
|
|
},
|
|
assets: {
|
|
hd: { href: `${host}/pictures/${id}/hd.jpg` },
|
|
sd: { href: `${host}/pictures/${id}/sd.jpg` },
|
|
thumb: { href: `${host}/pictures/${id}/thumb.jpg` },
|
|
},
|
|
links,
|
|
};
|
|
}
|