Files
gods-eye-view/src/data/detectionPolicy.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

257 lines
10 KiB
JavaScript

/**
* @module detectionPolicy
* @description Pure detection density/profile and collective-budget helpers.
* The five-stop UI, share links, voice actions, and renderer all use this
* module so density cannot drift into contradictory mode semantics again.
*/
export const ALLOCATION_ELASTIC = 'ELASTIC';
export const ALLOCATION_WEIGHTED = 'WEIGHTED';
export const ALLOCATION_STRATEGIES = Object.freeze([
ALLOCATION_ELASTIC,
ALLOCATION_WEIGHTED,
]);
// The keyhole's default 1% outside opacity is appropriate for text, but it
// made side aircraft brackets effectively disappear while the same contacts
// remained eligible. AIR geometry therefore gets a readable floor, without
// changing label budgets.
//
// The floor is 0.35 AT THE DEFAULT OUTSIDE OPACITY, and it SCALES with the
// OUTSIDE slider rather than overriding it. A flat 0.35 made that slider a
// no-op for aircraft: dialling it down to 1% still painted brackets at 35%, so
// the control did nothing across a third of its travel while visibly working
// for labels. A setting that moves everything except the thing the operator is
// looking at is a broken control, not a safety net.
export const AIRCRAFT_BRACKET_ALPHA_FLOOR = 0.35;
/**
* Mirror of celestialRing's KEYHOLE_OUTSIDE_OPACITY_DEFAULT — the OUTSIDE
* setting the bracket look is calibrated at, so `AIRCRAFT_BRACKET_ALPHA_FLOOR`
* lands EXACTLY at the shipped default. Mirrored rather than imported so this
* stays a pure policy module with no Cesium dependency; detectionPolicy.test.mjs
* imports the real constant and pins the two together so they cannot drift.
*
* It MOVES WITH THE DEFAULT (0.05 → 0.03 → 0.01; owner final lock 2026-08-24).
* That pin is the tripwire
* for exactly this change, and the decision it forces is which of two things the
* approval attaches to: the bracket BRIGHTNESS, or the slider POSITION. It is
* the brightness — the floor exists so side aircraft brackets stay readable at
* whatever the default is, and the owner's directive moved the label default,
* not the bracket look. Holding the anchor at 0.05 instead would dim brackets to
* 0.07 on a first run at the 1% default, which is the dead-zone complaint in
* reverse.
*/
export const AIRCRAFT_BRACKET_FLOOR_ANCHOR = 0.01;
/**
* The AIR bracket alpha floor for a given OUTSIDE opacity setting.
*
* Piecewise linear through three fixed points, chosen so the control is honest
* across its whole travel while the shipped look does not move:
*
* slider 0 → 0 off means off; the operator's zero stays authoritative
* slider 0.01 → 0.35 EXACTLY the shipped constant at the default, so the
* approved look is reproduced unchanged (above the
* anchor the floor rises smoothly — 0.05 ≈ 0.376)
* slider 1.0 → 1.0 the boost decays to nothing: at full opacity the floor
* equals the label alpha and stops overriding at all
*
* Strictly increasing, so every step of the slider changes the picture and
* there is no dead zone at either end — which was the whole complaint.
*
* A non-finite input falls back to the anchor: an unreadable setting should
* reproduce the approved default, never silently blank the brackets.
*
* @param {number} outsideOpacity - Keyhole outside opacity, 0-1.
* @returns {number} Alpha floor for AIR brackets, 0-1.
*/
export function aircraftBracketAlphaFloor(outsideOpacity) {
const raw = Number(outsideOpacity);
const outside = Number.isFinite(raw)
? Math.max(0, Math.min(1, raw))
: AIRCRAFT_BRACKET_FLOOR_ANCHOR;
if (outside <= 0) return 0;
if (outside <= AIRCRAFT_BRACKET_FLOOR_ANCHOR) {
return (
AIRCRAFT_BRACKET_ALPHA_FLOOR * (outside / AIRCRAFT_BRACKET_FLOOR_ANCHOR)
);
}
const progress =
(outside - AIRCRAFT_BRACKET_FLOOR_ANCHOR) /
(1 - AIRCRAFT_BRACKET_FLOOR_ANCHOR);
return (
AIRCRAFT_BRACKET_ALPHA_FLOOR + (1 - AIRCRAFT_BRACKET_ALPHA_FLOOR) * progress
);
}
/**
* Resolve the paint alpha for a detection bracket without altering admission.
* @param {string} type - Detection object type; only AIR is floored.
* @param {number} keyholeAlpha - Radial keyhole alpha for this contact.
* @param {number} [outsideOpacity] - Live OUTSIDE slider value. Omitted means
* the default, so a caller that does not know it reproduces the shipped look.
* @param {boolean} [strictKeyholeFade] - When true, do not apply the AIR
* readability floor; the bracket follows the radial keyhole alpha exactly.
* @returns {number} Paint alpha, 0-1.
*/
export function detectionBracketAlpha(
type,
keyholeAlpha,
outsideOpacity = AIRCRAFT_BRACKET_FLOOR_ANCHOR,
strictKeyholeFade = false,
) {
const alpha = Math.max(0, Math.min(1, Number(keyholeAlpha) || 0));
if (strictKeyholeFade) return alpha;
if (String(type || '').toUpperCase() !== 'AIR' || alpha <= 0) return alpha;
return Math.max(aircraftBracketAlphaFloor(outsideOpacity), alpha);
}
/** Stable left/front/right bucket for bracket coverage diagnostics and QA. */
export function detectionHorizontalSector(screenX, viewportWidth) {
const width = Number(viewportWidth);
const x = Number(screenX);
if (!(width > 0) || !Number.isFinite(x)) return 'front';
if (x < width / 3) return 'left';
if (x > (width * 2) / 3) return 'right';
return 'front';
}
const VIEW_SCALE_BUDGETS = Object.freeze({
street: Object.freeze({ 0: 9, 25: 23, 50: 45, 75: 68, 100: 90 }),
city: Object.freeze({ 0: 7, 25: 18, 50: 35, 75: 53, 100: 70 }),
metro: Object.freeze({ 0: 6, 25: 14, 50: 28, 75: 41, 100: 55 }),
regional: Object.freeze({ 0: 4, 25: 10, 50: 20, 75: 30, 100: 40 }),
// Full-globe views leave substantial radial screen space around the Earth.
// Dense satellite fields can use that space without card overlap, so the
// global row intentionally has a larger ceiling than the regional row.
global: Object.freeze({ 0: 6, 25: 14, 50: 28, 75: 42, 100: 56 }),
});
/** Clamp and canonicalize arbitrary input onto the five approved density stops. */
export function canonicalizeDensity(inputPct, fallback = 50) {
const raw = Number(inputPct);
if (!Number.isFinite(raw)) return canonicalizeDensity(fallback, 50);
const pct = Math.max(0, Math.min(100, raw));
if (pct <= 25) return pct < 12.5 ? 0 : 25;
if (pct < 75) return 50;
return pct < 87.5 ? 75 : 100;
}
/** Derive the only valid enabled profile from a canonical density stop. */
export function profileForDensity(inputPct) {
const stop = canonicalizeDensity(inputPct);
if (stop <= 25) return 'SPARSE';
if (stop >= 75) return 'DENSE';
return 'BALANCED';
}
/** Return the canonical default stop for an enabled profile or legacy alias. */
export function defaultDensityForProfile(profile) {
const normalized = normalizeProfile(profile);
if (normalized === 'SPARSE') return 25;
if (normalized === 'DENSE') return 75;
return 50;
}
/** Normalize current and legacy profile labels. OFF remains an enabled-state value. */
export function normalizeProfile(profile) {
const raw = String(profile || '')
.trim()
.toUpperCase();
if (raw === 'OFF') return 'OFF';
if (raw === 'SPARSE' || raw === 'SURVEY') return 'SPARSE';
if (raw === 'BALANCED' || raw === 'NORMAL') return 'BALANCED';
if (raw === 'DENSE' || raw === 'PANOPTIC' || raw === 'GOD' || raw === 'ON')
return 'DENSE';
return null;
}
/** Normalize the user-selectable layer allocation strategy. */
export function normalizeAllocationStrategy(
strategy,
fallback = ALLOCATION_ELASTIC,
) {
const raw = String(strategy || '')
.trim()
.toUpperCase();
if (ALLOCATION_STRATEGIES.includes(raw)) return raw;
const normalizedFallback = String(fallback || '')
.trim()
.toUpperCase();
return ALLOCATION_STRATEGIES.includes(normalizedFallback)
? normalizedFallback
: ALLOCATION_ELASTIC;
}
/** Classify camera altitude into the shared label-budget view scale. */
export function viewScaleForAltitude(altitudeM) {
const altitude = Number.isFinite(Number(altitudeM))
? Math.max(0, Number(altitudeM))
: 1e9;
if (altitude < 1200) return 'street';
if (altitude < 4500) return 'city';
if (altitude < 20000) return 'metro';
if (altitude < 150000) return 'regional';
return 'global';
}
/** Resolve the collective text-callout cap for an altitude and density stop. */
export function labelBudgetFor(altitudeM, densityPct) {
const scale = viewScaleForAltitude(altitudeM);
const stop = canonicalizeDensity(densityPct);
return VIEW_SCALE_BUDGETS[scale][stop];
}
/**
* Migrate a persisted/legacy mode+density pair into one canonical state.
* Legacy Panoptic/God intent resolves to Dense even when its historical
* density was below 75; legacy Sparse remains inside the Sparse band.
*/
export function migrateDetectionState(mode, densityPct, fallbackDensity = 50) {
const rawMode = String(mode || '')
.trim()
.toUpperCase();
const normalized = normalizeProfile(rawMode);
if (normalized === 'OFF') {
const supplied = Number.isFinite(Number(densityPct))
? densityPct
: fallbackDensity;
const stop = canonicalizeDensity(supplied, fallbackDensity);
return {
enabled: false,
profile: profileForDensity(stop),
densityPct: stop,
};
}
if (rawMode === 'PANOPTIC' || rawMode === 'GOD' || rawMode === 'ON') {
const density = Number.isFinite(Number(densityPct))
? Math.max(75, Number(densityPct))
: 75;
return {
enabled: true,
profile: 'DENSE',
densityPct: canonicalizeDensity(density),
};
}
if (rawMode === 'SPARSE' || rawMode === 'SURVEY') {
const density = Number.isFinite(Number(densityPct))
? Math.min(25, Number(densityPct))
: 25;
const stop = canonicalizeDensity(density, 25);
return { enabled: true, profile: 'SPARSE', densityPct: stop };
}
if (normalized === 'BALANCED' || normalized === 'DENSE') {
const supplied = Number.isFinite(Number(densityPct))
? canonicalizeDensity(densityPct)
: defaultDensityForProfile(normalized);
const stop =
profileForDensity(supplied) === normalized
? supplied
: defaultDensityForProfile(normalized);
return { enabled: true, profile: normalized, densityPct: stop };
}
const stop = canonicalizeDensity(densityPct, fallbackDensity);
return { enabled: true, profile: profileForDensity(stop), densityPct: stop };
}