48 lines
1.7 KiB
JavaScript
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]);
|
|
}
|