Skip to content

Speed Watcher Simulated Navigation

In this guide you will learn how to simulate navigation along a computed route rendered on an interactive map, from a departure position to a desired destination.
The map is fully 3D, supporting pan, pinch-zoom, rotate and tilt.
The navigation includes a display showing the current speed and the speed limit.

Setup

First, get an API key token, see the Getting Started guide.
Download the Maps & Navigation SDK for Android archive file

Download the SpeedWatcher project archive file or clone the project with Git

See the Configure Android Example guide.

Run the example

In Android Studio, from the File menu, select Sync Project with Gradle Files

Speed watcher simulated navigation example Android screenshot

Speed watcher simulated navigation example Android screenshot

Speed watcher simulated navigation example Android screenshot

An android device should be connected via USB cable.
Press SHIFT+F10 to compile, install and run the example on the android device.

How it works

Android example screenshot

You can open the MainActivity.kt file to see how simulated navigation along a computed route with current speed and speed limit display works.

1private val navigationService = NavigationService()

A NavigationService() is instantiated, which carries out both simulated navigation and real navigation.

 1 private val navigationListener: NavigationListener
 2 = NavigationListener.create(
 3     onNavigationStarted = {
 4         SdkCall.execute {
 5             gemSurfaceView.mapView?.let { mapView ->
 6                 mapView.preferences?.enableCursor = false
 7                 navRoute?.let { route ->
 8                     mapView.presentRoute(route)
 9                 }
10                 // Start listening for new positions.
11                 PositionService().addListener(positionListener,
12                     EDataType.Position)
13                 enableGPSButton()
14                 mapView.followPosition()
15             }
16         }
17     },
18     onNavigationInstructionUpdated = { instr ->
19         // From every new navigation instruction get the speed limit.
20         val limit = SdkCall.execute execute@{
21             val pair = GemUtil.getSpeedText(instr.currentStreetSpeedLimit,
22                 EUnitSystem.Metric)
23             return@execute pair.first + " " + pair.second
24         }
25         speedLimit.text = limit
26     },
27     onDestinationReached = {
28         //remove the position listener when navigation is completed
29         PositionService().removeListener(positionListener)
30     }
31)
The NavigationListener receives event updates, or notifications, from the navigation service, during navigation such as when the destination is reached, or when the route to the desired destination has been recomputed, because a detour away from the original route was taken.
Besides the onNavigationStarted listener, the onNavigationInstructionUpdated listener is also instantiated, to receive the speed limit which is included with each instruction, for turns and other events along the route.
The onDestinationReached listener is instantiated to get a notification upon arrival at the destination.
 1 private val positionListener = object : PositionListener() {
 2     override fun onNewPosition(value: PositionData) {
 3         // Get the current speed for every new position received
 4         val speed = GemUtil.getSpeedText(value.speed,
 5             EUnitSystem.Metric).let { speedPair ->
 6             speedPair.first + " " + speedPair.second
 7         }
 8         Util.postOnMain {
 9             currentSpeed.text = speed
10         }
11     }
12 }
Define a position listener to get the current speed.
1private val routingProgressListener = ProgressListener.create(
2     onStarted = {
3         progressBar.visibility = View.VISIBLE
4     },
5     onCompleted = { _, _ ->
6         progressBar.visibility = View.GONE
7     },
8     postOnMain = true
9)
Define a listener to indicate when the route computation is completed, so real or simulated navigation (simulated in this case) can start.
When navigation is started, mapView.followPosition() causes the camera to follow the green arrow.
If the user pans (moves) the map to another location, the camera no longer follows the green arrow.

Speed watcher simulated navigation example Android screenshot

 1private fun enableGPSButton() {
 2     // Set actions for entering/ exiting following position mode.
 3     gemSurfaceView.mapView?.apply {
 4         onExitFollowingPosition = {
 5             followCursorButton.visibility = View.VISIBLE
 6         }
 7         onEnterFollowingPosition = {
 8             followCursorButton.visibility = View.GONE
 9         }
10         // Set on click action for the GPS button.
11         followCursorButton.setOnClickListener {
12             SdkCall.execute { followPosition() }
13         }
14     }
15}
enableGPSButton() causes a round purple button to appear in the lower right corner of the screen, whenever the simulation is active and the camera is not following the green arrow. If the user pushes this button, the followPosition() function is called, and thus the camera starts to follow the green arrow once again.
1private fun startSimulation() = SdkCall.execute {
2     val waypoints = arrayListOf(
3         Landmark("Start", 48.526, 7.734),
4         Landmark("Destination", 41.645, -0.883)
5     )
6     navigationService.startSimulation(waypoints, navigationListener,
7         routingProgressListener,
8         speedMultiplier = 0.5f)
9}
The starting, or departure point of the route is the first waypoint in a list of 2 or more Landmarks (2 in this case), each containing a name, latitude (in degrees) and longitude (in degrees). The destination point is the last waypoint in the list.
The speedMultiplier = 0.5f is there because in a simulation, the speed is the same as the speed limit, so to have a different actual speed than the speed limit during this simulation, as both are displayed, the speed is set to be 0.5, or half, of the speed limit.
 1override fun onCreate(savedInstanceState: Bundle?) {
 2     super.onCreate(savedInstanceState)
 3     setContentView(R.layout.activity_main)
 4     gemSurfaceView = findViewById(R.id.gem_surface)
 5     progressBar = findViewById(R.id.progressBar)
 6     currentSpeed = findViewById(R.id.current_speed)
 7     speedLimit = findViewById(R.id.speed_limit)
 8     followCursorButton = findViewById(R.id.followCursor)
 9
10     SdkSettings.onMapDataReady = onMapDataReady@{ isReady ->
11         if (!isReady) return@onMapDataReady
12         // Defines an action that should be done when
13         // the world map is ready (Updated/ loaded).
14         startSimulation()
15     }
16     SdkSettings.onApiTokenRejected = {
17         Toast.makeText(this@MainActivity, "TOKEN REJECTED",
18             Toast.LENGTH_SHORT).show()
19     }
20     if (!Util.isInternetConnected(this)) {
21         Toast.makeText(this, "You must be connected to internet!",
22             Toast.LENGTH_LONG).show()
23     }
24}
The MainActivity overrides the onCreate() function which checks that an internet connection is available and starts the simulation when the map instantiation is complete (the map is ready).

Android Examples

Maps SDK for Android Examples can be downloaded or cloned with Git