Skip to main content
GuidesAPI ReferenceExamples

Navigation simulation on a downloaded map

Estimated reading time: 6 minutes

In this guide you will learn how to simulate offline navigation along a route on a previously downloaded map.

Setup

  1. Get your Magic Lane API key token: if you do not have a token, see the Getting Started guide
  2. Download the Maps & Navigation SDK for Android archive file
  3. Download the DownloadedOnboardMapSimulation project archive file or clone the project with Git
  4. See the Configure Android Example guide

Run the example

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

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


Simulates offline navigation along a route on a previously downloaded map.

How it works

How it works

You can open the MainActivity.kt file to see how the local previously downloaded offline map is used for simulated navigation.

<uses-permission android:name="android.permission.INTERNET" />

Note that, in AndroidManifest.xml, the INTERNET permission is NOT present, as this example uses an offline map.

The previously downloaded map is stored on the device in a directory such as this: /sdcard/Android/data/com.magiclane.examplename/files/Data/Maps/.

private val navigationService = NavigationService()

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

private val navigationListener: NavigationListener
= NavigationListener.create(
onNavigationStarted = {
SdkCall.execute {
gemSurfaceView.mapView?.let { mapView ->
mapView.preferences?.enableCursor = false
navRoute?.let { route ->
mapView.presentRoute(route)
}
enableGPSButton()
mapView.followPosition()
}
}
topPanel.visibility = View.VISIBLE
bottomPanel.visibility = View.VISIBLE
showStatusMessage("Simulation started.")
},
onNavigationInstructionUpdated = { instr ->
var instrText = ""
var instrIcon: Bitmap? = null
var instrDistance = ""
var etaText = ""
var rttText = ""
var rtdText = ""
SdkCall.execute {
// Fetch data for the navigation top panel (instruction related info).
instrText = instr.nextStreetName ?: ""
instrIcon = instr.nextTurnImage?.asBitmap(100, 100)
instrDistance = instr.getDistanceInMeters()
// Fetch data for the navigation bottom panel (route related info).
navRoute?.apply {
etaText = getEta() // estimated time of arrival
rttText = getRtt() // remaining travel time
rtdText = getRtd() // remaining travel distance
}
}
// Update the navigation panels info.
navInstruction.text = instrText
navInstructionIcon.setImageBitmap(instrIcon)
navInstructionDistance.text = instrDistance
eta.text = etaText
rtt.text = rttText
rtd.text = rtdText
if (statusText.isVisible)
{
statusText.visibility = View.GONE
}
}
)

Define a navigation listener, navigationListener, that will receive notifications from the navigation service. The onNavigationStarted and onNavigationInstructionUpdated callbacks are implemented.

The navigation instructions received include a text instruction, such as “turn left”, a bitmap image, such as an arrow, and a distance, indicating how far ahead the turn is located:

  • navInstruction.text = instrText
  • navInstructionIcon.setImageBitmap(instrIcon)
  • navInstructionDistance.text = instrDistance

Additionally, the ETA, RTT and RTD are also obtained from the navigation service and displayed:

  • eta.text = // estimated time of arrival
  • rtt.text = // remaining travel time
  • rtd.text = // remaining travel distance
private val routingProgressListener = ProgressListener.create(
onStarted = {
progressBar.visibility = View.VISIBLE
showStatusMessage("Routing process started.")
},
onCompleted = { _, _ ->
progressBar.visibility = View.GONE
showStatusMessage("Routing process completed.")
},
postOnMain = true
)

Define a listener to indicate when the route computation is completed, so real or simulated navigation (simulated in this case) can start.

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
gemSurfaceView = findViewById(R.id.gem_surface)
progressBar = findViewById(R.id.progressBar)
followCursorButton = findViewById(R.id.followCursor)
topPanel = findViewById(R.id.top_panel)
navInstruction = findViewById(R.id.nav_instruction)
navInstructionDistance = findViewById(R.id.instr_distance)
navInstructionIcon = findViewById(R.id.nav_icon)
statusText = findViewById(R.id.status_text)
bottomPanel = findViewById(R.id.bottom_panel)
eta = findViewById(R.id.eta)
rtt = findViewById(R.id.rtt)
rtd = findViewById(R.id.rtd)
SdkSettings.onApiTokenRejected = {
showDialog("TOKEN REJECTED")
}
gemSurfaceView.onSdkInitSucceeded = {
// Defines an action that should be done when the the sdk had been loaded.
startSimulation()
}
}

The MainActivity overrides the onCreate() function which gets the IDs of the 6 elements on the top and bottom panels to display the notifications from the navigation service:

  • text instructions
  • arrow bitmap image
  • distance to the instruction event
  • estimated time of arrival (ETA)
  • remaining travel time (RTT)
  • remaining travel distance (RTD)

If the SDK initialization succeeded, the navigation simulation is started: startSimulation()

private fun enableGPSButton() {
// Set actions for entering/ exiting following position mode.
gemSurfaceView.mapView?.apply {
onExitFollowingPosition = {
followCursorButton.visibility = View.VISIBLE
}
onEnterFollowingPosition = {
followCursorButton.visibility = View.GONE
}
// Set on click action for the GPS button.
followCursorButton.setOnClickListener {
SdkCall.execute { followPosition() }
}
}
}

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.

private fun startSimulation() = SdkCall.execute {
val waypoints = arrayListOf(
Landmark("Luxembourg", 49.61588784436375, 6.135843869736401),
Landmark("Mersch", 49.74785494642988, 6.103323786692679)
)
navigationService.startSimulation(
waypoints,
navigationListener,
routingProgressListener
)
}

The startSimulation() function first instantiates a list of 2 waypoints, one for the departure location and one for the destination; there can be more than 2 waypoints in a route, but not less than 2. These 2 waypoints are hardcoded here to locations within Luxembourg, because the offline map that is found in the app/assets/offlinemaps directory of this example, is of Luxembourg.

This example is configured to run offline (internet permission is not set) so no additional data will be downloaded - the route computation and navigation simulation will be completely offline, using the map data included with this example. This is why the departure and destination are within the area of the map that is stored offline for this example.

Android Examples

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