Mapbox-gl-native: What is the best practice to replace a MGLShapeSource?

Created on 7 Feb 2017  路  8Comments  路  Source: mapbox/mapbox-gl-native

Platform: iOS 10.2
Mapbox SDK version: 3.4.1

After seeing so many changes to the Mapbox iOS SDK, especially runtime styling, what is the recommended way of replacing an MGLShapeSource?
I'm currently facing an issue with the following code:

func updateSource(shapes: [MGLShape]) {
  if let currentSource = mapView.style?.source(withIdentifier: sourceName) as? MGLShapeSource {
    mapView.style?.removeSource(currentSource)
  }

  let newSource = MGLShapeSource(identifier: sourceName, features: shapes, options: nil)
  mapView.style?.addSource(newSource)

  if mapView.style?.layer(withIdentifier: layerName) == nil {
    let layer = MGLSymbolStyleLayer(identifier: layerName, source: newSource)
    [...setting some layer properties...]
    mapView.style?.addLayer(layer)
  }
}

After calling the above method twice in a short period of time, the symbols don't show up on the map anymore.

iOS support

Most helpful comment

By the way: My current way of replacing shapes is the following but I'm not sure if it's the recommended way.

if let currentSource = mapView.style?.source(withIdentifier: sourceName) as? MGLShapeSource {
  let collection = MGLShapeCollectionFeature(shapes: newShapes)
  currentSource.shape = collection
}

All 8 comments

By the way: My current way of replacing shapes is the following but I'm not sure if it's the recommended way.

if let currentSource = mapView.style?.source(withIdentifier: sourceName) as? MGLShapeSource {
  let collection = MGLShapeCollectionFeature(shapes: newShapes)
  currentSource.shape = collection
}

After calling the above method twice in a short period of time, the symbols don't show up on the map anymore.

My current way of replacing shapes is the following but I'm not sure if it's the recommended way.

in updateSource it looks like you are removing the source and adding back again but only adding a layer with the original new source once. I think you could get this to work but you would need to remove the layer, too, and add it back with the updated (new) source each time.

Instead, I think you should be able to get the reference to the existing source currentSource and, instead of removing it, just update its shapes. There is an Objective-C example in the test iosapp that illustrates updating a shape source's shape property.

Beware that there is a fine line between a "shape" and a "feature" and it can be bothersome to work with in 3.4.x (ref https://github.com/mapbox/mapbox-gl-native/issues/7913)

Thanks @boundsj
So I think I'm doing it right (as shown in my previous comment by updating the shape property). I already realised that there is a difference between them but I couldn't see a difference between MGLShapeCollectionFeature and MGLShapeCollection. The implementation of both is the same

open var shapes: [MGLShape] { get }
public convenience init(shapes: [MGLShape])

The only difference is that MGLShapeCollectionFeature implements MGLFeature, but why? I don't see any use case for this.

Speaking of #7913: I also encountered this issue. Is this a bug of v3.4.x or desired behaviour?

The only difference is that MGLShapeCollectionFeature implements MGLFeature, but why? I don't see any use case for this.

You are working with GeoJSON. There is a distinction between a shape and a feature. The former is a geometry and the latter contains a geometry but also metadata (i.e. attributes in the Mapbox iOS SDK parlance). Attributes allow you to set properties that can later be used to do things like power data-driven styling (DDS) or the filter predicate API while pure shapes (with no attributes) can be visualized but cannot be affected by DDS or filters.

Speaking of #7913: I also encountered this issue. Is this a bug of v3.4.x or desired behaviour?

7913 is filed a s bug.

I'm actually not working with GeoJSON (my datasource is GeoJSON but it is parsed to Realm objects which then are reloaded to custom MGLFeature objects). I tried to subclass MGLPointFeature but it didn't work as expected, see #7949. So basically I have the following class:

class RestaurantPointFeature: MGLPointFeature {
  fileprivate let restaurant: Restaurant
  init(_ restaurant: Restaurant) {
    self.restaurant = restaurant
    super.init()
    self.coordinate = restaurant.coordinate
    self.attributes["id"] = restaurant.id
    ...
  }
}

An array of [RestaurantPointFeature] is then passed to the MGLShapeSource initializer. So my question remains: Why is the MGLShapeCollectionFeature also of type MGLFeature? Is there some use case where a collection of shapes need to have a coordinate?

I'm actually not working with GeoJSON

Sorry, I meant that you are working with the Mapbox iOS SDKs object which are an abstraction of GeoJSON. MGLShapeCollectionFeature implements the MGLFeature protocol and has an attributes property that allows you to use it with DDS and filter APIs. MGLShapeCollection does not.

Closing. We will continue track the issue of MGLShape / MGLFeature conversion in https://github.com/mapbox/mapbox-gl-native/issues/7913

Was this page helpful?
0 / 5 - 0 ratings