How to do clustering with Google Maps in iOS
Tags: cluster, googlemaps, ios, swift
Published: 2019-04-08 21:55:14

---
title: How to do clustering with Google Maps in iOS
published: true
description: 
tags: cluster,google-maps,ios,swift
---

## Basic with Google Maps
Add to Podfile `pod 'GoogleMaps'`

Add a custom marker

```swift
import GoogleMaps

final class StopMarker: GMSMarker {
    let stop: Stop

    init(stop: Stop) {
        self.stop = stop
        super.init()
        self.title = stop.name
        self.position = stop.toCoordinate()
        
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        imageView.layer.cornerRadius = 15
        imageView.image = UIImage(named: "pin")
        self.iconView = imageView
    }
}
```

Show markers

```swift
func handle(stops: [Stop]) {
    self.stops = stops
    stops
        .map({ StopMarker(stop: $0) })
        .forEach {
            $0.map = mapView
        }
   
}
```

Assigning `$0.map = mapView` means telling `GMSMapView` to start rendering markers

Handle tap

```swift
extension ViewController: GMSMapViewDelegate {
    func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
        zoomIn(coordinate: marker.position)
        return false
    }
    
    func mapView(_ mapView: GMSMapView, didTapInfoWindowOf marker: GMSMarker) {
        guard let stopMarker = marker as? StopMarker else {
            return
        }
        
        let detailViewController = StopDetailViewController(stop: stopMarker.stop)
        presentPanModal(detailViewController)
    }
}
```

## Clustering
Add google-maps-ios-utils manually by following https://github.com/googlemaps/google-maps-ios-utils/blob/master/Swift.md

Otherwise, with CocoaPods, we get error

```
[!] The 'Pods-MyApp' target has transitive dependencies that include static binaries: (/Users/khoa/Projects/MyApp/Pods/GoogleMaps/Base/Frameworks/GoogleMapsBase.framework, /Users/khoa/Projects/MyApp/Pods/GoogleMaps/Maps/Frameworks/GoogleMaps.framework, and /Users/khoa/Projects/MyApp/Pods/GoogleMaps/Maps/Frameworks/GoogleMapsCore.framework)
```

Add `ClusterItem`

```swift
import Foundation
import CoreLocation

class ClusterItem: NSObject, GMUClusterItem {
    let position: CLLocationCoordinate2D
    let stop: Stop
    
    init(stop: Stop) {
        self.stop = stop
        self.position = stop.toCoordinate()
    }
}
```

Set up cluster manager

```swift
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
clusterManager.setDelegate(self, mapDelegate: self)
```

Default algorithm `GMUNonHierarchicalDistanceBasedAlgorithm`

```
 A simple clustering algorithm with O(nlog n) performance. Resulting clusters are not
 * hierarchical.
 * High level algorithm:
 * 1. Iterate over items in the order they were added (candidate clusters).
 * 2. Create a cluster with the center of the item.
 * 3. Add all items that are within a certain distance to the cluster.
 * 4. Move any items out of an existing cluster if they are closer to another cluster.
 * 5. Remove those items from the list of candidate clusters.
 * Clusters have the center of the first element (not the centroid of the items within it).
```


Make `ClusterItem` and add to `clusterManager`. In the end, call `clusterManager.cluster()`

```swift
func handle(stops: [Stop]) {
    self.stops = stops
    stops
        .map({ StopMarker(stop: $0) })
        .forEach {
            let item = ClusterItem(stop: $0.stop)
            clusterManager.add(item)
        }
    
    clusterManager.cluster()
}
```

By default, clusterManager uses renderer to render default pin marker. Implement `GMUClusterRendererDelegate` to use our custom `StopMarker`

```swift
extension ViewController: GMUClusterRendererDelegate {
    func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
        switch object {
        case let clusterItem as ClusterItem:
            return StopMarker(stop: clusterItem.stop)
        default:
            return nil
        }
    }
}
```

Finally handle tapping on cluster and cluster item

```swift
extension ViewController: GMUClusterManagerDelegate {
    func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
        print("tap cluster")
        return false
    }
    
    func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool {
        print("tap cluster item")
        return false
    }
}
```

Original post https://github.com/onmyway133/blog/issues/191