import { useEffect, useMemo, useReducer } from "react" import Supercluster, { type ClusterProperties } from "supercluster" import { useMapViewport } from "./use-map-viewport" import type { FeatureCollection, GeoJsonProperties, Point } from "geojson" export function useSupercluster( geojson: FeatureCollection, superclusterOptions: Supercluster.Options ) { // create the clusterer and keep it const clusterer = useMemo(() => { return new Supercluster(superclusterOptions) }, [superclusterOptions]) // version-number for the data loaded into the clusterer // (this is needed to trigger updating the clusters when data was changed) const [version, dataWasUpdated] = useReducer((x: number) => x + 1, 0) // when data changes, load it into the clusterer useEffect(() => { clusterer.load(geojson.features) dataWasUpdated() }, [clusterer, geojson]) // get bounding-box and zoomlevel from the map const { bbox, zoom } = useMapViewport({ padding: 100 }) // retrieve the clusters within the current viewport const clusters = useMemo(() => { // don't try to read clusters before data was loaded into the clusterer (version===0), // otherwise getClusters will crash if (!clusterer || version === 0) return [] return clusterer.getClusters(bbox, zoom) }, [version, clusterer, bbox, zoom]) function getClusterZoom(clusterId: number) { if (!clusterer || version === 0) { return null } return clusterer.getClusterExpansionZoom(clusterId) } // retrieve the hotel ids included in the cluster const containedHotels = clusters.map((cluster) => { if (cluster.properties?.cluster && typeof cluster.id === "number") { return clusterer.getLeaves(cluster.id).map((hotel) => Number(hotel.id)) } return [] }) return { clusters, containedHotels, getClusterZoom, } }