Files
NODEDC_DESIGN_GUIDELINE/apps/catalog/src/hGeoZoneProjection.mjs
T

48 lines
1.7 KiB
JavaScript

/**
* Canonicalize a provider-neutral GeoJSON linear ring before it reaches a
* Cesium geometry worker. GeoJSON closes a ring by repeating its first
* coordinate, while GroundPolylineGeometry with `loop: true` expects an open
* sequence. Some source snapshots contain both adjacent duplicates and more
* than one closing coordinate; forwarding either shape can create a
* zero-length segment and stop Cesium's render loop.
*
* Invalid coordinates invalidate the complete ring. Silently retaining a
* partial polygon would display a boundary the provider never published.
*
* @param {Array<[number, number]>} ring
* @returns {Array<[number, number]>}
*/
export function normalizeHGeoZoneRing(ring) {
if (!Array.isArray(ring)) return [];
/** @type {Array<[number, number]>} */
const normalized = [];
for (const coordinate of ring) {
if (
!Array.isArray(coordinate)
|| coordinate.length < 2
|| !Number.isFinite(coordinate[0])
|| !Number.isFinite(coordinate[1])
|| coordinate[0] < -180
|| coordinate[0] > 180
|| coordinate[1] < -90
|| coordinate[1] > 90
) return [];
const next = /** @type {[number, number]} */ ([coordinate[0], coordinate[1]]);
if (!sameCoordinate(normalized.at(-1), next)) normalized.push(next);
}
while (normalized.length > 1 && sameCoordinate(normalized[0], normalized.at(-1))) {
normalized.pop();
}
const distinct = new Set(normalized.map(([longitude, latitude]) => `${longitude}:${latitude}`));
return distinct.size >= 3 ? normalized : [];
}
/**
* @param {[number, number] | undefined} left
* @param {[number, number] | undefined} right
*/
function sameCoordinate(left, right) {
return Boolean(left && right && left[0] === right[0] && left[1] === right[1]);
}