Android Google Maps Utils ClusterManager Fit All Clusters

Use LatLngBounds.Builder and hack DefaultClusterRenderer.

Assuming you are using Google Maps Utils Cluster Manager to display markers as clusters and you would like the map to display all the clusters within the map view.

Google Maps fit all markers

You could utilize the Google Maps way of fitting all markers within the map view (adjustng zoom and position).

// if builder doesn't include any item, an exception shall be thrownif (clusterItems.size() > 0) {   LatLngBounds.Builder builder = new LatLngBounds.Builder();  for (ClusterItem item : clusterItems) {    builder.include(item.getPosition());  }  LatLngBounds bounds = builder.build();  // padding in pixels  int padding = 20;  // use animateCamera if animation is required  map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));  if (clusterItems.size() == 1) {    // you might want to a custom zoom level if there is only 1 item    map.moveCamera(CameraUpdateFactory.zoomTo(15));  }}

If your markers or clusters are spread across the world (e.g. Japan, Europe & US), this method might not work very well as Google Maps can only show about half the world in its view.

Hack DefaultClusterRenderer

DefaultClusterRenderer does not expose most of its variables and functions (private only), so the only way to expose them is to copy the code and modify them with your own implementation.

Copy the code of DefaultClusterRenderer and rename the class as MyClusterRenderer. Add the following method to explose all the clusters.

public class MyClusterRenderer<T extends ClusterItem> implements ClusterRenderer<T> {    ...    public Collection<Cluster<T>> getClusters() {        return  mClusterToMarker.keySet();    }    ...}

Rather than looping all the markers to fit all clusters to be displayed in the map view, we shall loop through the clusters instead to get the LatLngBounds.

mapFragment.getMapAsync(new OnMapReadyCallback() {    @Override    public void onMapReady(final GoogleMap map) {        ClusterManager clusterManager = new ClusterManager(activity, map);        final MyClusterRenderer renderer = new NumberClusterRenderer(activity, map, clusterManager);        // use this to show cluster only without markers        renderer.setMinClusterSize(0);        clusterManager.setRenderer(renderer);        clusterManager.addItems(items);        // clusters are not loaded until MapLoadedCallback        map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {            @Override            public void onMapLoaded() {                Collection<Cluster> clusters = renderer.getClusters();                if (clusters.size() > 0) {                    LatLngBounds.Builder builder = new LatLngBounds.Builder();                    for (Cluster cluster : clusters) {                        builder.include(cluster.getPosition());                    }                    LatLngBounds bounds = builder.build();                    // use 50dip to cater for the cluster icon size                    float density = context.getResources().getDisplayMetrics().density;                    int padding = (int) (50 * density);                    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Helper.convertDpToPx(context, padding));                    map.moveCamera(cu);                }            }        });    }});    

Fit clusters and markers around the world

If the clusters are spread across the world, this method might not work very well as Google Maps can only show about half the world in its view.

First, we need to check if the map view can fit all the clusters. If it can't fit, we shall focus the map view on the largest cluster while maintaining zoom (most likely the widest zoom).

mapFragment.getMapAsync(new OnMapReadyCallback() {    @Override    public void onMapReady(final GoogleMap map) {        ClusterManager clusterManager = new ClusterManager(activity, map);        final MyClusterRenderer renderer = new NumberClusterRenderer(activity, map, clusterManager);        // use this to show cluster only without markers        renderer.setMinClusterSize(0);        clusterManager.setRenderer(renderer);        clusterManager.addItems(items);        // clusters are not loaded until MapLoadedCallback        map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {            @Override            public void onMapLoaded() {                Collection<Cluster> clusters = renderer.getClusters();                if (clusters.size() > 0) {                    LatLngBounds.Builder builder = new LatLngBounds.Builder();                    for (Cluster cluster : clusters) {                        builder.include(cluster.getPosition());                    }                    LatLngBounds bounds = builder.build();                    // use 50dip to cater for the cluster icon size                    float density = context.getResources().getDisplayMetrics().density;                    int padding = (int) (50 * density);                    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Helper.convertDpToPx(context, padding));                    map.moveCamera(cu);                    // check can fit all the clusters                    LatLngBounds mapBounds = map.getProjection().getVisibleRegion().latLngBounds;                    if (mapBounds.contains(bounds.northeast) && mapBounds.contains(bounds.southwest)) {                        Log.d(TAG, "Fit OK");                    }                    else {                        Log.d(TAG, "Fit FAIL");                        // loop all to find the biggest cluster                        Cluster maxCluster = null;                        for (Cluster cluster : clusters) {                            if (maxCluster == null || cluster.getSize() > maxCluster.getSize()) {                                maxCluster = cluster;                            }                        }                        // focus on biggest cluster                        if (maxCluster != null) {                            cu = CameraUpdateFactory.newLatLng(maxCluster.getPosition());                            map.moveCamera(cu);                        }                    }                }            }        });    }});    

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.