Files
gods-eye-view/src/sources/openFreeMap.js
T
Sameh Khamis 73e789489b chore: drop exports nothing imports
Knip, configured with the application, package exports, scripts, QA
harnesses, workers, tools and tests as entry points, reports exports
that no module imports. Each was checked by hand against string-based
and namespace imports.

- Symbols that are still used inside their own module lose only the
  `export` keyword (176 constants and helpers).
- Unused re-exports are removed from barrel modules (flight, military,
  vessel, CCTV, directions, transit history, local ADS-B and street-level
  policy modules).
- Declarations that nothing uses at all are deleted, among them
  AIS_WATCHDOG_STATUSES, applyFilter, isRadioCountryCode, compactText,
  DENSITY_STOPS, the unused CCTV endpoint constants, and the SDR band
  constants.
- Default exports that nothing imports are removed. The ones in
  src/data/earthquakes.js, src/data/telegeographySubmarineCables.js,
  src/data/wind.js and src/data/bhoteKoshiLocator.js each constructed a
  layer instance at import time that nothing used; those instances are
  no longer created.

Every export of a package.json entry stays, as does TOMTOM_CREDIT,
which the traffic layer reads from the credits namespace it is given.
2026-10-22 19:00:40 -07:00

240 lines
7.6 KiB
JavaScript

import { PbfReader } from 'pbf';
import { VectorTile } from '@mapbox/vector-tile';
import { createVectorTileSource } from './vectorTiles.js';
import { clipTileRing, militaryOutlineLines } from './militaryTileGeometry.js';
import { tileToBBox } from '../data/tomtomTiles.js';
// Kept English in data; the map card and Contacts translate it.
import { UNNAMED_MILITARY_AREA } from '../data/installationNames.js';
/** Name of a military polygon the names pack has not named. */
export { UNNAMED_MILITARY_AREA };
const ROAD_TYPES = Object.freeze({
motorway: 'motorway',
trunk: 'trunk',
primary: 'primary',
secondary: 'secondary',
tertiary: 'tertiary',
minor: 'residential',
});
/**
* Surface traffic uses public motor-road classes only. Untyped service ways
* include car parks and grounds access, so none are used as through traffic.
* OpenMapTiles folds private access into `no`. Tunnels cannot be placed on
* the surface above them; ramps/bridges remain valid only on a motor road.
*/
function isDrivableOpenMapRoad(properties = {}) {
return (
Object.hasOwn(ROAD_TYPES, properties.class) &&
!properties.subclass &&
!properties.service &&
![
'no',
'private',
'destination',
'customers',
'delivery',
'agricultural',
'forestry',
].includes(properties.access) &&
properties.brunnel !== 'tunnel' &&
!properties.indoor
);
}
/** Translate public motor roads; reverse negative one-way geometry. */
export function openMapRoad(coordinates, properties = {}) {
const type = ROAD_TYPES[properties.class];
if (!isDrivableOpenMapRoad(properties) || coordinates.length < 2) return null;
const reverse = Number(properties.oneway) === -1;
return {
coordinates: reverse ? coordinates.slice().reverse() : coordinates,
type,
drivable: true,
roadClass: properties.class,
roadProperties: { ...properties },
oneway: reverse || Number(properties.oneway) === 1 ? 1 : 0,
ramp: properties.ramp === 1,
brunnel: properties.brunnel || null,
};
}
/** Area-weighted centroid in tile-scale longitude/latitude coordinates. */
export function polygonCentroid(ring) {
if (!ring?.length) return null;
const [ox, oy] = ring[0];
let area = 0,
x = 0,
y = 0;
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
const ax = ring[j][0] - ox,
ay = ring[j][1] - oy;
const bx = ring[i][0] - ox,
by = ring[i][1] - oy;
const cross = ax * by - bx * ay;
area += cross;
x += (ax + bx) * cross;
y += (ay + by) * cross;
}
return Math.abs(area) > 1e-15
? [ox + x / (3 * area), oy + y / (3 * area)]
: ring[0];
}
/** Clip a line to its tile core so buffered copies never animate duplicate dots. */
export function clipTileLine(coords, box) {
const lines = [];
let line = [];
for (let i = 1; i < coords.length; i++) {
const a = coords[i - 1],
b = coords[i];
const dx = b[0] - a[0],
dy = b[1] - a[1];
let lo = 0,
hi = 1;
const p = [-dx, dx, -dy, dy],
q = [
a[0] - box.west,
box.east - a[0],
a[1] - box.south,
box.north - a[1],
];
for (let k = 0; k < 4; k++) {
if (p[k] === 0) {
if (q[k] < 0) hi = -1;
} else if (p[k] < 0) lo = Math.max(lo, q[k] / p[k]);
else hi = Math.min(hi, q[k] / p[k]);
}
if (lo > hi) {
if (line.length > 1) lines.push(line);
line = [];
continue;
}
const start = [a[0] + lo * dx, a[1] + lo * dy],
end = [a[0] + hi * dx, a[1] + hi * dy];
const last = line.at(-1);
if (
!last ||
Math.abs(last[0] - start[0]) + Math.abs(last[1] - start[1]) > 1e-10
) {
if (line.length > 1) lines.push(line);
line = [start];
}
line.push(end);
}
if (line.length > 1) lines.push(line);
return lines.filter((points) => {
let metres = 0;
for (let i = 1; i < points.length; i++)
metres +=
Math.hypot(
(points[i][0] - points[i - 1][0]) *
Math.cos((points[i][1] * Math.PI) / 180),
points[i][1] - points[i - 1][1],
) * 111320;
return metres >= 12;
});
}
/** Decode road lines and unnamed military polygons from an OpenMapTiles tile. */
export function decodeOpenFreeMapTile(bytes, z, x, y) {
return decodeLayers(bytes, z, x, y, ['transportation', 'landuse']);
}
/** Decode only the unnamed military polygons; installations never use roads. */
export function decodeOpenFreeMapMilitaryTile(bytes, z, x, y) {
return decodeLayers(bytes, z, x, y, ['landuse']);
}
function decodeLayers(bytes, z, x, y, layerNames) {
const tile = new VectorTile(new PbfReader(bytes));
const roads = [],
military = [];
const box = tileToBBox(z, x, y);
for (const name of layerNames) {
const layer = tile.layers[name];
if (!layer) continue;
if (layer.length > 40_000)
throw new Error('Vector tile feature limit exceeded');
for (let i = 0; i < layer.length; i++) {
const feature = layer.feature(i),
props = feature.properties;
if (
name === 'transportation'
? !isDrivableOpenMapRoad(props)
: props.class !== 'military'
)
continue;
const geometry = feature.toGeoJSON(x, y, z).geometry;
if (name === 'transportation') {
const lines =
geometry.type === 'LineString'
? [geometry.coordinates]
: geometry.type === 'MultiLineString'
? geometry.coordinates
: [];
for (const coords of lines)
for (const clipped of clipTileLine(coords, box))
roads.push(openMapRoad(clipped, props));
} else {
const polygons =
geometry.type === 'Polygon'
? [geometry.coordinates]
: geometry.type === 'MultiPolygon'
? geometry.coordinates
: [];
for (let p = 0; p < polygons.length; p++) {
const rings = polygons[p]
.map((r) => clipTileRing(r, box))
.filter((r) => r.length >= 4);
const ring = rings[0],
centroid = polygonCentroid(ring);
if (!centroid || ring.length < 4) continue;
const [longitude, latitude] = centroid;
military.push({
id: `ofm:${z}/${x}/${y}:${feature.id ?? i}:${p}`,
kind: 'installation',
class: 'military_land',
name: UNNAMED_MILITARY_AREA,
latitude,
longitude,
footprint: ring,
rings,
featureKey:
feature.id == null
? `tile:${z}/${x}/${y}:${i}`
: String(feature.id),
tileEpsilon: (box.east - box.west) / feature.extent,
tileBounds: box,
tileZoom: z,
outlineLines: rings.flatMap((r) => militaryOutlineLines(r, box)),
validation: 'unreviewed',
sources: [{ name: 'OpenStreetMap', id: `tile:${z}/${x}/${y}` }],
});
}
}
}
}
return { roads, military };
}
/**
* Construct an immutable-version road/military tile source without starting I/O.
*
* `allowedOrigin` is deliberately not set here: `createVectorTileSource`
* derives it from whichever `tileJsonUrl` is in effect, so a caller that
* repoints only `tileJsonUrl` gets tiles from that host or a named error —
* never a silent fall back to the default host (#933). Passing
* `allowedOrigin` explicitly still overrides both.
*/
export function createOpenFreeMapSource(options = {}) {
return createVectorTileSource({
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
decode: decodeOpenFreeMapTile,
maxEntries: 192,
maxCacheBytes: 64 * 1024 * 1024,
...options,
});
}