Google Maps Utility: how to get all markers from ClusterManager<?>?
Asked Answered
C

6

7

Sorry for my English

I tried the ClusterManager<?>.getMarkerCollection().getMarkers() method but it returns empty collection.

I use in my app Google Maps Utility Library. Every time after a screen rotation I create AsynkTask and in background thread read data from DB and add items to ClusterManager:

cursor.moveToFirst();
while (!cursor.isAfterLast()) {
    SomeData row = readSomeDataRow(cursor);
    clusterManager.addItem(new ClusterItemImpl(row));
    cursor.moveToNext();
}

When the AsyncTask finished its work (i.e. in main thread) I tried to get all markers from the ClusterManager:

clusterManager.cluster();
// cluster manager returns empty collection  \|/
markers = clusterManager.getMarkerCollection().getMarkers(); 

but the ClusterManager returns empty collection.

May be at the moment when I call getMarkers() the ClusterManager yet doesn't place markers on map and will do it a bit later (may be in background thread). If so, then how can I catch that moment?

Clipfed answered 17/3, 2014 at 8:12 Comment(3)
Have you found a solution to your problem?Buffington
clusterManager.getClusterMarkerCollection().getMarkers(); returns markers only if the markers are at a zoom level so that we can see them in mapBirdlime
Since ClusterManager method cluster() creates and calls ClusterManager.ClusterTask() AsyncTask, your try to get the markers right after calling cluster() is wrong. It seems that when ClusterManager.ClusterTask is finished it calls method onClustersChanged() on ClusterRenderer object.Paste
R
15

I will give you a nice workaround. First, I will give some background. Then, I will tell you the very simple method for modifying your code.

BACKGROUND: Let's first look at the implementation of ClusterManager.addItem from the library code:

public void addItem(T myItem) {
    this.mAlgorithmLock.writeLock().lock();

    try {
        this.mAlgorithm.addItem(myItem);
    } finally {
        this.mAlgorithmLock.writeLock().unlock();
    }

}

As you can see, when you call clusterManager.addItem, the ClusterManager then calls this.mAlgorithm.addItem. mAlgorithm is where your item is stored. Let's now look at the default constructor of ClusterManager:

public ClusterManager(Context context, GoogleMap map, MarkerManager markerManager) {
    ...
    this.mAlgorithm = new PreCachingAlgorithmDecorator(new  NonHierarchicalDistanceBasedAlgorithm());
    ...        
}

mAlgorithm is instantiated as a PreCachingAlgorithmDecorator containing a NonHierarchicalDistanceBasedAlgorithm. Unfortunately, since mAlgorithm is declared private, we don't have access to the items which are being added to the algorithm. However, there is happily an easy workaround! We simply instantiate mAlgorithm using ClusterManager.setAlgorithm. This allows us access to the algorithm class.

WORKAROUND: Here is your code with the workaround inserted.

  1. Put this declaration with your class variables:

    private Algorithm<Post> clusterManagerAlgorithm;
    
  2. In the place where you instantiate your ClusterManager, put this immediately afterwards:

     // Instantiate the cluster manager algorithm as is done in the ClusterManager
     clusterManagerAlgorithm = new NonHierarchicalDistanceBasedAlgorithm();
    
     // Set this local algorithm in clusterManager
     clusterManager.setAlgorithm(clusterManagerAlgorithm);
    
  3. You can leave your code for inserting items into the cluster exactly the same.

  4. When you want to get access to the items inserted, you simply use the algorithm as opposed to the ClusterManager:

    Collection<ClusterItemImpl> items = clusterManagerAlgorithm.getItems();
    

This returns the items instead of the Marker objects, but I believe it is what you need.

Resolution answered 22/1, 2016 at 22:27 Comment(2)
Great solution! You have a typo: should be 'clusterManagerAlgorithm = new NonHierarchicalDistanceBasedAlgorithm' (you are missing the first 'N' in NonHierarchicalDistanceBasedAlgorithm)Clementius
Just saying - the original setAlgorithm method inevitably wraps your algorithm with caching algorithm, which can be in some cases difficulty because this wrapping algorithm automatically get clusters also for lower and higher zoom level, that means one getClusters(zoom) call = 3 calls. They are threaded i know, but just saying in same cases this may be not wanted.Paste
B
4

override one of these:

onBeforeClusterItemRendered

onBeforeClusterRendered

onClusterRendered

onClusterItemRendered

Just like

 @Override
    protected void onClusterRendered(Cluster<MyItem> cluster, Marker marker) {
        super.onClusterRendered(cluster, marker);
    }

cluster.getItems() will returns the markers in a Cluster

Birdlime answered 11/9, 2014 at 10:34 Comment(0)
V
2

@Ethan solution works. Another hack is to use Reflection API in order to access the mAlgorithm(As mention by Ethan mAlgorithm is where your item is stored) field of ClusterManager class.

try {
            Field field = mClusterManager.getClass().getDeclaredField("mAlgorithm");
            field.setAccessible(true);
            Object mAlgorithm = field.get(mClusterManager);
            List<MapItems> markers = new ArrayList<>(((Algorithm<MapItems>)mAlgorithm).getItems());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

Note :- In future if there any changes in field name(i.e. private Algorithm<T> mAlgorithm), change need to be done in business logic.

Vantassel answered 12/10, 2016 at 14:47 Comment(1)
this solution was great for meFardel
O
2

Ethan's answer is nice but you still can't get marker(s) with it. You need to create custom ClusterRenderer and use it to get Marker or ClusterItem.

   override fun onMapReady(map: GoogleMap) {
        clusterManager = ClusterManager(this, map)
        clusterRenderer = CustomClusterRenderer(this@ATMActivity, map, this)
        clusterManager.renderer = clusterRenderer
   }

   inner class CustomClusterRenderer(
        context: Context,
        map: GoogleMap,
        clusterManager: ClusterManager<CustomClusterItem>
) : DefaultClusterRenderer<CustomClusterItem>(context, map, clusterManager) 

And then you can get marker or cluster item from clusterRenderer

clusterRenderer.getMarker(clusterItem) // returns Marker object for selected cluster item 
clusterRenderer.getClusterItem(marker) // returns ClusterItem object for selected marker

It doesn't provide you whole collection of markers, but if you really still need it you should use loop to create it.

Oat answered 7/11, 2018 at 11:29 Comment(2)
This is by far the only thing that works for me, however it is not returning all markers. Nevertheless, a simple loop would do what OP wants. CheersBrazee
this solution works for me. My use case is I use cluster manager and I want to change the marker color every time it clicked.Averse
B
1

I had the same problem to get all the markers or at least to get the number of markers. I think I found the solution to get the markers.

I tried like you

markers = clusterManager.getMarkerCollection().getMarkers(); 

Try instead

markers = clusterManager.getClusterMarkerCollection().getMarkers();

This line returned me markers but I am not sure if it is the result that you/we want. I need to check this.

NOTE I would write my answer as a comment but I don't have enough point..

Bissell answered 25/7, 2014 at 14:18 Comment(2)
clusterManager.getClusterMarkerCollection().getMarkers(); returns markers only if the markers are at a zoom level so that we can see them in mapBirdlime
Yes, you'r right, I find out that it returns only the markers that appear and not the markers in the clustersBissell
H
0
You should tried something like this:
private MyItem mitemfilter;
renderer = new MyClusterManagerRender(this, googleMap, clusterManager);
clusterManager.setRenderer(renderer);

addItems();

    private void addItems() {
       clusterManager.addItem(mitemfilter=new  MyItem (19.641936, -99.132669, "food", "Villa Esmeralda, 54910 Fuentes del Valle, Méx.",R.drawable.ic_camaron));
        }

    private void foodfilter(){
    renderer.getMarker(mitemfilter).setVisible(false);
    
    }
Asumming that your model Myitem is defined.
This work for me trying to implementing filtering inside my cluster.
Huckaby answered 18/9, 2020 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.