Navigation use case

VERSION 0.3.34
PUBLIC PREVIEW

Navigation SDK for Android is only available upon request. Contact us to get started.

This tutorial shows how to build a simple navigation application using the TomTom Navigation SDK for Android. The app uses the built-in UI components. However, you can build custom components and integrate them with the SDK.

The application displays a map and shows the user’s location. After the user selects a destination with a long click, the app plans a route and draws it on the map. Navigation is started once the user taps on the route.


Complete app

Getting started

  1. Install Android Studio if you don’t already have it.
  2. Create a new project or open an existing one. Make sure that the minimum SDK API level is set to at least 21 (Android 5.0 "Lollipop") and that the compile SDK API level is set to 31.
  3. Add the packagingOptions.
    1android {
    2 ...
    3 packagingOptions {
    4 pickFirst "lib/**/libc++_shared.so"
    5 }
    6}
  4. Because the repository for Navigation SDK is private, you will need to contact us to get access.
  5. Once you have obtained access, go to repositories.tomtom.com and log in with your account. Expand the user menu in the top-right corner, and select "Edit profile" → "Generate an Identity Token". Copy your token and put it, together with your login, in the settings.gradle file of your project, replacing the repositoryUsername and repositoryToken placeholders.
    1dependencyResolutionManagement {
    2 repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    3 repositories {
    4 google()
    5 mavenCentral()
    6 maven {
    7 credentials {
    8 username = repositoryUsername
    9 password = repositoryToken
    10 }
    11 url = uri("https://repositories.tomtom.com/artifactory/maven")
    12 }
    13 }
    14}
  6. In the app/build.gradle file, add dependencies to the Map Display, Routing, Location, and Navigation modules.
    1dependencies {
    2 def version = "0.3.34"
    3 implementation "com.tomtom.sdk:maps-display:$version"
    4 implementation "com.tomtom.sdk:routing-client-online:$version"
    5 implementation "com.tomtom.sdk:location-android:$version"
    6 implementation "com.tomtom.sdk:location-simulation:$version"
    7 implementation "com.tomtom.sdk:location-mapmatched:$version"
    8 implementation "com.tomtom.sdk:navigation:$version"
    9 implementation "com.tomtom.sdk:navigation-ui:$version"
    10}
  7. Add the appropriate TomTom API key. If you don’t have an API Key, go to How to get a TomTom API Key to learn how to create one.

Displaying a map

Once the project is set up with the appropriate dependencies, you can proceed with map initialization. To initialize a map, prepare the FragmentContainerView for the map. The map will be displayed within this layout.

1<androidx.fragment.app.FragmentContainerView
2 android:id="@+id/map_container"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" />

Use MapFragment to display a map. MapOptions is required to initialize it. It must contain a valid TomTom API key. Optionally, you can configure the map by setting other properties of the MapOptions object. Read more about it in the Map Configuration guide. Finally, add the MapFragment to the previously created container.

1val mapOptions = MapOptions(mapKey = "YOUR_API_KEY")
2val mapFragment = MapFragment.newInstance(mapOptions)
3supportFragmentManager.beginTransaction()
4 .replace(R.id.map_container, mapFragment)
5 .commit()

Any interaction with the map such as adding a marker or drawing a route is made via the TomTomMap object. Since map initialization can take a while, retrieving the TomTomMap object has to be done asynchronously. Keep the instance of it for further actions.

1mapFragment.getMapAsync { map ->
2 this.tomTomMap = map
3 enableUserLocation()
4 setUpMapListeners()
5}

center

Showing user location

Showing the user’s location is crucial in a navigation application. To do this, the application has to use the device’s location services, which requires the appropriate permissions. The Android system requires the user to grant ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.

On devices that run Android 6.0 or higher, you must request permissions at runtime. See how to grant location permissions in the Android permissions document.

The TomTom SDK provides a LocationEngine interface that is used between different modules to get location updates. In this tutorial the AndroidLocationEngine is used. Under the hood, the engine uses Android’s system location services. Note that the LocationEngine.enable() method has to be called to obtain location updates. If you want to learn more about the LocationEngine, you can read the guides. A good place to start is the Location quickstart guide.

locationEngine = AndroidLocationEngine(context = this)
locationEngine.enable()

The LocationEngine itself only reports location changes. It does not interact internally with the map or navigation. Therefore, to show the user’s location on the map you have to set the LocationEngine to the TomTomMap. You also have to manually enable the location indicator. It can be configured using the LocationMarkerOptions class. Read more about user location on the map in the Showing User Location document.

1tomTomMap.setLocationEngine(locationEngine)
2val locationMarker = LocationMarkerOptions(type = LocationMarkerType.POINTER)
3tomTomMap.enableLocationMarker(locationMarker)

center

Generating a route

This section describes how to calculate a route between two locations using the appropriate parameters for navigation purposes. You can find more details about calculating a route in the routing module documentation. A good place to start is the Quickstart guide. It also shows how to display a route on the map.

The entry point for the routing service is the RoutingApi interface. To initialize it, use the default TomTom implementation based on TomTom’s Routing API.

routingApi = OnlineRoutingApi.create(context = this, apiKey = "YOUR_API_KEY")

The routing request is configured using RoutingOptions. The origin point of the route is the current location of the user, which is taken from the TomTomMap object. Note that you can also find the latest location using the LocationEngine object.

For a better user experience during navigation, set some of the parameters:

  • InstructionType - indicates that the routing result has to contain a guidance instruction.
  • InstructionPhoneticsType - specify whether to include phonetic transcriptions in the response.
  • AnnouncementPoints - if specified, the instruction in the response will include up to three additional fine-grained announcement points, each with its own location, maneuver type, and distance to the instruction point.
  • SectionType.MOTORWAY - improve instruction generation on the motorways.
  • SectionType.LANES, SectionType.SPEED_LIMIT - section types used in lane guidance.
1val userLocation =
2 tomTomMap.currentLocation?.position ?: return
3val itinerary = Itinerary(origin = userLocation, destination = destination)
4routingOptions = RoutingOptions.Builder(itinerary)
5 .instructionType(InstructionType.TEXT)
6 .instructionPhonetics(InstructionPhoneticsType.IPA)
7 .instructionAnnouncementPoints(AnnouncementPoints.ALL)
8 .sectionTypes(listOf(SectionType.MOTORWAY, SectionType.LANES, SectionType.SPEED_LIMIT))
9 .travelMode(TravelMode.CAR)
10 .build()

The prepared RoutingOptions object is used as a parameter to the request method. The second parameter is RoutingCallback. It is used to return the result from the routing request.

routingApi.planRoute(routingOptions, routingCallback)

The RoutingCallback itself has two methods. One is triggered when the request has failed. The second one returns the routing results. The RoutingResult contains a list of calculated routes. This tutorial uses only one calculated route.

1private val routingCallback = object : RoutingCallback {
2 override fun onSuccess(result: RoutingResult) {
3 route = result.routes.firstOrNull() ?: return
4 route?.let { drawRoute(it) }
5 }
6
7 override fun onError(error: RoutingError) {
8 Toast.makeText(this@MainActivity, error.message, Toast.LENGTH_SHORT).show()
9 }
10}

Now the returned Route has to be drawn on the map. RouteOptions, which specifies the appearance of the route, is used for this. Then the RouteOptions object is added to the TomTomMap. Finally, you can show the overview of the added routes using the TomTomMap.zoomToRoutes(Int) method. Note that its padding parameter is expressed in pixels. Read more about adding a route to the map in the Route Planning and Driving document.

For the navigation use case, the instructions can be drawn on the route in form of arrows that indicate maneuvers. To do this, map the Instruction object provided by the routing to the Instruction object used by the map. Note that during navigation, you need to update the progress property of the drawn route to display the next instructions.

1private fun Route.mapInstructions(): List<Instruction> {
2 val routeInstructions = legs.flatMap { routeLeg -> routeLeg.instructions }
3 return routeInstructions.map {
4 Instruction(
5 routeOffset = it.routeOffset,
6 combineWithNext = it.isPossibleToCombineWithNext
7 )
8 }
9}
1val instructions = route.mapInstructions()
2val geometry = route.legs.flatMap { it.points }
3val routeOptions = RouteOptions(
4 geometry = geometry,
5 destinationMarkerVisible = true,
6 departureMarkerVisible = true,
7 instructions = instructions
8)
9tomTomMap.addRoute(routeOptions)
10tomTomMap.zoomToRoutes(100)

Route instruction

Starting navigation

The TomTom Navigation SDK provides turn-by-turn navigation. Read more about it in the Turn-by-turn navigation guide. It can be used with the provided user interface components or your custom ones. This tutorial uses the default Navigation UI in a form of the NavigationFragment.

  1. Initialize the TomTomNavigation object. The TomTomNavigation object is used to interact with navigation. Read more about configuration navigation in the Navigation configuration document.

    1val navigationConfiguration = NavigationConfiguration.Builder(
    2 context = this,
    3 navigationApiKey = "YOUR_API_KEY",
    4 locationEngine = locationEngine,
    5 routingApi = routingApi
    6).build()
    7tomTomNavigation = TomTomNavigation.create(navigationConfiguration)
  2. Create the NavigationFragment and add it to your XML layout using FragmentManager. This fragment wraps TomTomNavigation and provides the UI to show upcoming maneuvers, remaining distance, Estimated Time of Arrival (ETA), current speed, and speed limit. Note that you have to set the previously-created TomTomNavigation object to the NavigationFragment before using it.

    Disposal of TomTomNavigation is not handled by the NavigationFragment. You have to call TomTomNavigation.dispose() on your own once the navigation is no longer needed.

    1<androidx.fragment.app.FragmentContainerView
    2 android:id="@+id/navigation_fragment_container"
    3 android:layout_width="match_parent"
    4 android:layout_height="wrap_content"
    5 app:layout_constraintBottom_toBottomOf="@+id/map_container" />
    1val navigationUiOptions = NavigationUiOptions.Builder()
    2 .keepInBackground(true)
    3 .build()
    4navigationFragment = NavigationFragment.newInstance(navigationUiOptions)
    5supportFragmentManager.beginTransaction()
    6 .add(R.id.navigation_fragment_container, navigationFragment)
    7 .commitNow()
    navigationFragment.setTomTomNavigation(tomTomNavigation)
  3. To start the turn-by-turn navigation you need the Route object along which the navigation will be done, and RoutingOptions used during the route planning. Combine them using RoutePlan object and call NavigationFragment.start(RoutePlan) method.

    val routePlan = RoutePlan(route, routingOptions)
    navigationFragment.startNavigation(routePlan)
  4. Use NavigationFragment to configure the map to provide the best user experience. NavigationFragment uses NavigationListener to update the navigation state.

    navigationFragment.addNavigationListener(navigationListener)
    1private val navigationListener = object : NavigationFragment.NavigationListener {
    2 override fun onStarted() {
    3 tomTomMap.changeCameraTrackingMode(CameraTrackingMode.FOLLOW_WITH_HEADING)
    4 tomTomMap.enableLocationMarker(LocationMarkerOptions(LocationMarkerType.CHEVRON))
    5 setSimulationLocationEngineToNavigation()
    6 setMapMatchedLocationEngine()
    7 setMapNavigationPadding()
    8 }
    9
    10 override fun onFailed(error: NavigationError) {
    11 Toast.makeText(this@MainActivity, error.message, Toast.LENGTH_SHORT).show()
    12 stopNavigation()
    13 }
    14
    15 override fun onStopped() {
    16 stopNavigation()
    17 }
    18}
    19
    20private fun setSimulationLocationEngineToNavigation() {
    21 // For testing purposes the SimulationLocationEngine is used
    22 locationEngine = createSimulationLocationEngine(route!!)
    23 tomTomNavigation.setLocationEngine(locationEngine)
    24 locationEngine.enable()
    25}
  5. Once navigation is started, the camera is set to follow the user position, and the location indicator is changed to a chevron. To match raw location updates to the routes, use MapMatchedLocationEngine and set it to the TomTomMap.

    1val mapMatchedLocationEngine = MapMatchedLocationEngine(tomTomNavigation)
    2tomTomMap.setLocationEngine(mapMatchedLocationEngine)
    3mapMatchedLocationEngine.enable()
  6. Set the bottom padding on the map. The padding sets a safe area of the MapView in which user interaction is not received. It is used to uncover the chevron in the navigation panel.

    1val paddingBottom =
    2 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 263f, resources.displayMetrics)
    3 .toInt()
    4val padding = Padding(0, 0, 0, paddingBottom)
    5tomTomMap.setPadding(padding)
  7. Stop the navigation process using NavigationFragment. This hides the UI elements and calls the TomTomNavigation.stop() method.

    navigationFragment.stopNavigation()
  8. Don’t forget to reset any map settings that were changed, such as camera tracking, location marker, and map padding.

    1tomTomMap.changeCameraTrackingMode(CameraTrackingMode.NONE)
    2tomTomMap.enableLocationMarker(LocationMarkerOptions(LocationMarkerType.POINTER))
    3resetMapPadding()

Remember to dispose TomTomNavigation if it is no longer needed to release resources.

Run the application. You should see a globe showing the user’s location. Set the destination point with a long press. If you want to start navigation along the drawn route, tap it. Navigation should start with a guidance panel and voice instructions.

center

Next steps

The TomTom Navigation SDK allows you to customize the appearance of the map and its overlays, use your own Navigation UI components, or provide a custom implementation of certain navigation behaviors. See the following guides for more information: