Navigation simulation on a downloaded map¶
In this guide you will learn how to simulate offline navigation along a route on a previously downloaded map.
Setup¶
First, get an API key token, see the Getting Started guide.
Download the Maps & Navigation SDK for Android archive fileDownload the DownloadedOnboardMapSimulation 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
Simulates offline navigation along a route on a previously downloaded map.
How it works¶
You can open the MainActivity.kt file to see how the local previously downloaded offline map is used for simulated navigation.
1<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/
.
1private val navigationService = NavigationService()
NavigationService()
is instantiated, which carries out
both simulated navigation and real navigation. 1private 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 enableGPSButton()
11 mapView.followPosition()
12 }
13 }
14 topPanel.visibility = View.VISIBLE
15 bottomPanel.visibility = View.VISIBLE
16 showStatusMessage("Simulation started.")
17 },
18 onNavigationInstructionUpdated = { instr ->
19 var instrText = ""
20 var instrIcon: Bitmap? = null
21 var instrDistance = ""
22 var etaText = ""
23 var rttText = ""
24 var rtdText = ""
25 SdkCall.execute {
26 // Fetch data for the navigation top panel (instruction related info).
27 instrText = instr.nextStreetName ?: ""
28 instrIcon = instr.nextTurnImage?.asBitmap(100, 100)
29 instrDistance = instr.getDistanceInMeters()
30 // Fetch data for the navigation bottom panel (route related info).
31 navRoute?.apply {
32 etaText = getEta() // estimated time of arrival
33 rttText = getRtt() // remaining travel time
34 rtdText = getRtd() // remaining travel distance
35 }
36 }
37 // Update the navigation panels info.
38 navInstruction.text = instrText
39 navInstructionIcon.setImageBitmap(instrIcon)
40 navInstructionDistance.text = instrDistance
41 eta.text = etaText
42 rtt.text = rttText
43 rtd.text = rtdText
44 if (statusText.isVisible)
45 {
46 statusText.visibility = View.GONE
47 }
48 }
49)
navigationListener
, that will
receive notifications from the navigation service.
The onNavigationStarted
and onNavigationInstructionUpdated
callbacks are implemented.navInstruction.text = instrText
navInstructionIcon.setImageBitmap(instrIcon)
navInstructionDistance.text = instrDistance
eta.text = // estimated time of arrival
rtt.text = // remaining travel time
rtd.text = // remaining travel distance
1private val routingProgressListener = ProgressListener.create(
2 onStarted = {
3 progressBar.visibility = View.VISIBLE
4 showStatusMessage("Routing process started.")
5 },
6 onCompleted = { _, _ ->
7 progressBar.visibility = View.GONE
8 showStatusMessage("Routing process completed.")
9 },
10 postOnMain = true
11)
1override fun onCreate(savedInstanceState: Bundle?)
2{
3 super.onCreate(savedInstanceState)
4 setContentView(R.layout.activity_main)
5 gemSurfaceView = findViewById(R.id.gem_surface)
6 progressBar = findViewById(R.id.progressBar)
7 followCursorButton = findViewById(R.id.followCursor)
8 topPanel = findViewById(R.id.top_panel)
9 navInstruction = findViewById(R.id.nav_instruction)
10 navInstructionDistance = findViewById(R.id.instr_distance)
11 navInstructionIcon = findViewById(R.id.nav_icon)
12 statusText = findViewById(R.id.status_text)
13 bottomPanel = findViewById(R.id.bottom_panel)
14 eta = findViewById(R.id.eta)
15 rtt = findViewById(R.id.rtt)
16 rtd = findViewById(R.id.rtd)
17 SdkSettings.onApiTokenRejected = {
18 showDialog("TOKEN REJECTED")
19 }
20 gemSurfaceView.onSdkInitSucceeded = {
21 // Defines an action that should be done when the the sdk had been loaded.
22 startSimulation()
23 }
24}
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:startSimulation()
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("Luxembourg", 49.61588784436375, 6.135843869736401),
4 Landmark("Mersch", 49.74785494642988, 6.103323786692679)
5 )
6 navigationService.startSimulation(
7 waypoints,
8 navigationListener,
9 routingProgressListener
10 )
11 }
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.