import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; class ClusterParams { final List points; final double clusterRadiusMeters; ClusterParams({ required this.points, required this.clusterRadiusMeters, }); } class Cluster { final LatLng center; final List members; Cluster(this.center, this.members); } // Use a more efficient quadtree-based clustering algorithm Future> clusterMarkersQuadtreeIsolate(ClusterParams params) async { return compute(_clusterMarkersQuadtree, params); } List _clusterMarkersQuadtree(ClusterParams params) { final points = params.points; final radius = params.clusterRadiusMeters; final distance = const Distance(); final List clusters = []; // Skip clustering if we have a small number of points if (points.length < 100) { return points.map((p) => Cluster(p, [p])).toList(); } // Find bounds double minLat = points[0].latitude; double maxLat = points[0].latitude; double minLng = points[0].longitude; double maxLng = points[0].longitude; for (final point in points) { minLat = min(minLat, point.latitude); maxLat = max(maxLat, point.latitude); minLng = min(minLng, point.longitude); maxLng = max(maxLng, point.longitude); } // Build spatial grid for faster lookups (simple spatial index) // Convert geographic distance to approximate degrees final double radiusDegLat = radius / 111000; // ~111km per degree latitude final double radiusDegLng = radius / (111000 * cos(minLat * pi / 180)); // Adjust for longitude final int gridLatSize = ((maxLat - minLat) / radiusDegLat).ceil(); final int gridLngSize = ((maxLng - minLng) / radiusDegLng).ceil(); // Create spatial grid final List>> grid = List.generate( gridLatSize + 1, (_) => List.generate(gridLngSize + 1, (_) => []) ); // Add points to grid cells for (final point in points) { final int latIdx = ((point.latitude - minLat) / radiusDegLat).floor(); final int lngIdx = ((point.longitude - minLng) / radiusDegLng).floor(); grid[latIdx][lngIdx].add(point); } // Process grid cells in batches final Set processed = {}; for (int latIdx = 0; latIdx < gridLatSize; latIdx++) { for (int lngIdx = 0; lngIdx < gridLngSize; lngIdx++) { final cellPoints = grid[latIdx][lngIdx]; for (final point in cellPoints) { if (processed.contains(point)) continue; // Find nearby points final List neighbors = []; neighbors.add(point); processed.add(point); // Check current and adjacent cells for neighbors for (int adjLat = max(0, latIdx - 1); adjLat <= min(gridLatSize - 1, latIdx + 1); adjLat++) { for (int adjLng = max(0, lngIdx - 1); adjLng <= min(gridLngSize - 1, lngIdx + 1); adjLng++) { for (final neighbor in grid[adjLat][adjLng]) { if (!processed.contains(neighbor) && distance(point, neighbor) <= radius) { neighbors.add(neighbor); processed.add(neighbor); } } } } // Calculate cluster center if (neighbors.isNotEmpty) { final avgLat = neighbors.map((p) => p.latitude).reduce((a, b) => a + b) / neighbors.length; final avgLng = neighbors.map((p) => p.longitude).reduce((a, b) => a + b) / neighbors.length; clusters.add(Cluster(LatLng(avgLat, avgLng), neighbors)); } } } } return clusters; }