Google Maps Android Heatmap Utility

Select platform: Android iOS JavaScript

Heatmaps are useful for representing the distribution and density of data points on a map.

Introduction

The Maps SDK for Android Utility Library includes a heatmap utility, which you can use to add one or more heatmaps to a Google map in your application.

This video discusses the use of heatmaps as an alternative to markers, when your data requires a large number of data points on the map.

Heatmaps make it easy for viewers to understand the distribution and relative intensity of data points on a map. Rather than placing a marker at each location, heatmaps use color to represent the distribution of the data.

In the example below, red represents areas of high concentration of police stations in Victoria, Australia.

A map with a heatmap showing location of police stations
A heatmap on a map

If you haven't yet set up the Maps SDK for Android Utility library, follow the setup guide before reading the rest of this page.

Add a simple heatmap

To add a heatmap to your map, you will need a dataset consisting of the coordinates for each location of interest. First create a HeatmapTileProvider, passing it the collection of LatLng objects. Then create a new TileOverlay, passing it the heatmap tile provider, and add the tile overlay to the map.

The utility supplies the HeatmapTileProvider class, which implements the TileProvider interface to supply the tile images for the heatmap. HeatmapTileProvider accepts a collection of LatLng objects (or WeightedLatLng objects, as described below). It creates the tile images for various zoom levels, based on the radius, gradient and opacity options supplied. You can change the default values for these options.

Looking at the steps in more detail:

  1. Use HeatmapTileProvider.Builder(), passing it a collection of LatLng objects, to add a new HeatmapTileProvider.
  2. Create a new TileOverlayOptions object with the relevant options, including the HeatmapTileProvider.
  3. Call GoogleMap.addTileOverlay() to add the overlay to the map.

Kotlin

private fun addHeatMap() {
    var latLngs: List<LatLng?>? = null

    // Get the data: latitude/longitude positions of police stations.
    try {
        latLngs = readItems(R.raw.police_stations)
    } catch (e: JSONException) {
        Toast.makeText(context, "Problem reading list of locations.", Toast.LENGTH_LONG)
            .show()
    }

    // Create a heat map tile provider, passing it the latlngs of the police stations.
    val provider = HeatmapTileProvider.Builder()
        .data(latLngs)
        .build()

    // Add a tile overlay to the map, using the heat map tile provider.
    val overlay = map.addTileOverlay(TileOverlayOptions().tileProvider(provider))
}

@Throws(JSONException::class)
private fun readItems(@RawRes resource: Int): List<LatLng?> {
    val result: MutableList<LatLng?> = ArrayList()
    val inputStream = context.resources.openRawResource(resource)
    val json = Scanner(inputStream).useDelimiter("\\A").next()
    val array = JSONArray(json)
    for (i in 0 until array.length()) {
        val `object` = array.getJSONObject(i)
        val lat = `object`.getDouble("lat")
        val lng = `object`.getDouble("lng")
        result.add(LatLng(lat, lng))
    }
    return result
}

      

Java

private void addHeatMap() {
    List<LatLng> latLngs = null;

    // Get the data: latitude/longitude positions of police stations.
    try {
        latLngs = readItems(R.raw.police_stations);
    } catch (JSONException e) {
        Toast.makeText(context, "Problem reading list of locations.", Toast.LENGTH_LONG).show();
    }

    // Create a heat map tile provider, passing it the latlngs of the police stations.
    HeatmapTileProvider provider = new HeatmapTileProvider.Builder()
        .data(latLngs)
        .build();

    // Add a tile overlay to the map, using the heat map tile provider.
    TileOverlay overlay = map.addTileOverlay(new TileOverlayOptions().tileProvider(provider));
}

private List<LatLng> readItems(@RawRes int resource) throws JSONException {
    List<LatLng> result = new ArrayList<>();
    InputStream inputStream = context.getResources().openRawResource(resource);
    String json = new Scanner(inputStream).useDelimiter("\\A").next();
    JSONArray array = new JSONArray(json);
    for (int i = 0; i < array.length(); i++) {
        JSONObject object = array.getJSONObject(i);
        double lat = object.getDouble("lat");
        double lng = object.getDouble("lng");
        result.add(new LatLng(lat, lng));
    }
    return result;
}

      

For this example, the data is stored in a JSON file, police_stations.json. Here is an extract from the file:

[
{"lat" : -37.1886, "lng" : 145.708 } ,
{"lat" : -37.8361, "lng" : 144.845 } ,
{"lat" : -38.4034, "lng" : 144.192 } ,
{"lat" : -38.7597, "lng" : 143.67 } ,
{"lat" : -36.9672, "lng" : 141.083 }
]

Use weighted latitude/longitude points

When creating a HeatmapTileProvider, you can pass it a collection of weighted latitude/longitude coordinates. This is useful if you want to illustrate the importance of a particular set of locations.

To apply weighting to specific locations:

  1. Create a new WeightedLatLng for each location that requires weighting. Pass in the LatLng and a double representing the intensity required. The intensity indicates the relative importance, or value, of this location. A higher value will result in a higher-intensity color in the heatmap gradient. By default, the highest-intensity color is red.
  2. Call HeatmapTileProvider.Builder().weightedData(), instead of HeatmapTileProvider.Builder().data(), to create the heatmap.

Customize the heatmap

A number of properties of the heatmap are customizable. You can set the options at time of creation, via Builder functions. Alternatively, change an option at any time by calling the relevant setter on the HeatmapTileProvider, and then clear the overlay's tile cache so that it redraws all the tiles with the new options.

The following options are available:

  1. Radius: The size of the Gaussian blur applied to the heatmap, expressed in pixels. The default is 20. Must be between 10 and 50. Use the Builder's radius() to set the value when creating the heatmap, or change the value later with setRadius().
  2. Gradient: A range of colors that the heatmap uses to generate its color map, ranging from lowest to highest intensity. A gradient is created using two arrays: an integer array containing the colors, and a float array indicating the starting point for each color, given as a percentage of the maximum intensity, and expressed as a fraction from 0 to 1. You need to specify only one color for a single-colored gradient, or a minimum of two colors for a multi-colored gradient. The color map is generated using interpolation between those colors. The default gradient has two colors. Use the Builder's gradient() to set the value when creating the heatmap, or change the value later with setGradient().
  3. Opacity: This is the opacity of the entire heatmap layer, and ranges from 0 to 1. The default is 0.7. Use the Builder's opacity() to set the value when creating the heatmap, or change the value later with setOpacity().

For example, create a Gradient to set the gradient before adding the heatmap:

Kotlin

// Create the gradient.
val colors = intArrayOf(
    Color.rgb(102, 225, 0),  // green
    Color.rgb(255, 0, 0) // red
)
val startPoints = floatArrayOf(0.2f, 1f)
val gradient = Gradient(colors, startPoints)

// Create the tile provider.
val provider = HeatmapTileProvider.Builder()
    .data(latLngs)
    .gradient(gradient)
    .build()

// Add the tile overlay to the map.
val tileOverlay = map.addTileOverlay(
    TileOverlayOptions()
        .tileProvider(provider)
)

      

Java

// Create the gradient.
int[] colors = {
    Color.rgb(102, 225, 0), // green
    Color.rgb(255, 0, 0)    // red
};

float[] startPoints = {
    0.2f, 1f
};

Gradient gradient = new Gradient(colors, startPoints);

// Create the tile provider.
HeatmapTileProvider provider = new HeatmapTileProvider.Builder()
    .data(latLngs)
    .gradient(gradient)
    .build();

// Add the tile overlay to the map.
TileOverlay tileOverlay = map.addTileOverlay(new TileOverlayOptions().tileProvider(provider));

      

To change the opacity of an existing heatmap:

Kotlin

provider.setOpacity(0.7)
tileOverlay?.clearTileCache()

      

Java

provider.setOpacity(0.7);
tileOverlay.clearTileCache();

      

Change the dataset

To change the dataset upon which a heatmap is built, use HeatmapTileProvider.setData(), or HeatmapTileProvider.setWeightedData() for WeightedLatLng points. Note: If you want to add points to the heatmap, or remove points from the heatmap, update your data collection and then use setData() or setWeightedData().

Kotlin

val data: List<WeightedLatLng> = ArrayList()
provider.setWeightedData(data)
tileOverlay?.clearTileCache()

      

Java

List<WeightedLatLng> data = new ArrayList<>();
provider.setWeightedData(data);
tileOverlay.clearTileCache();

      

Remove a heatmap

To remove the heatmap, you need to remove the tile overlay:

Kotlin

tileOverlay?.remove()

      

Java

tileOverlay.remove();

      

See the demo app

For another example of a heatmap implementation, take a look at the HeatmapsDemoActivity in the demo app that ships with the utility library. The setup guide shows you how to run the demo app.