const clusterCount = point => { const count = Number(point?.count) return Number.isFinite(count) && count > 0 ? count : 1 } const urgentCount = point => { const count = Number(point?.urgent_count) return Number.isFinite(count) && count > 0 ? count : 0 } export const clusterDiameterPixels = count => { if (count >= 100_000) return 60 if (count >= 10_000) return 56 if (count >= 1_000) return 52 return 44 } export const clusterSizeClass = count => { if (count >= 100_000) return "is-xxl" if (count >= 10_000) return "is-xl" if (count >= 1_000) return "is-lg" return "is-md" } const mergeDistance = (left, right, gap) => (clusterDiameterPixels(left.count) + clusterDiameterPixels(right.count)) / 2 + gap const coordinatesFromPixel = (map, pixel) => { const coordinates = map.unproject([pixel.x, pixel.y]) return { longitude: Number(coordinates.lng ?? coordinates[0]), latitude: Number(coordinates.lat ?? coordinates[1]) } } export const mergeNearbyMapClusters = (map, points, options = {}) => { const gap = Number.isFinite(options.gap) ? Math.max(options.gap, 0) : 8 const groups = points.map(point => { const pixel = map.project([point.longitude, point.latitude]) return { points: [point], x: pixel.x, y: pixel.y, count: clusterCount(point), urgent_count: urgentCount(point) } }) let merged = true while (merged) { merged = false outer: for (let leftIndex = 0; leftIndex < groups.length; leftIndex += 1) { for (let rightIndex = leftIndex + 1; rightIndex < groups.length; rightIndex += 1) { const left = groups[leftIndex] const right = groups[rightIndex] const distance = Math.hypot(left.x - right.x, left.y - right.y) if (distance >= mergeDistance(left, right, gap)) continue const count = left.count + right.count left.x = (left.x * left.count + right.x * right.count) / count left.y = (left.y * left.count + right.y * right.count) / count left.count = count left.urgent_count += right.urgent_count left.points.push(...right.points) groups.splice(rightIndex, 1) merged = true break outer } } } return groups.map(group => { const coordinates = coordinatesFromPixel(map, group) const firstId = group.points[0]?.id || "anonymous" return { type: "cluster", id: `visual-cluster-${group.points.length}-${firstId}`, count: group.count, urgent_count: group.urgent_count, longitude: coordinates.longitude, latitude: coordinates.latitude } }) }