76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
export interface GeoJsonBounds {
|
|
minX: number
|
|
minY: number
|
|
maxX: number
|
|
maxY: number
|
|
}
|
|
|
|
function extendBounds(bounds: GeoJsonBounds | null, x: number, y: number): GeoJsonBounds {
|
|
if (!bounds) {
|
|
return { minX: x, minY: y, maxX: x, maxY: y }
|
|
}
|
|
bounds.minX = Math.min(bounds.minX, x)
|
|
bounds.minY = Math.min(bounds.minY, y)
|
|
bounds.maxX = Math.max(bounds.maxX, x)
|
|
bounds.maxY = Math.max(bounds.maxY, y)
|
|
return bounds
|
|
}
|
|
|
|
function scanCoordinates(coordinates: unknown, initial: GeoJsonBounds | null): GeoJsonBounds | null {
|
|
let bounds = initial
|
|
const stack: unknown[] = [coordinates]
|
|
while (stack.length > 0) {
|
|
const value = stack.pop()
|
|
if (!Array.isArray(value)) {
|
|
continue
|
|
}
|
|
if (value.length >= 2 && typeof value[0] === 'number' && typeof value[1] === 'number') {
|
|
if (Number.isFinite(value[0]) && Number.isFinite(value[1])) {
|
|
bounds = extendBounds(bounds, value[0], value[1])
|
|
}
|
|
continue
|
|
}
|
|
for (const child of value) {
|
|
stack.push(child)
|
|
}
|
|
}
|
|
return bounds
|
|
}
|
|
|
|
export function geometryBounds(geometry: GeoJSON.Geometry | null | undefined): GeoJsonBounds | null {
|
|
if (!geometry) {
|
|
return null
|
|
}
|
|
if (geometry.type === 'GeometryCollection') {
|
|
return geometry.geometries.reduce<GeoJsonBounds | null>(
|
|
(bounds, child) => mergeBounds(bounds, geometryBounds(child)),
|
|
null,
|
|
)
|
|
}
|
|
return scanCoordinates(geometry.coordinates, null)
|
|
}
|
|
|
|
export function mergeBounds(left: GeoJsonBounds | null, right: GeoJsonBounds | null): GeoJsonBounds | null {
|
|
if (!left) {
|
|
return right ? { ...right } : null
|
|
}
|
|
if (!right) {
|
|
return left
|
|
}
|
|
left.minX = Math.min(left.minX, right.minX)
|
|
left.minY = Math.min(left.minY, right.minY)
|
|
left.maxX = Math.max(left.maxX, right.maxX)
|
|
left.maxY = Math.max(left.maxY, right.maxY)
|
|
return left
|
|
}
|
|
|
|
export function featureCollectionBounds(collection: GeoJSON.FeatureCollection | null | undefined): GeoJsonBounds | null {
|
|
if (!collection) {
|
|
return null
|
|
}
|
|
return collection.features.reduce<GeoJsonBounds | null>(
|
|
(bounds, feature) => mergeBounds(bounds, geometryBounds(feature.geometry)),
|
|
null,
|
|
)
|
|
}
|