Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@

package com.google.maps.android.clustering;

import android.content.Context;
import android.os.AsyncTask;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Marker;
import com.google.maps.android.collections.MarkerManager;
import com.google.maps.android.clustering.algo.Algorithm;
import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm;
import com.google.maps.android.clustering.algo.PreCachingAlgorithmDecorator;
import com.google.maps.android.clustering.algo.ScreenBasedAlgorithm;
import com.google.maps.android.clustering.algo.ScreenBasedAlgorithmAdapter;
import com.google.maps.android.clustering.view.ClusterRenderer;
import com.google.maps.android.clustering.view.DefaultClusterRenderer;
import com.google.maps.android.collections.MarkerManager;

import android.content.Context;
import android.os.AsyncTask;

import java.util.Collection;
import java.util.Set;
Expand Down Expand Up @@ -147,6 +147,10 @@ public Algorithm<T> getAlgorithm() {
return mAlgorithm;
}

/**
* Removes all items from the cluster manager. After calling this method you must invoke
* {@link #cluster()} for the map to be cleared.
*/
public void clearItems() {
mAlgorithm.lock();
try {
Expand All @@ -156,44 +160,85 @@ public void clearItems() {
}
}

public void addItems(Collection<T> items) {
/**
* Adds items to clusters. After calling this method you must invoke {@link #cluster()} for the
* state of the clusters to be updated on the map.
* @param items items to add to clusters
* @return true if the cluster manager contents changed as a result of the call
*/
public boolean addItems(Collection<T> items) {
mAlgorithm.lock();
try {
mAlgorithm.addItems(items);
return mAlgorithm.addItems(items);
} finally {
mAlgorithm.unlock();
}
}

public void addItem(T myItem) {
/**
* Adds an item to a cluster. After calling this method you must invoke {@link #cluster()} for
* the state of the clusters to be updated on the map.
* @param myItem item to add to clusters
* @return true if the cluster manager contents changed as a result of the call
*/
public boolean addItem(T myItem) {
mAlgorithm.lock();
try {
mAlgorithm.addItem(myItem);
return mAlgorithm.addItem(myItem);
} finally {
mAlgorithm.unlock();
}
}

public void removeItems(Collection<T> items) {
/**
* Removes items from clusters. After calling this method you must invoke {@link #cluster()} for
* the state of the clusters to be updated on the map.
* @param items items to remove from clusters
* @return true if the cluster manager contents changed as a result of the call
*/
public boolean removeItems(Collection<T> items) {
mAlgorithm.lock();
try {
mAlgorithm.removeItems(items);
return mAlgorithm.removeItems(items);
} finally {
mAlgorithm.unlock();
}
}

public void removeItem(T item) {
/**
* Removes an item from clusters. After calling this method you must invoke {@link #cluster()}
* for the state of the clusters to be updated on the map.
* @param item item to remove from clusters
* @return true if the item was removed from the cluster manager as a result of this call
*/
public boolean removeItem(T item) {
mAlgorithm.lock();
try {
return mAlgorithm.removeItem(item);
} finally {
mAlgorithm.unlock();
}
}

/**
* Updates an item in clusters. After calling this method you must invoke {@link #cluster()} for
* the state of the clusters to be updated on the map.
* @param item item to update in clusters
* @return true if the item was updated in the cluster manager, false if the item is not
* contained within the cluster manager and the cluster manager contents are unchanged
*/
public boolean updateItem(T item) {
mAlgorithm.lock();
try {
mAlgorithm.removeItem(item);
return mAlgorithm.updateItem(item);
} finally {
mAlgorithm.unlock();
}
}

/**
* Force a re-cluster. You may want to call this after adding new item(s).
* Force a re-cluster on the map. You should call this after adding, removing, updating,
* or clearing item(s).
*/
public void cluster() {
mClusterTaskLock.writeLock().lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,44 @@
*/
public interface Algorithm<T extends ClusterItem> {

void addItem(T item);
/**
* Adds an item to the algorithm
* @param item the item to be added
* @return true if the algorithm contents changed as a result of the call
*/
boolean addItem(T item);

void addItems(Collection<T> items);
/**
* Adds a collection of items to the algorithm
* @param items the items to be added
* @return true if the algorithm contents changed as a result of the call
*/
boolean addItems(Collection<T> items);

void clearItems();

void removeItem(T item);
/**
* Removes an item from the algorithm
* @param item the item to be removed
* @return true if this algorithm contained the specified element (or equivalently, if this
* algorithm changed as a result of the call).
*/
boolean removeItem(T item);

void removeItems(Collection<T> items);
/**
* Updates the provided item in the algorithm
* @param item the item to be updated
* @return true if the item existed in the algorithm and was updated, or false if the item did
* not exist in the algorithm and the algorithm contents remain unchanged.
*/
boolean updateItem(T item);

/**
* Removes a collection of items from the algorithm
* @param items the items to be removed
* @return true if this algorithm contents changed as a result of the call
*/
boolean removeItems(Collection<T> items);

Set<? extends Cluster<T>> getClusters(float zoom);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

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

import androidx.collection.LongSparseArray;

import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import com.google.maps.android.geometry.Point;
Expand All @@ -28,6 +26,8 @@
import java.util.HashSet;
import java.util.Set;

import androidx.collection.LongSparseArray;

/**
* Groups markers into a grid.
*/
Expand All @@ -38,29 +38,69 @@ public class GridBasedAlgorithm<T extends ClusterItem> extends AbstractAlgorithm

private final Set<T> mItems = Collections.synchronizedSet(new HashSet<T>());

/**
* Adds an item to the algorithm
* @param item the item to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public void addItem(T item) {
mItems.add(item);
public boolean addItem(T item) {
return mItems.add(item);
}

/**
* Adds a collection of items to the algorithm
* @param items the items to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public void addItems(Collection<T> items) {
mItems.addAll(items);
public boolean addItems(Collection<T> items) {
return mItems.addAll(items);
}

@Override
public void clearItems() {
mItems.clear();
}

/**
* Removes an item from the algorithm
* @param item the item to be removed
* @return true if this algorithm contained the specified element (or equivalently, if this
* algorithm changed as a result of the call).
*/
@Override
public boolean removeItem(T item) {
return mItems.remove(item);
}

/**
* Removes a collection of items from the algorithm
* @param items the items to be removed
* @return true if this algorithm contents changed as a result of the call
*/
@Override
public void removeItem(T item) {
mItems.remove(item);
public boolean removeItems(Collection<T> items) {
return mItems.removeAll(items);
}

/**
* Updates the provided item in the algorithm
* @param item the item to be updated
* @return true if the item existed in the algorithm and was updated, or false if the item did
* not exist in the algorithm and the algorithm contents remain unchanged.
*/
@Override
public void removeItems(Collection<T> items) {
mItems.removeAll(items);
public boolean updateItem(T item) {
boolean result;
synchronized (mItems) {
result = removeItem(item);
if (result) {
// Only add the item if it was removed (to help prevent accidental duplicates on map)
result = addItem(item);
}
}
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,39 @@ public class NonHierarchicalDistanceBasedAlgorithm<T extends ClusterItem> extend

private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);

/**
* Adds an item to the algorithm
* @param item the item to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public void addItem(T item) {
public boolean addItem(T item) {
boolean result;
final QuadItem<T> quadItem = new QuadItem<>(item);
synchronized (mQuadTree) {
mItems.add(quadItem);
mQuadTree.add(quadItem);
result = mItems.add(quadItem);
if (result) {
mQuadTree.add(quadItem);
}
}
return result;
}

/**
* Adds a collection of items to the algorithm
* @param items the items to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public void addItems(Collection<T> items) {
public boolean addItems(Collection<T> items) {
boolean result = false;
for (T item : items) {
addItem(item);
boolean individualResult = addItem(item);
if (individualResult) {
result = true;
}
}
return result;
}

@Override
Expand All @@ -86,28 +105,68 @@ public void clearItems() {
}
}

/**
* Removes an item from the algorithm
* @param item the item to be removed
* @return true if this algorithm contained the specified element (or equivalently, if this
* algorithm changed as a result of the call).
*/
@Override
public void removeItem(T item) {
public boolean removeItem(T item) {
boolean result;
// QuadItem delegates hashcode() and equals() to its item so,
// removing any QuadItem to that item will remove the item
final QuadItem<T> quadItem = new QuadItem<>(item);
synchronized (mQuadTree) {
mItems.remove(quadItem);
mQuadTree.remove(quadItem);
result = mItems.remove(quadItem);
if (result) {
mQuadTree.remove(quadItem);
}
}
return result;
}

/**
* Removes a collection of items from the algorithm
* @param items the items to be removed
* @return true if this algorithm contents changed as a result of the call
*/
@Override
public void removeItems(Collection<T> items) {
public boolean removeItems(Collection<T> items) {
boolean result = false;
synchronized (mQuadTree) {
for (T item : items) {
// QuadItem delegates hashcode() and equals() to its item so,
// removing any QuadItem to that item will remove the item
final QuadItem<T> quadItem = new QuadItem<>(item);
mItems.remove(quadItem);
mQuadTree.remove(quadItem);
boolean individualResult = mItems.remove(quadItem);
if (individualResult) {
mQuadTree.remove(quadItem);
result = true;
}
}
}
return result;
}

/**
* Updates the provided item in the algorithm
* @param item the item to be updated
* @return true if the item existed in the algorithm and was updated, or false if the item did
* not exist in the algorithm and the algorithm contents remain unchanged.
*/
@Override
public boolean updateItem(T item) {
// TODO - Can this be optimized to update the item in-place if the location hasn't changed?
boolean result;
synchronized (mQuadTree) {
result = removeItem(item);
if (result) {
// Only add the item if it was removed (to help prevent accidental duplicates on map)
result = addItem(item);
}
}
return result;
}

@Override
Expand Down
Loading