9
votes

I'm trying to implement clustering in Mapbox for iOS and literally using this example from Mapbox website

It works fine, but I'd like to be able to use simple MGLAnnotations to place on the map and cluster them together if they are too close.

I read here that MGLShapeSource not only accepts external geoJSON, but other sources, such as polylines and annotations. But when I feed it with annotations array no clustering occur, I just see bunch of my markers from my annotations array:

let source = MGLShapeSource(identifier: "clusteredParkings", shapes: annotationsArray, options: [.clustered: true, .clusterRadius: 20])

When I swap the source back to geoJSON everything works again for clusters. Btw there are no errors or warnings.

What am I doing wrong? Does anyone have a working example of Mapbox clustering with MGLAnnotations rather than geoJSON source file?

https://www.mapbox.com/ios-sdk/api/3.6.0/Classes/MGLShapeSource.html

2

2 Answers

5
votes

I did some research on this not too long ago and it didn't seem to be possible on iOS. Here is the suggestion on github that is still open. Here is another issue about how it wasn't mentioned in the documentation but has since been added.

2
votes

Does anyone have a working example of Mapbox clustering with MGLAnnotations rather than geoJSON source file?

Given a Style

let style: MGLStyle

1) Build/Get your [MGLPointFeature]

2) Build a MGLShapeSource

let source = MGLShapeSource(identifier: "YOUR_IDENTIFIER_A", features: YOUR_ MGLPointFeature_ARRAY, options: [.clustered: true, .clusterRadius: YOUR_WIDTH])
style.addSource(source)

3) Build a style for the marker when it's not clustered

let markerLayer = MGLSymbolStyleLayer(identifier: "YOUR_IDENTIFIER_B", source: source)
    markerLayer.iconImageName = NSExpression(forConstantValue: "MARKER_IDENTIFIER")
    markerLayer.predicate = NSPredicate(format: "cluster != YES")
style.setImage(UIImage(named: "MARKER_IMAGE")!, forName: "MARKER_IDENTIFIER")

4) Build a style for the cluster

let clusterLayer = MGLSymbolStyleLayer(identifier: "YOUR_IDENTIFIER_C", source: source)
    clusterLayer.textColor = NSExpression(forConstantValue: UIColor.white)
    clusterLayer.textFontSize = NSExpression(forConstantValue: NSNumber(value: Double(YOUR_WIDTH) / 2.5))
    clusterLayer.iconAllowsOverlap = NSExpression(forConstantValue: true)
    clusterLayer.textOffset = NSExpression(forConstantValue: CGVector(dx: 0, dy: -0.2))
    clusterLayer.predicate = NSPredicate(format: "cluster == YES")
    style.setImage(UIImage(named: "CLUSTER_IMAGE")!, forName: "CLUSTER_IDENTIFIER")

5) You can use Stops features. This will let you change cluster image based on clustering count change.

let stops = [
        10: NSExpression(forConstantValue: "CLUSTER_IMAGE"),
        50: NSExpression(forConstantValue: "ANOTHER_CLUSTER_IMAGE")
    ]

6) Use expressions to set each cluster's image based on defined stops and display the point count over the corresponding image

let defaultShape = NSExpression(forConstantValue: "CLUSTER_IDENTIFIER")
    clusterLayer.iconImageName = NSExpression(format: "mgl_step:from:stops:(point_count, %@, %@)", defaultShape, stops)
    clusterLayer.text = NSExpression(format: "CAST(point_count, 'NSString')")

7) Add layers to the Style

style.addLayer(markerLayer)
style.addLayer(clusterLayer)