Files
gods-eye-view/src/data/militaryNames.js
T
Sameh KhamisandClaude Opus 5.5 9c87fabf88 build: ship each bundled data pack once
loadBundledJson's callers passed a literal JSON import as a Node fallback,
which made the production build emit a JavaScript copy of every pack beside
the JSON the browser fetches. The loader now reads file: URLs itself under
Node (through process.getBuiltinModule, synchronously, as promptly as the
cached import it replaces), and the callers pass only the URL. On a Node too
old to provide getBuiltinModule it says that Node 24.14 or newer is needed.
dist/ drops from 54 MB to 42 MB. A test keeps app code from importing a pack
as a module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 21:08:11 -07:00

120 lines
3.7 KiB
JavaScript

import { militaryOsmKey } from '../sources/militaryTileGeometry.js';
export { militaryOsmKey } from '../sources/militaryTileGeometry.js';
import { requestWithDeadline } from '../sources/requestDeadline.js';
import { loadBundledJson } from './bundledJson.js';
import { createRetryableLoader } from './retryableLoad.js';
export const MILITARY_POINT_CAP = 512;
export const MILITARY_LABEL_CAP = 24;
export const MILITARY_NAMES_TIMEOUT_MS = 10_000;
/** Share a bounded acquisition; failed assets become retryable after cooldown. */
export function createMilitaryNamesLoader({
loadPack = (signal) =>
loadBundledJson(
new URL('./local_data/osm_military_names/names.json', import.meta.url),
{ signal },
),
timeoutMs = MILITARY_NAMES_TIMEOUT_MS,
...retryOptions
} = {}) {
return createRetryableLoader(async () => {
const pack = await requestWithDeadline(loadPack, { timeoutMs });
const records = pack.records.map(
([
osmKey,
name,
longitude,
latitude,
west,
south,
east,
north,
kind,
areaM2,
]) => ({
id: `osm:military:${osmKey}`,
osmKey,
name,
longitude,
latitude,
bbox: [west, south, east, north],
class: pack.classes[kind],
areaM2,
kind: 'installation',
namedArea: true,
pointOnly: true,
validation: 'unreviewed',
sources: [
{ name: 'OpenStreetMap', id: osmKey },
{ name: 'Overture Maps Foundation', id: osmKey },
],
}),
);
records.sort(
(a, b) => b.areaM2 - a.areaM2 || a.osmKey.localeCompare(b.osmKey),
);
return { records, byId: new Map(records.map((r) => [r.osmKey, r])) };
}, retryOptions);
}
/** Load and index the separate data asset only when installation context needs it. */
export const loadMilitaryNames = createMilitaryNamesLoader();
/** Join exact source identity before parcel merging; reject geographically inconsistent ids. */
export function nameMilitaryFragment(fragment, names) {
const osmKey = militaryOsmKey(fragment.featureKey);
const named = names?.byId.get(osmKey);
if (!named || !fragment.footprint?.length) return fragment;
const [west, south, east, north] = named.bbox;
const epsilon = (fragment.tileEpsilon || 0) * 2 + 0.0001;
const xs = fragment.footprint.map((p) => p[0]);
const ys = fragment.footprint.map((p) => p[1]);
if (
Math.max(...xs) < west - epsilon ||
Math.min(...xs) > east + epsilon ||
Math.max(...ys) < south - epsilon ||
Math.min(...ys) > north + epsilon
)
return fragment;
return {
...fragment,
name: named.name,
class: named.class,
osmKey,
namedArea: true,
areaM2: named.areaM2,
nameRecord: named,
};
}
/**
* Area-ranked spatial thinning of visible label points, including dateline
* views. With `thinned` false every point in the box is kept, up to `cap`.
*/
export function militaryNamesInView(
names,
box,
cap = MILITARY_POINT_CAP,
thinned = true,
) {
const width =
box.east >= box.west ? box.east - box.west : 360 + box.east - box.west;
const height = box.north - box.south;
const cells = new Set(),
records = [];
let count = 0;
for (const record of names?.records || []) {
const x = (record.longitude - box.west + 360) % 360;
if (x > width || record.latitude < box.south || record.latitude > box.north)
continue;
count++;
const cell = `${Math.min(31, Math.floor((x / width) * 32))}:${Math.min(15, Math.floor(((record.latitude - box.south) / height) * 16))}`;
if ((thinned && cells.has(cell)) || records.length >= cap) continue;
cells.add(cell);
records.push(record);
}
return { records, count };
}