Skip to content

Commit 1329850

Browse files
authored
fix: Correct continuous zoom clustering implementation (#1587)
1 parent e0516c2 commit 1329850

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

library/src/main/java/com/google/maps/android/clustering/algo/ContinuousZoomEuclideanCentroidAlgorithm.java

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,23 @@
1616

1717
package com.google.maps.android.clustering.algo;
1818

19+
import com.google.android.gms.maps.model.LatLng;
20+
import com.google.maps.android.clustering.Cluster;
1921
import com.google.maps.android.clustering.ClusterItem;
2022
import com.google.maps.android.geometry.Bounds;
21-
import com.google.maps.android.quadtree.PointQuadTree;
2223

2324
import java.util.ArrayList;
2425
import java.util.Collection;
26+
import java.util.HashMap;
2527
import java.util.HashSet;
28+
import java.util.Map;
2629
import java.util.Set;
2730

2831
/**
2932
* A variant of {@link CentroidNonHierarchicalDistanceBasedAlgorithm} that uses
3033
* continuous zoom scaling and Euclidean distance for clustering.
3134
*
32-
* <p>This class overrides {@link #getClusteringItems(PointQuadTree, float)} to compute
35+
* <p>This class overrides {@link #getClusters(float)} to compute
3336
* clusters with a zoom-dependent radius, while keeping the centroid-based cluster positions.</p>
3437
*
3538
* @param <T> the type of cluster item
@@ -38,15 +41,21 @@ public class ContinuousZoomEuclideanCentroidAlgorithm<T extends ClusterItem>
3841
extends CentroidNonHierarchicalDistanceBasedAlgorithm<T> {
3942

4043
@Override
41-
protected Collection<QuadItem<T>> getClusteringItems(PointQuadTree<QuadItem<T>> quadTree, float zoom) {
44+
public Set<? extends Cluster<T>> getClusters(float zoom) {
4245
// Continuous zoom — no casting to int
4346
final double zoomSpecificSpan = getMaxDistanceBetweenClusteredItems() / Math.pow(2, zoom) / 256;
4447

4548
final Set<QuadItem<T>> visitedCandidates = new HashSet<>();
46-
final Collection<QuadItem<T>> result = new ArrayList<>();
49+
final Set<Cluster<T>> results = new HashSet<>();
50+
final Map<QuadItem<T>, Double> distanceToCluster = new HashMap<>();
51+
final Map<QuadItem<T>, StaticCluster<T>> itemToCluster = new HashMap<>();
52+
4753
synchronized (mQuadTree) {
48-
for (QuadItem<T> candidate : mItems) {
49-
if (visitedCandidates.contains(candidate)) continue;
54+
for (QuadItem<T> candidate : getClusteringItems(mQuadTree, zoom)) {
55+
if (visitedCandidates.contains(candidate)) {
56+
// Candidate is already part of another cluster.
57+
continue;
58+
}
5059

5160
Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
5261
Collection<QuadItem<T>> clusterItems = new ArrayList<>();
@@ -58,11 +67,46 @@ protected Collection<QuadItem<T>> getClusteringItems(PointQuadTree<QuadItem<T>>
5867
}
5968
}
6069

70+
if (clusterItems.size() == 1) {
71+
// Only the current marker is in range. Just add the single item to the results.
72+
results.add(candidate);
73+
visitedCandidates.add(candidate);
74+
distanceToCluster.put(candidate, 0d);
75+
continue;
76+
}
77+
StaticCluster<T> cluster = new StaticCluster<>(candidate.mClusterItem.getPosition());
78+
results.add(cluster);
79+
80+
for (QuadItem<T> clusterItem : clusterItems) {
81+
Double existingDistance = distanceToCluster.get(clusterItem);
82+
double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
83+
if (existingDistance != null) {
84+
// Item already belongs to another cluster. Check if it's closer to this cluster.
85+
if (existingDistance < distance) {
86+
continue;
87+
}
88+
// Move item to the closer cluster.
89+
itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
90+
}
91+
distanceToCluster.put(clusterItem, distance);
92+
cluster.add(clusterItem.mClusterItem);
93+
itemToCluster.put(clusterItem, cluster);
94+
}
6195
visitedCandidates.addAll(clusterItems);
62-
result.add(candidate);
6396
}
6497
}
65-
return result;
66-
}
6798

68-
}
99+
// Now, apply the centroid logic from CentroidNonHierarchicalDistanceBasedAlgorithm
100+
Set<StaticCluster<T>> newClusters = new HashSet<>();
101+
for (Cluster<T> cluster : results) {
102+
LatLng centroid = computeCentroid(cluster.getItems());
103+
StaticCluster<T> newCluster = new StaticCluster<>(centroid);
104+
for (T item : cluster.getItems()) {
105+
newCluster.add(item);
106+
}
107+
newClusters.add(newCluster);
108+
}
109+
110+
return newClusters;
111+
}
112+
}

0 commit comments

Comments
 (0)