diff --git a/src/components/ui/DynamicMapContainer.tsx b/src/components/ui/DynamicMapContainer.tsx index 8e3da85..4ea6596 100644 --- a/src/components/ui/DynamicMapContainer.tsx +++ b/src/components/ui/DynamicMapContainer.tsx @@ -22,6 +22,52 @@ interface RawNode { // ... other raw fields you don't need } +const clusterNodes = (nodeList: GeoNode[], cellSize = 2) => { + const buckets = new Map< + string, + { latSum: number; lngSum: number; count: number } + >(); + + nodeList.forEach((node) => { + const latBucket = Math.round(node.lat / cellSize) * cellSize; + const lngBucket = Math.round(node.lng / cellSize) * cellSize; + const key = `${latBucket}|${lngBucket}`; + + const bucket = buckets.get(key); + if (bucket) { + bucket.latSum += node.lat; + bucket.lngSum += node.lng; + bucket.count += 1; + } else { + buckets.set(key, { + latSum: node.lat, + lngSum: node.lng, + count: 1, + }); + } + }); + + return Array.from(buckets.values()).map((bucket) => { + const avgLat = bucket.latSum / bucket.count; + const avgLng = bucket.lngSum / bucket.count; + const count = bucket.count; + + let color = "#06b6d4"; + if (count > 20) { + color = "#0891b2"; + } else if (count > 5) { + color = "#22d3ee"; + } + + return { + lat: avgLat, + lng: avgLng, + color, + label: `${count} nodes`, + }; + }); +}; + function DynamicMapContainer() { const [loading, setLoading] = useState(true); const [nodes, setNodes] = useState([]); @@ -44,7 +90,8 @@ function DynamicMapContainer() { // Optionally set color based on some node property if available })); - setNodes(geoNodes); + const clusteredNodes = clusterNodes(geoNodes); + setNodes(clusteredNodes); setLoading(false); } catch (error) { console.error("Failed to fetch node data:", error);