Files
gods-eye-view/src/maps/imageryComparison.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

125 lines
4.1 KiB
JavaScript

/** Basemap that before/after imagery comparisons are read against. */
const IMAGERY_COMPARISON_STACK_ID = 'esri-imagery';
const SWITCH_POLICIES = new Set(['preserve', 'esri', 'esri-if-photoreal']);
const leases = new WeakMap();
function comparisonTargetId(activeId, switchPolicy) {
if (!SWITCH_POLICIES.has(switchPolicy))
throw new Error(
`Unknown imagery comparison switch policy: ${switchPolicy}`,
);
if (switchPolicy === 'esri' && activeId !== IMAGERY_COMPARISON_STACK_ID)
return IMAGERY_COMPARISON_STACK_ID;
if (switchPolicy === 'esri-if-photoreal' && activeId === 'photoreal')
return IMAGERY_COMPARISON_STACK_ID;
return null;
}
/**
* Lease the map for an imagery comparison.
*
* Works on any controller exposing getActiveId(), getSwitchGeneration() and
* setStack(id); MapSourceController#acquireImageryComparison delegates here.
* One lease per controller: acquiring while another owner holds it throws
* synchronously. The previous stack id and the switch generation are recorded
* the moment the switch is issued (not after awaiting it), so release() can
* tell its own switch from a later operator choice. `ready` resolves to
* `{ status: 'ready' | 'superseded' | 'failed', activeId }`; a resolved
* setStack() does not mean success because the controller falls back
* internally. release() is idempotent and restores the previous stack only
* while the lease's own generation still owns the active stack; releasing
* during acquisition re-issues the previous stack so the late activation
* cannot survive. The lease stays owned until that restoration settles (a
* rival acquiring meanwhile still throws), so nobody can read the comparison
* stack as their own while it is on its way out; a release with nothing to
* restore frees the lease synchronously.
*/
export function acquireImageryComparison(
controller,
{ owner = 'imagery-comparison', switchPolicy = 'preserve' } = {},
) {
if (!controller)
throw new Error('Imagery comparison needs a map stack controller');
const held = leases.get(controller);
if (held)
throw new Error(
held.releasing
? `Imagery comparison is still held by ${held.owner} until its release settles`
: `Imagery comparison is already held by ${held.owner}`,
);
const previousId = controller.getActiveId?.() ?? null;
const targetId = comparisonTargetId(previousId, switchPolicy);
const lease = {
owner,
generation: null,
acquiring: false,
releasing: null,
};
leases.set(controller, lease);
const activeId = () => controller.getActiveId?.() ?? null;
const generation = () => controller.getSwitchGeneration?.() ?? null;
let ready;
if (targetId) {
lease.acquiring = true;
let switching;
try {
switching = Promise.resolve(controller.setStack(targetId));
} catch (error) {
switching = Promise.reject(error);
}
lease.generation = generation();
const settle = () => {
lease.acquiring = false;
};
ready = switching.then(
() => {
settle();
const active = activeId();
const status =
generation() !== lease.generation
? 'superseded'
: active === targetId
? 'ready'
: 'failed';
return { status, activeId: active };
},
(error) => {
settle();
throw error;
},
);
} else {
ready = Promise.resolve({ status: 'ready', activeId: previousId });
}
const disown = () => {
if (leases.get(controller) === lease) leases.delete(controller);
};
const release = () => {
if (lease.releasing) return lease.releasing;
const restore =
Boolean(targetId) &&
previousId != null &&
previousId !== targetId &&
generation() === lease.generation &&
(lease.acquiring || activeId() === targetId);
if (!restore) {
disown();
lease.releasing = Promise.resolve();
return lease.releasing;
}
lease.releasing = (async () => {
try {
await controller.setStack(previousId);
} finally {
disown();
}
})();
return lease.releasing;
};
return { ready, release };
}