51 lines
1.6 KiB
TypeScript
51 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { useMemo } from 'react'
|
|
import { useEffectiveSpatialGranularity } from '@/lib/store'
|
|
import { SPATIAL_TO_LEAFLET_ZOOM, type MapState, type EventListItem } from '@cal/shared'
|
|
|
|
const WORLD_CENTER: [number, number] = [30, 0]
|
|
|
|
interface EventWithCoords {
|
|
latitude?: number | null
|
|
longitude?: number | null
|
|
coordinates?: { latitude: number; longitude: number } | null
|
|
}
|
|
|
|
/**
|
|
* Computes map center and zoom from events and the current spatial granularity.
|
|
* Center is the centroid of all events with coordinates.
|
|
* Zoom is derived from the effective spatial granularity.
|
|
*/
|
|
export function useMapState(events: (EventListItem & EventWithCoords)[]): MapState {
|
|
const spatialGranularity = useEffectiveSpatialGranularity()
|
|
|
|
const center = useMemo<[number, number]>(() => {
|
|
const withCoords = events.filter((e) => {
|
|
if (e.coordinates) return true
|
|
if ('latitude' in e && e.latitude != null && 'longitude' in e && e.longitude != null) return true
|
|
return false
|
|
})
|
|
|
|
if (withCoords.length === 0) return WORLD_CENTER
|
|
|
|
let sumLat = 0
|
|
let sumLng = 0
|
|
for (const e of withCoords) {
|
|
if (e.coordinates) {
|
|
sumLat += e.coordinates.latitude
|
|
sumLng += e.coordinates.longitude
|
|
} else if ('latitude' in e && e.latitude != null && 'longitude' in e && e.longitude != null) {
|
|
sumLat += e.latitude
|
|
sumLng += e.longitude
|
|
}
|
|
}
|
|
|
|
return [sumLat / withCoords.length, sumLng / withCoords.length]
|
|
}, [events])
|
|
|
|
const zoom = SPATIAL_TO_LEAFLET_ZOOM[spatialGranularity]
|
|
|
|
return { center, zoom }
|
|
}
|