Filter symbols based on pitch and distance
This example demonstrates how to use pitch
and distance-from-center
expressions in the filter field of a SymbolLayer
within the Mapbox Maps SDK for iOS. The purpose is to remove large-sized points of interest (POI) labels in the far distance when the map is at a high pitch angle, reallocating screen space for displaying smaller road and street labels efficiently.
In the implementation, the updateFilter
method adds an additional condition to the existing filter based on the pitch
and distance-from-center
values. By setting these conditions, the symbols are displayed differently based on the pitch angle and proximity to the camera, ensuring the visibility of POI labels is optimized. The setPitchDistanceFilter
function is used to apply this filter logic to specific layers like poi-label
and transit-label
on the map.
This example code is part of the Maps SDK for iOS Examples App, a working iOS project available on Github. iOS developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.
See our Run the Maps SDK for iOS Examples App tutorial for step-by-step instructions.
// Use pitch and distance-from-center expressions in the filter
// field of a symbol layer to remove large size POI labels in the far
// distance at high pitch, freeing up that screen real-estate for smaller road and street labels.
import UIKit
import MapboxMaps
final class ViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancelable>()
override func viewDidLoad() {
super.viewDidLoad()
let mapInitOptions = MapInitOptions(
cameraOptions: CameraOptions(
center: CLLocationCoordinate2D(
latitude: 38.888,
longitude: -77.01866),
zoom: 15,
pitch: 75),
styleURI: StyleURI.streets)
mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.ornaments.options.scaleBar.visibility = .visible
view.addSubview(mapView)
// Wait for the map to load its style before setting the filter.
mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
self?.setPitchDistanceFilter()
}.store(in: &cancelables)
}
// Add an additional condition to the current filter
// to filter based on ["pitch"] and ["distance-from-center"]
func updateFilter(currentFilter: Exp) -> Exp {
let updatedFilter = Exp(.all) {
currentFilter
Exp(.switchCase) {
// Always show the symbol when pitch <= 60
Exp(.lte) {
Exp(.pitch)
60
}
true
// When pitch > 60, show the symbol only
// when it is close to the camera ( distance <= 2 )
Exp(.lte) {
Exp(.distanceFromCenter)
2
}
true
// Hide in the remaining case, far and high pitch
false
}
}
return updatedFilter
}
func setPitchDistanceFilter() {
let poiLayers = ["poi-label", "transit-label"]
for layerID in poiLayers {
do {
try mapView.mapboxMap.updateLayer(withId: layerID, type: SymbolLayer.self, update: { (layer: inout SymbolLayer) in
layer.filter = layer.filter.map(updateFilter(currentFilter: ))
})
} catch {
print("Updating the layer '\(layerID)' failed: \(error.localizedDescription)")
}
}
}
}