Android: Google Maps Clustering

https://miro.medium.com/max/706/1*XWr2MFkZFxM0tZzm0OqXpw.png

Android is the fastest growing mobile operating system based on the Linux kernel, and having its own Java implementation from Google. It’s the brainchild of the search giant, which not only has released an open source platform, but also created the Open Handset Alliance, through which we have SDK at our disposal. Google has adapted its many services under the mobile platform, and Google Maps being among them. Just what we will be talking about today.

Location data plays a big role in building a great mobile app experience. Maps are immersible and users are increasingly demanding of a highly-responsive experience.Android Google Maps APIv2 is quite topical at the moment. I think it’s needless to talk about how to connect the service, get the keys and start to work — you can easily find such information on the Internet. So we’ll go straight to the point. When designing the project the task was to display the map markers, which are divided into several types (with help of Google Maps markers clustering). But, for example, nearby markers when zoomed out overlapped the way it all looked like one continuous blur. They were to be grouped — clustered — with regard to the type of each marker. Standard toolkit looked poorly for solving such problems. Of course, you could create your own class that inherits markers, and extending it at the same time. But why reinvent the wheel if a similar problem could have being solved before you. After a careful search we found two libraries designed if not completely, then to a great extent to simplify our lives without reading huge tutorial.

Many developers, when building location-based applications that use Google Maps, face the problem of displaying a large number of different types of markers.
Today we’ll learn how:

  • to use clustering
  • to create custom icons for clusters and markers
  • to detect cluster marker click
  • to create a custom popup on a marker click

Let’s get started.

Project Setup

Add the following dependencies in your build.gradle (Module: app):

dependencies{
...
implementation 'com.google.android.gms:play-services-maps:16.0.0'
}

Create a new project from console.developers.google.com and enable Google maps API key

Sync Gradle and you’re good to go.

Implement

And we initialize in our onCreate method as follows:

SupportMapFragment supportMapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.googleMap);
supportMapFragment.getMapAsync(this);

XML File

<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.github.tonyshkurenko.clustermanagerdemo.MapsActivity"
/>

Clustering

  • Add the dependency
  • Implement the ClusterItem interface
  • Inherit from DefaultClusterRenderer to explain how we want to render i.e. minimum number of markers to cluster
  • Use the ClusterManager in our MainActivity to achieve the needed result
dependencies{
...
implementation 'com.google.maps.android:android-maps-utils:0.5+'
}

Implement ClusterItem Interface

In order to cluster markers, we use ClusterItem. They act as our markers. A ClusterItem has three methods to implement:

  • getPosition: Returns actual item position as LatLng
  • getTitle: Returns title of the marker/item
  • getSnippet: Additional text that gets displayed below the title

We’re going to create a new class named MarkerClusterItem and implement the interface as follows:

public class MarkerClusterItem implements ClusterItem {

private LatLng latLng;
private String title;

public MarkerClusterItem(LatLng latLng, String title){
this.latLng = latLng;
this.title = title;
}

@Override
public LatLng getPosition() {
return latLng;
}

@Override
public String getTitle() {
return title;
}

@Override
public String getSnippet() {
return "";
}
}

Defining a Cluster Renderer

We have a class called DefaultClusterRenderer, which, from its name, renders the clusters based on default values. If, for whatever reason, you want to change the values such as cluster size, then you have to inherit from the class override whatever it is that you want.

public class MarkerClusterRenderer<T extends ClusterItem> extends DefaultClusterRenderer<T> {

public MarkerClusterRenderer(Context context, GoogleMap googleMap, ClusterManager<T> clusterManager){
super(context, googleMap, clusterManager);
}

@Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
return cluster.getSize() >= 1;
}
}

In short, DefaultClusterRenderer is a generic class, and T must be of type ClusterItem. So when we create our class, the generic T must be of type ClusterItem. That’s why we have <T extends ClusterItem>. So when create a new MarkerClusterRenderer , we pass it any class that implements the interface ClusterItem, that is, of type ClusterItem.

Below is an example of how to create an instance of MarkerClusterRenderer:

MarkerClusterRenderer<MarkerClusterItem> clusterRenderer = new MarkerClusterRenderer<>(this, googleMap, clusterManager);

Finally we are ready to start clustering the markers.

We need to:

  • Create an instance of ClusterManager
  • Add markers/cluster items
  • Set the renderer
  • Cluster the markers

We’re going to declare the variable in our Activity:

private ClusterManager<MarkerClusterItem> clusterManager;clusterManager = new ClusterManager<>(this, googleMap);private void addClusterItems() {
for(MarkerOptions markerOptions : listMarkers){
MarkerClusterItem clusterItem = new MarkerClusterItem(markerOptions.getPosition(), markerOptions.getTitle());
clusterManager.addItem(clusterItem);
}
}

Set Renderer

Now we need to set our custom renderer that we’ve created (MarkerClusterRenderer):

private void setRenderer() {
MarkerClusterRenderer<MarkerClusterItem> clusterRenderer = new MarkerClusterRenderer<>(this, googleMap, clusterManager);
clusterManager.setRenderer(clusterRenderer);
}

Add markers:

or (int i = 0; i < 10; i++) {
final LatLng latLng = new LatLng(-34 + i, 151 + i);
mClusterManager.addItem(new StringClusterItem("Marker #" + (i + 1), latLng));
}
mClusterManager.cluster();

To change the basic icons for markers and clusters,

onBeforeClusterItemRendered is invoked before rendering a conventional marker.

Add the code below to the method:

final BitmapDescriptor markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE);markerOptions.icon(markerDescriptor).snippet(item.title);

onBeforeClusterRendered is invoked before rendering a cluster (cluster is just a simple marker under the hood).

Add a field to a renderer class:

private final IconGenerator mClusterIconGenerator;// in constructor
mClusterIconGenerator = new IconGenerator(mContext.getApplicationContext());

Bigs Thanks:

Thanks for reading, please feel free to comment with this post.

--

--

--

🎓 A true Software Engineer aspires to build a strong community and help other people grow up.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

What’s in a NumPy Array?

Understand JVM and JIT Compiler — Part 2

Let’s Git Started . . .

How to use .includes? in Rails 7

The Formulation Of Quotely

Testing Times:

What’s new in Rails 7

My experience at LGM-VIP 2021 web internship

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Hau NGUYEN (Leo)

Hau NGUYEN (Leo)

🎓 A true Software Engineer aspires to build a strong community and help other people grow up.

More from Medium

👨🏼‍💻HMS ML Kit Text Classification ( News Classification — Kotlin)

google maps display using android java

How to upload your first library to Open Source

Handling the toString() method response in Java — Android