Skip to main content

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.

iOS Examples App Available

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.

PitchAndDistanceExample.swift
// 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)")
}
}
}
}
Was this example helpful?