SDK for Android Developer's Guide

Marker Clustering

You can use marker clustering to reduce the visual overload caused by too many markers being displayed on the map at once. With this feature markers that are close together are automatically replaced by numbered cluster markers to indicate that multiple map markers are represented. For better user experience, the replacement algorithm depends on the device pixel density. Therefore you can observe different behavior on different devices.

Figure 1. Cluster Markers

Showing Cluster Markers

You can enable cluster markers on a map by using a ClusterLayer and adding map markers to it. All markers that are on a layer are automatically clustered based on a grid-based clustering algorithm that depends on the map zoom level.

The following steps demonstrate how to use the ClusterLayer class:

  1. Create map markers as normal:
    MapMarker mm = new MapMarker();
    mm.setIcon(myImage);
    mm.setCoordinate(new GeoCoordinate(52.53,13.23));
    
  2. Create a ClusterLayer object.
    ClusterLayer cl = new ClusterLayer();
  3. Add markers to the cluster layer instead of the map directly. You can also add a Collection of MapMarker instead of just adding a single marker.
    cl.addMarker(mm);
  4. Add the cluster layer to the map.
    mMap.addClusterLayer(cl);
    Note: The order of these two steps is not important. You can also add the cluster layer to the map first and add markers to it afterwards.
  5. To remove a marker or collection or markers from the cluster layer again call:
    cl.removeMarker(mm);

You can also retrieve all markers on a cluster layer with the ClusterLayer.getMarkers() method. This is useful if you would like to remove all markers by using the removeMarkers(Collection<MapMarker>) method.

Theming

You can customize clusters by assigning a ClusterTheme object to the ClusterLayer. Every theme consists of several styles, and a cluster style defines the look of marker cluster objects at a particular density. Cluster density is the amount of markers represented by a cluster.

Figure 2. Marker Cluster with Density of 7

There are three available cluster styles you can use with a ClusterTheme:

  • Default cluster style - the predefined markers behavior. This is the default style used if you do not set a theme. It is also used for ranges not covered by your own theme.
  • BasicClusterStyle - similar to the default style but you can change the fill color, text color, and stroke color for the markers.
  • ImageClusterStyle - use your own bitmap image as a marker.

To set a style use the setStyleForDensityRange(int, int, ClusterStyle) or setStyleForDensityRange(ClusterDensityRange, ClusterStyle) methods in ClusterTheme. For example, if you want red for all clusters between density 10 to 19, and green for 20 to 49, and the default blue for all other cases, you can use BasicClusterStyle as follows:

  1. Create a style with a red circle and a style with a green one:
    BasicClusterStyle redStyle = new BasicClusterStyle();
    redStyle.setFillColor(Color.RED);
    BasicClusterStyle greenStyle = new BasicClusterStyle();
    greenStyle.setFillColor(Color.GREEN);
    
  2. Create a new theme and add those styles to the theme with defining the density ranges they should be used for:
    ClusterTheme theme = new ClusterTheme();
    theme.setStyleForDensityRange(10, 19, redStyle);
    theme.setStyleForDensityRange(20, 49, greenStyle);
    
    Instead of setting the integer values directly you can also make use of the ClusterDensityRange class.
    Note: Do not overlap density ranges. Overlapping ranges causes InvalidArgumentException.
  3. Finally, add this theme to the cluster layer you use:
    cl.setTheme(theme);

To use your own image as a cluster icon, set an Image to an ImageClusterStyle instance before setting the style to the cluster theme. For example:

Image img = new Image();
try {
 img.setImageResource(R.drawable.banner);
} catch (IOException e) {
 e.printStackTrace();
}
ImageClusterStyle imageCluster = new ImageClusterStyle(img);

ClusterTheme theme = new ClusterTheme();
theme.setStyleForDensityRange(2, 9, imageCluster);
cl.setTheme(theme);

Although you can only set one theme per layer, you can mix styles for different densities in a single theme. For example, you can set a BasicClusterStyle from density of 10 to 19 and an ImageClusterStyle from 20 to 30. The default theme applies for all other densities not covered by the custom themes.

Cluster Marker Events

Cluster markers are similar to normal markers on the map. You can also use map object gesture listeners in a similar manner as normal map markers. For example:

  1. Add a gesture listener to the map via:
    mMapFragment.getMapGesture().addOnGestureListener(mMyGestureHandler);
  2. Next, listen for
    public boolean onMapObjectsSelected(List<ViewObject> viewObjects)
    to get the map click event.
  3. Iterate over the ViewObjects and check for type PROXY_OBJECT and sub-type CLUSTER_MARKER. Alternatively, you can also use the instanceof keyword.
    @Override
    public boolean onMapObjectsSelected(List<ViewObject> viewObjects) {
    for (ViewObject obj : viewObjects){
    if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT){
      if (proxyObj.getType() == MapProxyObject.Type.CLUSTER_MARKER) {
        ClusterViewObject cv = (ClusterViewObject) proxyObj;
        Log.i(TAG, "Cluster clicked: markers#"+cv.getMarkers().size());
        return true;
      }
    }
    return false;
    }

Working with Clusters

HERE SDK also provides a few other ways for you to interact with marker clusters. You can get all markers inside one specific cluster by using the ClusterViewObject, which is a proxy object representing a cluster. For example:

Collection<MapMarker> ClusterViewObject.getMarkers()

You can also retrieve the bounding box around all markers that are in a cluster marker by calling the following:

BoundingBox ClusterViewObject.getBoundingBox();