Files
web/apps/scandic-web/hooks/maps/use-supercluster.ts
Matilda Landström 00689607bc Merged in feat/SW-2241-country-map (pull request #2808)
Feat/SW-2241 country map

Approved-by: Erik Tiekstra
Approved-by: Chuma Mcphoy (We Ahead)
2025-09-24 12:04:01 +00:00

76 lines
2.3 KiB
TypeScript

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"
import type { CityMarkerProperties } from "@/types/components/maps/destinationMarkers"
export function useSupercluster<T extends GeoJsonProperties>(
geojson: FeatureCollection<Point, T>,
superclusterOptions: Supercluster.Options<T, ClusterProperties>
) {
// 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 containedHotelIds = clusters.map((cluster) => {
if (cluster.properties?.cluster && typeof cluster.id === "number") {
return clusterer
.getLeaves(cluster.id)
.map((location) => String(location.id))
}
return []
})
// retrieve the city ids included in the cluster
const containedCityIds = clusters.map((cluster) => {
if (cluster.properties?.cluster && typeof cluster.id === "number") {
return clusterer
.getLeaves(cluster.id)
.map((city) => city.properties as CityMarkerProperties)
}
return []
})
return {
clusters,
containedHotelIds,
containedCityIds,
getClusterZoom,
}
}