import type { LayerProps } from 'react-map-gl/maplibre' import type { ClusterLayerOptions } from '../types' const DEFAULT_COLORS: [string, string, string] = ['#51bbd6', '#f1f075', '#f28cb1'] const DEFAULT_RADII: [number, number, number] = [20, 30, 40] const DEFAULT_THRESHOLDS: [number, number] = [100, 750] const DEFAULT_HOVER_COLOR = '#3b82f6' // blue-500 export function createClusterLayers(options: ClusterLayerOptions): { cluster: LayerProps clusterCount: LayerProps unclusteredPoint: LayerProps } { const { sourceId, colors = DEFAULT_COLORS, radii = DEFAULT_RADII, thresholds = DEFAULT_THRESHOLDS, hoverColor = DEFAULT_HOVER_COLOR, } = options const cluster: LayerProps = { id: `${sourceId}-clusters`, type: 'circle', source: sourceId, filter: ['has', 'point_count'], paint: { 'circle-color': [ 'case', ['boolean', ['feature-state', 'hover'], false], hoverColor, [ 'step', ['get', 'point_count'], colors[0], thresholds[0], colors[1], thresholds[1], colors[2], ], ], 'circle-radius': [ 'step', ['get', 'point_count'], radii[0], thresholds[0], radii[1], thresholds[1], radii[2], ], 'circle-stroke-width': 2, 'circle-stroke-color': '#fff', }, } const clusterCount: LayerProps = { id: `${sourceId}-cluster-count`, type: 'symbol', source: sourceId, filter: ['has', 'point_count'], layout: { 'text-field': ['get', 'point_count_abbreviated'], 'text-size': 12, }, paint: { 'text-color': '#000', }, } const unclusteredPoint: LayerProps = { id: `${sourceId}-unclustered-point`, type: 'circle', source: sourceId, filter: ['!', ['has', 'point_count']], paint: { 'circle-color': [ 'case', ['boolean', ['feature-state', 'hover'], false], hoverColor, colors[0], ], 'circle-radius': [ 'case', ['boolean', ['feature-state', 'hover'], false], 10, 8, ], 'circle-stroke-width': 2, 'circle-stroke-color': '#fff', }, } return { cluster, clusterCount, unclusteredPoint } }