Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- for i in 1...numOfLanes {
- import UIKit
- import NMAKit
- //
- // ViewController.swift
- // Next Gen Lane Guidance
- //
- // Created by Jason on 2/8/18.
- // Copyright © 2018 Jason. All rights reserved.
- //
- /*
- ViewController manages the view for the lane guidance app.
- */
- class ViewController: UIViewController, LaneGuidanceModelDelegate {
- // UI elements
- @IBOutlet weak var mapView: NMAMapView!
- @IBOutlet weak var currentManeuverInstructionLabel: UILabel!
- @IBOutlet weak var nextRoadLabel: UILabel!
- @IBOutlet weak var currentManeuverGraphicBox: UIView!
- @IBOutlet weak var distToCurrentManeuver: UILabel!
- @IBOutlet weak var laneGuidanceBox: UIView!
- @IBOutlet weak var navStartStopButtonLabel: UIButton!
- /// Start/stop button state (true = pressed to start route calculation, false = no route calculated)
- var navStartStopButtonState : Bool = false
- /// Data model
- var laneGuidanceDataModel : LaneGuidanceDataModel? = nil
- /// Previous maneuver graphic
- var prevManeuverGraphic = CAShapeLayer()
- /// Previous set of lane guidance maneuvers
- var prevLaneGuidanceBanner = UIStackView()
- /// Route element where current maneuver happens
- var routeElementsForCurrentManeuver : [NMARouteElement]? = nil
- /// Complete dictionary of direction graphics
- var directionArrowDictionary : [NMALaneInformationDirection : CAShapeLayer] = [:]
- /// Complete list of all possible directions a lane may point in
- let possibleLaneDirections : [NMALaneInformationDirection]
- = [NMALaneInformationDirection.left,
- NMALaneInformationDirection.mergeLanes,
- NMALaneInformationDirection.mergeLeft,
- NMALaneInformationDirection.mergeRight,
- NMALaneInformationDirection.right,
- NMALaneInformationDirection.secondLeft,
- NMALaneInformationDirection.secondRight,
- NMALaneInformationDirection.sharpLeft,
- NMALaneInformationDirection.sharpRight,
- NMALaneInformationDirection.slightlyLeft,
- NMALaneInformationDirection.slightlyRight,
- NMALaneInformationDirection.straight,
- NMALaneInformationDirection.uTurnLeft,
- NMALaneInformationDirection.uTurnRight]
- /**
- Notifies the view controller that its view is about to be added to a view hierarchy.
- - Parameter animated: If true, the view is being added to the window using an animation.
- */
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- laneGuidanceDataModel = LaneGuidanceDataModel()
- laneGuidanceDataModel!.delegate = self
- /// Set label on navigation control button
- navStartStopButtonLabel.setTitle("Start Navigation", for: .normal)
- /// Turn off voice guidance
- laneGuidanceDataModel!.setVoiceGuidance(activeState: false)
- }
- /**
- Starts/stops navigations.
- To start turn-by-turn navigation, a concrete route object is required. We will create a route from point A to point B. The route calculation requires local map data. Unless there is pre-downloaded map data on the device by utilizing MapLoader APIs, it is not recommended to trigger the route calculation immediately after the MapEngine is initialized. This is why we have routing triggered by a button press.
- - Parameter sender: Button that was pressed?
- */
- @IBAction func navStartStopButton(_ sender: Any) {
- if navStartStopButtonState == false {
- /// If guided navigation is off (and it is now turning on) ...
- navStartStopButtonState = true
- /// Create route
- laneGuidanceDataModel!.createRoute()
- /// Change button from "Start Navigation" to "Stop Navigation"
- navStartStopButtonLabel.setTitle("Stop Navigation", for: .normal)
- setPositionSource()
- }
- else {
- /// If guided navigation is on (and it is now turning off) ...
- navStartStopButtonState = false
- /// Remove old lane guidance info from screen
- prevLaneGuidanceBanner.removeFromSuperview()
- /// Stop guidance
- laneGuidanceDataModel!.stopNavigation()
- /// Remove route from screen
- clearRoute(mapRoute: laneGuidanceDataModel!.getMapRoute())
- /// Reset tilt to 0 (birds eye view)
- mapView.tilt = 0
- /// Orient map north up
- mapView.set(orientation: 0, animation: .none)
- /// Show entire route on screen
- mapView.set(boundingBox: laneGuidanceDataModel!.geoBoundingBox!, animation: .linear)
- /// Change button from "Stop Navigation" to "Start Navigation"
- navStartStopButtonLabel.setTitle("Start Navigation", for: .normal)
- /// Position point around which movements/animations are centered to center of screen
- mapView.transformCenter = CGPoint(x: 0, y: 0)
- }
- }
- /**
- Receive callback from the navigation manager as to whether a route was successfully created.
- - Parameter routeFound: True if navigation manager found route.
- */
- func didCreateRoute(routeFound: Bool) {
- if(routeFound == true) {
- plotRoute(mapRoute: laneGuidanceDataModel!.getMapRoute(), geoBoundingBox: laneGuidanceDataModel!.getGeoBoundingBox())
- }
- }
- /**
- Plot a route on the screen.
- - Parameter mapRoute: Route to plot.
- - Parameter geoBoundingBox: Window to use when plotting route on screen.
- */
- func plotRoute(mapRoute : NMAMapRoute, geoBoundingBox : NMAGeoBoundingBox) {
- mapView.add(mapObject: mapRoute) // Add the route onto the map
- /// NOTE: For an unknown reason, the only way I can seem to get the following two commands to work is to put the orientation command first and to set the animation for both to "none." */
- /// Orient map north up
- mapView.set(orientation: 0, animation: .none)
- /// Show entire route on screen
- mapView.set(boundingBox: geoBoundingBox, animation: .linear)
- }
- /**
- Clear route from the screen.
- - Parameter mapRoute: Route to clear.
- */
- func clearRoute(mapRoute : NMAMapRoute) {
- mapView.remove(mapObject: mapRoute)
- }
- /**
- Present the user with a decision: real world navigation or simulated navigation?
- */
- func setPositionSource() {
- /// Create an action sheet that enables user to choose between real world and simulated turn-by-turn navigation
- let alertController = UIAlertController(title: "Choose Navigation Mode", message: "Please choose a mode", preferredStyle: .actionSheet)
- /// Option 1: Real world navigation
- let navAction = UIAlertAction(title: "Real World Nav", style: .default, handler: { (action: UIAlertAction) in
- self.laneGuidanceDataModel!.startPositioning(simulation: false)
- self.startJourney()
- })
- /// Option 2: Simulated navigation
- let simulationAction = UIAlertAction(title: "Simulated Nav", style: .default, handler: { (action: UIAlertAction) in
- self.laneGuidanceDataModel!.startPositioning(simulation: true)
- self.startJourney()
- })
- /// Add two actions to action sheet
- alertController.addAction(navAction)
- alertController.addAction(simulationAction)
- /// Present the action sheet to user
- present(alertController, animated: true, completion: nil)
- }
- /**
- Present the user with a GO button.
- */
- func startJourney() {
- /// Display position indicator on map
- mapView.positionIndicator.isVisible = true
- /// Position point around which movements/animations are centered to bottom of screen to leave room for maneuver info at top of screen
- mapView.transformCenter = CGPoint(x: 0.5, y: 0.9)
- /// Create an action sheet
- let alertController = UIAlertController(title: "Start of the Journey", message: "Press GO to Start!", preferredStyle: .actionSheet)
- let go = UIAlertAction(title: "GO", style: .default, handler: { (action: UIAlertAction) in
- print("Journey started.")
- /// Configure navigation manager to launch navigation on current map
- self.laneGuidanceDataModel!.getNavManager().map = self.mapView
- /// Start following route
- self.laneGuidanceDataModel!.startNavigation()
- })
- /// Add action to action sheet
- alertController.addAction(go)
- /// Present the action sheet to user
- present(alertController, animated: true, completion: nil)
- }
- /**
- Update maneuver info on screen.
- - Parameter currentManeuver: Details of current maneuver.
- */
- func updateManeuverDisplay(currentManeuver: NMAManeuver) {
- print("New maneuver information received.")
- /// Remove previous maneuver graphic from screen
- prevManeuverGraphic.removeFromSuperlayer()
- /// Clear route elements for current maneuver
- routeElementsForCurrentManeuver = nil
- /// Update maneuver instruction, next road, and distance to current maneuver on screen
- // TODO: ADD CODE.
- /// Update maneuver graphic
- // TODO: ADD CODE.
- }
- /**
- Update lane guidance info on screen.
- - Parameter laneInformations: Lane reconfiguration information.
- - Parameter roadElement: Road element where lane reconfiguration takes place.
- */
- func updateLaneGuidanceDisplay(laneInformations: [NMALaneInformation], roadElement: NMARoadElement?) {
- /// Remove old lane guidance info from screen
- prevLaneGuidanceBanner.removeFromSuperview()
- /// Set up lane guidance banner
- let laneGuidanceBanner = UIStackView()
- laneGuidanceBanner.axis = UILayoutConstraintAxis.horizontal
- laneGuidanceBanner.distribution = UIStackViewDistribution.equalSpacing
- laneGuidanceBanner.alignment = UIStackViewAlignment.center
- laneGuidanceBanner.spacing = 16.0
- /// Create array of views to store lane direction graphics
- var laneViews = [UIView]()
- /// Populate lane guidance banner
- let numOfLanes = laneInformations.count
- for i in 1...numOfLanes {
- createDirectionArrowDictionary()
- /// For all lanes at given route cross section (i.e. position along route)...
- print("Lane: (i)")
- /// Create new view for (i-1)th lane
- laneViews += [UIView()]
- /// Set color and size of lane view
- laneViews[i - 1].backgroundColor = UIColor.blue
- laneViews[i - 1].heightAnchor.constraint(equalToConstant: 46).isActive = true
- laneViews[i - 1].widthAnchor.constraint(equalToConstant: 46).isActive = true
- /// Add direction arrows to lane views
- for j in 1...possibleLaneDirections.count {
- if laneInformations[i - 1].directions.contains(possibleLaneDirections[j - 1]) {
- /// Print string representation of lane direction to debug window
- print(possibleLaneDirections[j - 1].getString())
- /// Get arrow and recommendation
- let (currentArrow, recommendation) = getDirectionArrow(laneDirection: possibleLaneDirections[j - 1], roadElement: roadElement!)
- /// Set arrow color to white if recommended and light gray if not
- if recommendation == true {
- currentArrow.strokeColor = UIColor.white.cgColor
- }
- else {
- currentArrow.strokeColor = UIColor.lightGray.cgColor
- }
- /// Set line width for arrow
- currentArrow.lineWidth = 2.0
- /// Make sure there's no fill
- currentArrow.fillColor = nil
- /// Add arrow as sublayer of lane view
- laneViews[i - 1].layer.addSublayer(currentArrow)
- }
- }
- /// Add lane views to lane guidance banner
- laneGuidanceBanner.addArrangedSubview(laneViews[i - 1])
- }
- /// Turn OFF auto layout
- laneGuidanceBanner.translatesAutoresizingMaskIntoConstraints = false
- /// Add lane guidance banner to screen
- laneGuidanceBox.addSubview(laneGuidanceBanner)
- /// Distribute lane guidance views evenly around vertical centerline of screen
- laneGuidanceBanner.centerXAnchor.constraint(equalTo: laneGuidanceBox.centerXAnchor).isActive = true
- laneGuidanceBanner.centerYAnchor.constraint(equalTo: laneGuidanceBox.centerYAnchor).isActive = true
- /// Keep track of lane guidance info so it can be cleared when new lane guidance info becomes available
- prevLaneGuidanceBanner = laneGuidanceBanner
- }
- /**
- Get direction arrow.
- - Parameter laneDirection: Direction of interest.
- - Parameter roadElement: Road element where lane reconfiguration takes place.
- - Returns CAShapeLayer: Graphical representation of direction arrow.
- - Returns recommendation: True if current direction is recommended for driver to stay on route.
- */
- func getDirectionArrow(laneDirection : NMALaneInformationDirection, roadElement: NMARoadElement) -> (CAShapeLayer, Bool) {
- // If road element for lane reconfiguration is contained in route element for current maneuver, then we can be reasonably sure that recommended lane direction corresponds with maneuver.
- // TODO: ADD CODE THAT CHECKS THIS.
- print("Retrieving (laneDirection) arrow...")
- return (directionArrowDictionary[laneDirection]!, true)
- }
- /**
- Create dictionary of direction arrows.
- */
- func createDirectionArrowDictionary() {
- // Left Arrow
- let leftArrow = CAShapeLayer()
- let path1 = UIBezierPath()
- path1.move(to: CGPoint(x: 23, y: 43))
- path1.addLine(to: CGPoint(x: 23, y: 23))
- path1.addLine(to: CGPoint(x: 7, y: 23))
- path1.move(to: CGPoint(x: 11, y: 27))
- path1.addLine(to: CGPoint(x: 3, y: 23))
- path1.addLine(to: CGPoint(x: 11, y: 19))
- path1.close()
- leftArrow.path = path1.cgPath
- directionArrowDictionary[NMALaneInformationDirection.left] = leftArrow
- // Merge Lanes Arrow
- let mergeLanesArrow = CAShapeLayer()
- let path2 = UIBezierPath()
- path2.move(to: CGPoint(x: 11, y: 43))
- path2.addLine(to: CGPoint(x: 11, y: 27))
- path2.addLine(to: CGPoint(x: 23, y: 19))
- path2.addLine(to: CGPoint(x: 35, y: 27))
- path2.addLine(to: CGPoint(x: 35, y: 43))
- path2.move(to: CGPoint(x: 23, y: 19))
- path2.addLine(to: CGPoint(x: 23, y: 7))
- path2.move(to: CGPoint(x: 19, y: 11))
- path2.addLine(to: CGPoint(x: 23, y: 3))
- path2.addLine(to: CGPoint(x: 27, y: 11))
- path2.close()
- mergeLanesArrow.path = path2.cgPath
- directionArrowDictionary[NMALaneInformationDirection.mergeLanes] = mergeLanesArrow
- // Merge Left Arrow
- let mergeLeftArrow = CAShapeLayer()
- let path3 = UIBezierPath()
- path3.move(to: CGPoint(x: 23, y: 43))
- path3.addLine(to: CGPoint(x: 23, y: 27))
- path3.addLine(to: CGPoint(x: 11, y: 19))
- path3.addLine(to: CGPoint(x: 11, y: 7))
- path3.move(to: CGPoint(x: 7, y: 11))
- path3.addLine(to: CGPoint(x: 15, y: 11))
- path3.addLine(to: CGPoint(x: 11, y: 3))
- path3.close()
- mergeLeftArrow.path = path3.cgPath
- directionArrowDictionary[NMALaneInformationDirection.mergeLeft] = mergeLeftArrow
- // Merge Right Arrow
- let mergeRightArrow = CAShapeLayer()
- let path4 = UIBezierPath()
- path4.move(to: CGPoint(x: 23, y: 43))
- path4.addLine(to: CGPoint(x: 23, y: 27))
- path4.addLine(to: CGPoint(x: 35, y: 19))
- path4.addLine(to: CGPoint(x: 35, y: 7))
- path4.move(to: CGPoint(x: 31, y: 11))
- path4.addLine(to: CGPoint(x: 39, y: 11))
- path4.addLine(to: CGPoint(x: 35, y: 3))
- path4.close()
- mergeRightArrow.path = path4.cgPath
- directionArrowDictionary[NMALaneInformationDirection.mergeRight] = mergeRightArrow
- // Right Arrow
- let rightArrow = CAShapeLayer()
- let path5 = UIBezierPath()
- path5.move(to: CGPoint(x: 23, y: 43))
- path5.addLine(to: CGPoint(x: 23, y: 23))
- path5.addLine(to: CGPoint(x: 39, y: 23))
- path5.move(to: CGPoint(x: 35, y: 27))
- path5.addLine(to: CGPoint(x: 43, y: 23))
- path5.addLine(to: CGPoint(x: 35, y: 19))
- path5.close()
- rightArrow.path = path5.cgPath
- directionArrowDictionary[NMALaneInformationDirection.right] = rightArrow
- // Second Left Arrow
- let secondLeftArrow = CAShapeLayer()
- let path6 = UIBezierPath()
- path6.move(to: CGPoint(x: 23, y: 43))
- path6.addLine(to: CGPoint(x: 23, y: 23))
- path6.addLine(to: CGPoint(x: 7, y: 23))
- path6.move(to: CGPoint(x: 11, y: 27))
- path6.addLine(to: CGPoint(x: 3, y: 23))
- path6.addLine(to: CGPoint(x: 11, y: 19))
- path6.close()
- path6.move(to: CGPoint(x: 23, y: 23))
- path6.addLine(to: CGPoint(x: 23, y: 9))
- path6.addLine(to: CGPoint(x: 7, y: 9))
- path6.move(to: CGPoint(x: 11, y: 13))
- path6.addLine(to: CGPoint(x: 3, y: 9))
- path6.addLine(to: CGPoint(x: 11, y: 5))
- path6.close()
- secondLeftArrow.path = path6.cgPath
- directionArrowDictionary[NMALaneInformationDirection.secondLeft] = secondLeftArrow
- // Second Right Arrow
- let secondRightArrow = CAShapeLayer()
- let path7 = UIBezierPath()
- path7.move(to: CGPoint(x: 23, y: 43))
- path7.addLine(to: CGPoint(x: 23, y: 23))
- path7.addLine(to: CGPoint(x: 39, y: 23))
- path7.move(to: CGPoint(x: 35, y: 27))
- path7.addLine(to: CGPoint(x: 43, y: 23))
- path7.addLine(to: CGPoint(x: 35, y: 19))
- path7.close()
- path7.move(to: CGPoint(x: 23, y: 23))
- path7.addLine(to: CGPoint(x: 23, y: 9))
- path7.addLine(to: CGPoint(x: 35, y: 9))
- path7.move(to: CGPoint(x: 35, y: 13))
- path7.addLine(to: CGPoint(x: 43, y: 9))
- path7.addLine(to: CGPoint(x: 35, y: 5))
- path7.close()
- secondRightArrow.path = path7.cgPath
- directionArrowDictionary[NMALaneInformationDirection.secondRight] = secondRightArrow
- // Sharp Left Arrow
- let sharpLeftArrow = CAShapeLayer()
- let path8 = UIBezierPath()
- path8.move(to: CGPoint(x: 23, y: 43))
- path8.addLine(to: CGPoint(x: 23, y: 23))
- path8.addLine(to: CGPoint(x: 13.1, y: 32.9))
- path8.move(to: CGPoint(x: 17.3, y: 34.3))
- path8.addLine(to: CGPoint(x: 11.7, y: 28.7))
- path8.addLine(to: CGPoint(x: 8.9, y: 37.1))
- path8.close()
- sharpLeftArrow.path = path8.cgPath
- directionArrowDictionary[NMALaneInformationDirection.sharpLeft] = sharpLeftArrow
- // Sharp Right Arrow
- let sharpRightArrow = CAShapeLayer()
- let path9 = UIBezierPath()
- path9.move(to: CGPoint(x: 23, y: 43))
- path9.addLine(to: CGPoint(x: 23, y: 23))
- path9.addLine(to: CGPoint(x: 32.9, y: 32.9))
- path9.move(to: CGPoint(x: 28.7, y: 34.3))
- path9.addLine(to: CGPoint(x: 34.3, y: 28.7))
- path9.addLine(to: CGPoint(x: 37.1, y: 37.1))
- path9.close()
- sharpRightArrow.path = path9.cgPath
- directionArrowDictionary[NMALaneInformationDirection.sharpRight] = sharpRightArrow
- // Slightly Left Arrow
- let slightlyLeftArrow = CAShapeLayer()
- let path10 = UIBezierPath()
- path10.move(to: CGPoint(x: 23, y: 43))
- path10.addLine(to: CGPoint(x: 23, y: 23))
- path10.addLine(to: CGPoint(x: 13.2, y: 13.2))
- path10.move(to: CGPoint(x: 11.8, y: 17.5))
- path10.addLine(to: CGPoint(x: 9, y: 9))
- path10.addLine(to: CGPoint(x: 17.5, y: 11.8))
- path10.close()
- slightlyLeftArrow.path = path10.cgPath
- directionArrowDictionary[NMALaneInformationDirection.slightlyLeft] = slightlyLeftArrow
- // Slightly Right Arrow
- let slightlyRightArrow = CAShapeLayer()
- let path11 = UIBezierPath()
- path11.move(to: CGPoint(x: 23, y: 43))
- path11.addLine(to: CGPoint(x: 23, y: 23))
- path11.addLine(to: CGPoint(x: 32.8, y: 13.2))
- path11.move(to: CGPoint(x: 34.2, y: 17.5))
- path11.addLine(to: CGPoint(x: 37, y: 9))
- path11.addLine(to: CGPoint(x: 28.5, y: 11.8))
- path11.close()
- slightlyRightArrow.path = path11.cgPath
- directionArrowDictionary[NMALaneInformationDirection.slightlyRight] = slightlyRightArrow
- // Straight Arrow
- let straightArrow = CAShapeLayer()
- let path12 = UIBezierPath()
- path12.move(to: CGPoint(x: 23, y: 43))
- path12.addLine(to: CGPoint(x: 23, y: 9))
- path12.move(to: CGPoint(x: 19, y: 11))
- path12.addLine(to: CGPoint(x: 27, y: 11))
- path12.addLine(to: CGPoint(x: 23, y: 3))
- path12.close()
- straightArrow.path = path12.cgPath
- directionArrowDictionary[NMALaneInformationDirection.straight] = straightArrow
- // U Turn Left Arrow
- let uTurnLeftArrow = CAShapeLayer()
- let path13 = UIBezierPath()
- path13.move(to: CGPoint(x: 23, y: 43))
- path13.addLine(to: CGPoint(x: 23, y: 23))
- path13.addLine(to: CGPoint(x: 8, y: 23))
- path13.addLine(to: CGPoint(x: 8, y: 35))
- path13.move(to: CGPoint(x: 4, y: 35))
- path13.addLine(to: CGPoint(x: 12, y: 35))
- path13.addLine(to: CGPoint(x: 8, y: 43))
- path13.close()
- uTurnLeftArrow.path = path13.cgPath
- directionArrowDictionary[NMALaneInformationDirection.uTurnLeft] = uTurnLeftArrow
- // U Turn Right Arrow
- let uTurnRightArrow = CAShapeLayer()
- let path14 = UIBezierPath()
- path14.move(to: CGPoint(x: 23, y: 43))
- path14.addLine(to: CGPoint(x: 23, y: 23))
- path14.addLine(to: CGPoint(x: 38, y: 23))
- path14.addLine(to: CGPoint(x: 38, y: 35))
- path14.move(to: CGPoint(x: 34, y: 35))
- path14.addLine(to: CGPoint(x: 42, y: 35))
- path14.addLine(to: CGPoint(x: 38, y: 43))
- path14.close()
- uTurnRightArrow.path = path14.cgPath
- directionArrowDictionary[NMALaneInformationDirection.uTurnRight] = uTurnRightArrow
- }
- /**
- Create dictionary of maneuver information.
- */
- func createManeuverDictionary() {
- }
- }
Add Comment
Please, Sign In to add comment