mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Camera-driven Mapillary coverage (z0–5 overview points from orbit, z11–14 ground-clamped sequence lines below 60 km), per-sequence image cones, an embedded MapillaryJS viewer bridge with a camera-follow mode, sprite icons for map features, and a query engine that turns a plan into a streamed z14 tile fan-out. Registers an on-globe CC BY-SA 4.0 credit while enabled. Co-Authored-By: Claude Fable 5.1 <[email protected]>
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
import { PbfReader } from 'pbf';
|
|
import { VectorTile } from '@mapbox/vector-tile';
|
|
import { tileLocalToLonLat } from '../../../src/layers/mapillary/tileMath.js';
|
|
|
|
/** Vector-tile layer name inside each Mapillary map-feature tile. */
|
|
const FEATURE_LAYER_NAMES = Object.freeze({
|
|
points: 'point',
|
|
signs: 'traffic_sign',
|
|
});
|
|
|
|
/**
|
|
* Decode one map-feature tile into compact rows, keeping only features whose
|
|
* value passes `matcher` and whose sighting window overlaps [since, until].
|
|
* Rows are positional arrays to keep the streamed payload small:
|
|
* [lon, lat, id, value, firstSeenMs, lastSeenMs].
|
|
* @param {Buffer|Uint8Array} bytes
|
|
* @param {{x:number,y:number,z:number,layer:'points'|'signs'}} address
|
|
* @param {{matcher?: (value:string)=>boolean, since?: number|null, until?: number|null}} [filter]
|
|
* @returns {Array<[number, number, string, string, number, number]>}
|
|
*/
|
|
export function decodeFeatureRows(bytes, address, filter = {}) {
|
|
if (!bytes || !bytes.length) return [];
|
|
const layerName = FEATURE_LAYER_NAMES[address.layer];
|
|
const tile = new VectorTile(new PbfReader(bytes));
|
|
const layer = layerName ? tile.layers[layerName] : null;
|
|
if (!layer) return [];
|
|
const matcher = filter.matcher || (() => true);
|
|
const since = Number.isFinite(filter.since) ? filter.since : null;
|
|
const until = Number.isFinite(filter.until) ? filter.until : null;
|
|
const rows = [];
|
|
for (let i = 0; i < layer.length; i++) {
|
|
const feature = layer.feature(i);
|
|
const value = feature.properties?.value;
|
|
if (typeof value !== 'string' || !matcher(value)) continue;
|
|
const first = Number(feature.properties.first_seen_at) || 0;
|
|
const last = Number(feature.properties.last_seen_at) || first;
|
|
if (since !== null && last < since) continue;
|
|
if (until !== null && first > until) continue;
|
|
const geometry = feature.loadGeometry();
|
|
const point = geometry?.[0]?.[0];
|
|
if (!point) continue;
|
|
const [lon, lat] = tileLocalToLonLat(
|
|
point.x,
|
|
point.y,
|
|
layer.extent,
|
|
address.x,
|
|
address.y,
|
|
address.z,
|
|
);
|
|
rows.push([
|
|
Number(lon.toFixed(6)),
|
|
Number(lat.toFixed(6)),
|
|
String(feature.properties.id ?? feature.id ?? ''),
|
|
value,
|
|
first,
|
|
last,
|
|
]);
|
|
}
|
|
return rows;
|
|
}
|