Custom waypoint styling
Customize waypoint styling.
/*This code example is part of the Mapbox Navigation SDK for iOS demo app,which you can build and run: https://github.com/mapbox/mapbox-navigation-ios-examplesTo learn more about each example in this app, including descriptions and linksto documentation, see our docs: https://docs.mapbox.com/ios/navigation/examples/custom-waypoint*/ import UIKitimport MapboxCoreNavigationimport MapboxNavigationimport MapboxDirectionsimport MapboxMapsimport Turf class CustomWaypointsViewController: UIViewController { var navigationMapView: NavigationMapView! var currentRouteIndex = 0 {didSet {showCurrentRoute()}} var routes: [Route]? {return routeResponse?.routes} var routeResponse: RouteResponse? {didSet {guard routes != nil else {navigationMapView.removeRoutes()return}currentRouteIndex = 0}} func showCurrentRoute() {guard let currentRoute = routes?[currentRouteIndex] else { return } var routes = [currentRoute]routes.append(contentsOf: self.routes!.filter {$0 != currentRoute})navigationMapView.show(routes)navigationMapView.showWaypoints(on: currentRoute)} var startButton: UIButton! // MARK: - UIViewController lifecycle methods override func viewDidLoad() {super.viewDidLoad() navigationMapView = NavigationMapView(frame: view.bounds)navigationMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]navigationMapView.delegate = selfnavigationMapView.userLocationStyle = .puck2D() view.addSubview(navigationMapView) startButton = UIButton()startButton.setTitle("Start Navigation", for: .normal)startButton.translatesAutoresizingMaskIntoConstraints = falsestartButton.backgroundColor = .bluestartButton.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)startButton.addTarget(self, action: #selector(tappedButton(sender:)), for: .touchUpInside)startButton.isHidden = trueview.addSubview(startButton) startButton.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = truestartButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = trueview.setNeedsLayout() requestRoute()} // Override layout lifecycle callback to be able to style the start button.override func viewDidLayoutSubviews() {super.viewDidLayoutSubviews() startButton.layer.cornerRadius = startButton.bounds.midYstartButton.clipsToBounds = truestartButton.setNeedsDisplay()} @objc func tappedButton(sender: UIButton) {guard let routeResponse = routeResponse else { return }// For demonstration purposes, simulate locations if the Simulate Navigation option is on. let indexedRouteResponse = IndexedRouteResponse(routeResponse: routeResponse, routeIndex: currentRouteIndex)let navigationService = MapboxNavigationService(indexedRouteResponse: indexedRouteResponse,customRoutingProvider: NavigationSettings.shared.directions,credentials: NavigationSettings.shared.directions.credentials,simulating: simulationIsEnabled ? .always : .onPoorGPS)let navigationOptions = NavigationOptions(navigationService: navigationService)let navigationViewController = NavigationViewController(for: indexedRouteResponse,navigationOptions: navigationOptions)navigationViewController.delegate = self present(navigationViewController, animated: true, completion: nil)} func requestRoute() {let origin = CLLocationCoordinate2DMake(37.773, -122.411)let firstWaypoint = CLLocationCoordinate2DMake(37.763252389415186, -122.40061448679577)let secondWaypoint = CLLocationCoordinate2DMake(37.76259647118012, -122.42072747880516)let navigationRouteOptions = NavigationRouteOptions(coordinates: [origin, firstWaypoint, secondWaypoint]) let cameraOptions = CameraOptions(center: origin, zoom: 13.0)self.navigationMapView.mapView.mapboxMap.setCamera(to: cameraOptions) Directions.shared.calculate(navigationRouteOptions) { [weak self] (_, result) inswitch result {case .failure(let error):print(error.localizedDescription)case .success(let response):guard let routes = response.routes,let currentRoute = routes.first,let self = self else { return } self.routeResponse = responseself.startButton?.isHidden = falseself.navigationMapView.show(routes)self.navigationMapView.showWaypoints(on: currentRoute)}}} func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {dismiss(animated: true, completion: nil)} // MARK: - Styling methodsfunc customCircleLayer(with identifier: String, sourceIdentifier: String) -> CircleLayer {var circleLayer = CircleLayer(id: identifier)circleLayer.source = sourceIdentifierlet opacity = Exp(.switchCase) {Exp(.any) {Exp(.get) {"waypointCompleted"}}0.51}circleLayer.circleColor = .constant(.init(UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)))circleLayer.circleOpacity = .expression(opacity)circleLayer.circleRadius = .constant(.init(10))circleLayer.circleStrokeColor = .constant(.init(UIColor.black))circleLayer.circleStrokeWidth = .constant(.init(1))circleLayer.circleStrokeOpacity = .expression(opacity)return circleLayer} func customSymbolLayer(with identifier: String, sourceIdentifier: String) -> SymbolLayer {var symbolLayer = SymbolLayer(id: identifier)symbolLayer.source = sourceIdentifiersymbolLayer.textField = .expression(Exp(.toString) {Exp(.get) {"name"}})symbolLayer.textSize = .constant(.init(10))symbolLayer.textOpacity = .expression(Exp(.switchCase) {Exp(.any) {Exp(.get) {"waypointCompleted"}}0.51})symbolLayer.textHaloWidth = .constant(.init(0.25))symbolLayer.textHaloColor = .constant(.init(UIColor.black))return symbolLayer} func customWaypointShape(shapeFor waypoints: [Waypoint], legIndex: Int) -> FeatureCollection {var features = [Turf.Feature]()for (waypointIndex, waypoint) in waypoints.enumerated() {var feature = Feature(geometry: .point(Point(waypoint.coordinate)))feature.properties = ["waypointCompleted": .boolean(waypointIndex < legIndex),"name": .number(Double(waypointIndex + 1))]features.append(feature)}return FeatureCollection(features: features)}} // MARK: Delegate methodsextension CustomWaypointsViewController: NavigationMapViewDelegate {func navigationMapView(_ navigationMapView: NavigationMapView, waypointCircleLayerWithIdentifier identifier: String, sourceIdentifier: String) -> CircleLayer? {return customCircleLayer(with: identifier, sourceIdentifier: sourceIdentifier)} func navigationMapView(_ navigationMapView: NavigationMapView, waypointSymbolLayerWithIdentifier identifier: String, sourceIdentifier: String) -> SymbolLayer? {return customSymbolLayer(with: identifier, sourceIdentifier: sourceIdentifier)} func navigationMapView(_ navigationMapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> FeatureCollection? {return customWaypointShape(shapeFor: waypoints, legIndex: legIndex)}} extension CustomWaypointsViewController: NavigationViewControllerDelegate {func navigationViewController(_ navigationViewController: NavigationViewController, waypointCircleLayerWithIdentifier identifier: String, sourceIdentifier: String) -> CircleLayer? {return customCircleLayer(with: identifier, sourceIdentifier: sourceIdentifier)} func navigationViewController(_ navigationViewController: NavigationViewController, waypointSymbolLayerWithIdentifier identifier: String, sourceIdentifier: String) -> SymbolLayer? {return customSymbolLayer(with: identifier, sourceIdentifier: sourceIdentifier)} func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor waypoints: [Waypoint], legIndex: Int) -> FeatureCollection? {return customWaypointShape(shapeFor: waypoints, legIndex: legIndex)}}