https://github.com/khmm12/react-map-gl-supercluster
https://github.com/khmm12/react-map-gl-supercluster
clustering react react-map-gl supercluster
Last synced: 11 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/khmm12/react-map-gl-supercluster
- Owner: khmm12
- License: mit
- Created: 2022-03-07T08:00:18.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2023-08-30T16:18:52.000Z (over 2 years ago)
- Last Synced: 2025-04-06T17:23:35.145Z (about 1 year ago)
- Topics: clustering, react, react-map-gl, supercluster
- Language: TypeScript
- Homepage:
- Size: 1.99 MB
- Stars: 23
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# react-map-gl-supercluster
> The easiest way to get `react-map-gl` and `supercluster` to work together
# Highlights
- TypeScript support.
- ESM support.
- Ready for tree shaking.
- No unnecessary re-renders.
# Install
```bash
$ yarn add react-map-gl-supercluster
# or
$ npm install react-map-gl-supercluster
```
# Usage
```tsx
import Map, { useMap } from 'react-map-gl' // react-map-gl/maplibre if you use maplibre instead
import {
useSupercluster,
PointFeature,
PointFeatureProperties,
PointClusterProperties
} from 'react-map-gl-supercluster' // or react-map-gl-supercluster/maplibre if you use maplibre instead
type Item = {}
type ItemPointFeatureProperties = PointFeatureProperties<{ item: Item }>
type ItemPointClusterProperties = PointClusterProperties<{ items: Item[] }>
function MyAwesomeMap(): ReactElement {
const mapRef = useRef()
// Points should be memoized
const points = useMemo(() => createPoints(items), [])
const { supercluster, clusters } = useSupercluster(points, {
mapRef,
map: mapFeature,
reduce: reduceCluster
})
const expandCluster = (clusterId, coordinates) => {
const zoom = supercluster.getClusterExpansionZoom(clusterId)
mapRef.current?.easeTo({
center: [coordinates.longitude, coordinates.latitude],
zoom,
})
}
return (
{clusters.map((cluster) => {
const [longitude, latitude] = cluster.geometry.coordinates
return cluster.properties.cluster ? (
expandCluster(cluster.properties.cluster_id, { longitude, latitude })}
/>
) : (
)
})}
)
}
function createPoints(items: Item[]): Array> {
return items.map(createPoint)
}
function createPoint(item: Item): PointFeature {
const { longitude, latitude } = item
return {
type: 'Feature',
properties: { cluster: false, item },
geometry: {
type: 'Point',
coordinates: [longitude, latitude],
},
}
}
// It creates cluster properties from feature properties.
function mapFeature(props: ItemPointFeatureProperties): ItemPointClusterProperties {
return { items: [props.item] }
}
// It merges clusters properties. Yes, it's simply mutates.
function reduceCluster(memo: ItemPointClusterProperties, props: ItemPointClusterProperties): void {
memo.items = memo.items.concat(props.items)
}
```
Alternatively you can use the hook inside `Map`.
```tsx
import Map, { useMap } from 'react-map-gl'// react-map-gl/maplibre if you use maplibre instead
import { useSupercluster } from 'react-map-gl-supercluster' // or react-map-gl-supercluster/maplibre if you use maplibre instead
function MyAwesomeMap(): ReactElement {
return (
)
}
type MarkersProps = {
items: Item[]
}
function Markers(props: MarkersProps): ReactElement {
const { items } = props
const map = useMap().current
// Points should be memoized
const points = useMemo(() => createPoints(items), [items])
const { supercluster, clusters } = useSupercluster(points, {
map: mapFeature,
reduce: reduceCluster
})
const expandCluster = (clusterId, coordinates) => {
const zoom = supercluster.getClusterExpansionZoom(clusterId)
map?.easeTo({
center: [coordinates.longitude, coordinates.latitude],
zoom,
})
}
return (
<>
{clusters.map((cluster) => {
const [longitude, latitude] = cluster.geometry.coordinates
return cluster.properties.cluster ? (
expandCluster(cluster.properties.cluster_id, { longitude, latitude })}
/>
) : (
)
})}
>
)
}
```
# API
## `useSupercluster`
The hook can be used in a component which renders `Map` component or inside `Map` children.
### Arguments
1. `points` - GeoJSON points array. The value should be memoized.
2. `options` – various options, see bellow.
### Return value
Object which contains 2 fields:
- `clusters` – clusters list
- `supercluster` – supercluster instance.
## Options
| Option | Default | Description |
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| mapRef | Optional | Reference to `react-map-gl` instance. |
| minZoom | 0 | Minimum zoom level at which clusters are generated. |
| maxZoom | 16 | Maximum zoom level at which clusters are generated. |
| minPoints | 2 | Minimum number of points to form a cluster. |
| radius | 40 | Cluster radius, in pixels. |
| map | Optional | A function that returns cluster properties corresponding to a single point. Should be memoized. [See](https://github.com/mapbox/supercluster/blob/main/README.md#property-mapreduce-options). |
| reduce | Optional | A reduce function that merges properties of two clusters into one. Should be memoized. [See](https://github.com/mapbox/supercluster/blob/main/README.md#property-mapreduce-options). |
`react-map-gl-supercluster` supports all `supercluster` options, you can find more information about them [there](https://github.com/mapbox/supercluster#options).
# FAQ
## Does it support react-map-gl v5/v6?
No, it doesn't.
## Why does it cause component re-rendering or why do I get infinite component update loop?
Please be careful with `points` and `map`/`reduce` functions. They always should be memoized.
## Does it support WebWorker?
No, the hook is running in the main thread. But probably WebWorker support will come in the future.