Heatmaps

Select platform: Android iOS JavaScript

This page describes the heatmap utility that's available in the utility library for the Maps SDK for iOS. Heatmaps are useful for representing the distribution and density of data points on a map.

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 library, follow the setup guide before reading the rest of this page.

Adding 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 GMUHeatmapTileLayer instance, setting the map property to a GMSMapView. Do this in your app's viewDidLoad() function, to ensure that the base map is loaded before working with the heatmap. Then pass a collection of GMUWeightedLatLng objects to the GMUHeatmapTileLayer instance.

The utility supplies the GMUHeatmapTileLayer class, which accepts a collection of GMUWeightedLatLng objects. It creates the tile images for various zoom levels, based on the radius, gradient and opacity options supplied.

Looking at the steps in more detail:

  1. Create a GMUHeatmapTileLayer instance, setting the map property to a GMSMapView (do this in your app's viewDidLoad() function).
  2. Pass a collection of GMUWeightedLatLng objects to the GMUHeatmapTileLayer instance.
  3. Call GMUHeatmapTileLayer.map, passing the map view.

    Swift

    class Heatmap: UIViewController {
    
      private var mapView: GMSMapView!
      private var heatmapLayer: GMUHeatmapTileLayer!
    
      override func viewDidLoad() {
        super.viewDidLoad()
        heatmapLayer = GMUHeatmapTileLayer()
        heatmapLayer.map = mapView
      }
    
      // ...
    
      func addHeatmap() {
    
        // Get the data: latitude/longitude positions of police stations.
        guard let path = Bundle.main.url(forResource: "police_stations", withExtension: "json") else {
          return
        }
        guard let data = try? Data(contentsOf: path) else {
          return
        }
        guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
          return
        }
        guard let object = json as? [[String: Any]] else {
          print("Could not read the JSON.")
          return
        }
    
        var list = [GMUWeightedLatLng]()
        for item in object {
          let lat = item["lat"] as! CLLocationDegrees
          let lng = item["lng"] as! CLLocationDegrees
          let coords = GMUWeightedLatLng(
            coordinate: CLLocationCoordinate2DMake(lat, lng),
            intensity: 1.0
          )
          list.append(coords)
        }
    
        // Add the latlngs to the heatmap layer.
        heatmapLayer.weightedData = list
      }
    }
          

    Objective-C

    @implementation Heatmap {
      GMSMapView *_mapView;
      GMUHeatmapTileLayer *_heatmapLayer;
    }
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      _heatmapLayer = [[GMUHeatmapTileLayer alloc] init];
      _heatmapLayer.map = _mapView;
    }
    
    // ...
    
    - (void) addHeatmap {
    
      // Get the data: latitude/longitude positions of police stations.
      NSURL *path = [NSBundle.mainBundle URLForResource:@"police_stations" withExtension:@"json"];
      NSData *data = [NSData dataWithContentsOfURL:path];
      NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    
      NSMutableArray<GMUWeightedLatLng *> *list = [[NSMutableArray alloc] init];
      [json enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSDictionary *item = (NSDictionary *)obj;
        CLLocationDegrees lat = [(NSNumber *) [item valueForKey:@"lat"] doubleValue];
        CLLocationDegrees lng = [(NSNumber *) [item valueForKey:@"lng"] doubleValue];
        GMUWeightedLatLng *coords = [[GMUWeightedLatLng alloc] initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
                                                                        intensity:1.0];
        [list addObject:coords];
      }];
    
    
      // Add the latlngs to the heatmap layer.
      _heatmapLayer.weightedData = list;
    }
    @end
          

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 }
]

Customizing the heatmap

The heatmap has several customizable properties. You can set the options when initially creating the GMUHeatmapTileLayer instance, or at any time by setting a new value for the option.

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 GMUHeatmapTileLayer.radius to set the radius.

  2. Gradient: A range of colors that the heatmap uses to generate its color map, ranging from lowest to highest intensity. A GMUGradient is created using 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. The colorMapSize parameter defines the number of steps in the gradient. Larger numbers will result in a smoother gradient, while smaller numbers will give sharper transitions similar to a contour graph. Use GMUHeatmapTileLayer.gradient to set the gradient.

  3. Opacity: This is the opacity of the entire heatmap layer, and ranges from 0 to 1. The default is 0.7. Use GMUHeatmapTileLayer.opacity to set the opacity value.

For example, create a Gradient:

Swift

let gradientColors: [UIColor] = [.green, .red]
let gradientStartPoints: [NSNumber] = [0.2, 1.0]
heatmapLayer.gradient = GMUGradient(
  colors: gradientColors,
  startPoints: gradientStartPoints,
  colorMapSize: 256
)
      

Objective-C

NSArray<UIColor *> *gradientColors = @[UIColor.greenColor, UIColor.redColor];
NSArray<NSNumber *> *gradientStartPoints = @[@0.2, @1.0];
_heatmapLayer.gradient = [[GMUGradient alloc] initWithColors:gradientColors
                                                 startPoints:gradientStartPoints
                                                colorMapSize:256];
      

To change the opacity of an existing heatmap:

Swift

heatmapLayer.opacity = 0.7
      

Objective-C

_heatmapLayer.opacity = 0.7;
      

Update an existing option

To update an option that has already been set, take these steps:

  1. Update the option to the desired value.
  2. Call GMUHeatmapTileLayer.clearTileCache().

Changing the dataset

To change the dataset upon which a heatmap is built:

  1. Update your data collection. Use GMUHeatmapTileLayer.weightedData, passing an array of GMUWeightedLatLng.
  2. Call GMUHeatmapTileLayer.clearTileCache().

Removing a heatmap

To remove a heatmap, call GMUHeatmapTileLayer.map, passing nil.

Swift

heatmapLayer.map = nil
      

Objective-C

_heatmapLayer.map = nil;
      

See the demo app

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