mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
86 lines
2.9 KiB
JavaScript
86 lines
2.9 KiB
JavaScript
import { MILITARY_INSTALLATION_BBOX_STEP_DEG } from './constants.js';
|
|
import { requiredFiniteQueryNumber } from '../common/query.js';
|
|
|
|
/**
|
|
* Snap a request bbox outward onto the shared installation cache grid.
|
|
* @param {{south:number, west:number, north:number, east:number}} box
|
|
* @param {number} [stepDeg]
|
|
* @returns {{south:number, west:number, north:number, east:number}}
|
|
*/
|
|
function quantizeMilitaryInstallationBox(
|
|
box,
|
|
stepDeg = MILITARY_INSTALLATION_BBOX_STEP_DEG,
|
|
) {
|
|
// Round the ratio first: 29.9999/0.05 lands a hair under an exact grid line
|
|
// in binary floating point, which would otherwise snap a whole cell too far.
|
|
const snap = (value, grow) => {
|
|
const cells = Number((value / stepDeg).toFixed(9));
|
|
return Number(
|
|
((grow > 0 ? Math.ceil(cells) : Math.floor(cells)) * stepDeg).toFixed(6),
|
|
);
|
|
};
|
|
return {
|
|
south: Math.max(-90, snap(box.south, -1)),
|
|
west: Math.max(-180, snap(box.west, -1)),
|
|
north: Math.min(90, snap(box.north, 1)),
|
|
east: Math.min(180, snap(box.east, 1)),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Stable disk/memory cache key for an installation bbox.
|
|
*
|
|
* The key's precision must match the precision of the bounds the QUERY uses, or
|
|
* two different queries collide on one entry. Snapped boxes live on a 0.05 deg
|
|
* grid, so 3 decimals is exact for them; an `exact=1` request carries the raw
|
|
* viewport at 5 decimals and must be keyed at 5, otherwise two nearby exact
|
|
* viewports would share an answer and the second would be missing the edge
|
|
* strip it just exposed.
|
|
* @param {{south:number, west:number, north:number, east:number}} box
|
|
* @param {number} [decimals]
|
|
*/
|
|
function militaryInstallationCacheKey(box, decimals = 3) {
|
|
return [box.south, box.west, box.north, box.east]
|
|
.map((value) => value.toFixed(decimals))
|
|
.join(',');
|
|
}
|
|
|
|
function validMilitaryInstallationBox(params) {
|
|
const south = requiredFiniteQueryNumber(params, 'south');
|
|
const west = requiredFiniteQueryNumber(params, 'west');
|
|
const north = requiredFiniteQueryNumber(params, 'north');
|
|
const east = requiredFiniteQueryNumber(params, 'east');
|
|
if (![south, west, north, east].every(Number.isFinite)) return null;
|
|
if (
|
|
south < -90 ||
|
|
north > 90 ||
|
|
west < -180 ||
|
|
east > 180 ||
|
|
south >= north ||
|
|
west >= east
|
|
)
|
|
return null;
|
|
if (north - south > 10 || east - west > 10) return null;
|
|
return { south, west, north, east };
|
|
}
|
|
|
|
/** Safe, evidence-based reason for an installation upstream failure. */
|
|
function militaryInstallationFailureReason(error) {
|
|
if (
|
|
['rate_limited', 'timeout', 'query_failed'].includes(
|
|
error?.installationReason,
|
|
)
|
|
)
|
|
return error.installationReason;
|
|
return ['AbortError', 'TimeoutError'].includes(error?.name)
|
|
? 'timeout'
|
|
: 'unavailable';
|
|
}
|
|
|
|
export {
|
|
validMilitaryInstallationBox,
|
|
quantizeMilitaryInstallationBox,
|
|
militaryInstallationCacheKey,
|
|
militaryInstallationFailureReason,
|
|
};
|