Mapbox Tutorial For Android: Getting Started

In this tutorial, you’ll learn everything there is to setting up a simple GPS navigation app, using MapBox, by building an app called Where2Go! By Abdalla Ali.

3.8 (11) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Displaying the Location on Map

Now get the user’s current location and show it on the map. First, implement the enableLocation function by adding the following code inside it.

if (PermissionsManager.areLocationPermissionsGranted(this)) {
  initializeLocationComponent()
  initializeLocationEngine()
} else {
  permissionManager = PermissionsManager(this)
  permissionManager.requestLocationPermissions(this)
}

Check to see if the location permission was granted. If it was, call initializeLocationComponent and initializeLocationEngine to start locating the user. Otherwise, the app will ask the user for permission to access the location.

Next, modify the initializeLocationEngine function by adding the following code.

locationEngine = LocationEngineProvider(this).obtainBestLocationEngineAvailable()
locationEngine?.priority = LocationEnginePriority.HIGH_ACCURACY
locationEngine?.activate()
locationEngine?.addLocationEngineListener(this)

val lastLocation = locationEngine?.lastLocation
if (lastLocation != null) {
  originLocation = lastLocation
  setCameraPosition(lastLocation)
} else {
  locationEngine?.addLocationEngineListener(this)
}

In this code:

  • First, you get the location.
  • Next, set the priority as high.
  • Then, attach the onLocationChanged & onConnected listeners so that the app can respond to any changes.
  • Finally, try to get the user’s last location and pass it to setCameraPosition method. If the user doesn’t have a last location, call the location listeners again.

Now you need to include the following code inside initializeLocationComponent.

locationComponent = map.locationComponent
locationComponent?.activateLocationComponent(this)
locationComponent?.isLocationComponentEnabled = true
locationComponent?.cameraMode = CameraMode.TRACKING

In this code, you:

  • Initialize the locationComponent.
  • Activate and enable it to start listening for the user’s location.
  • Set cameraMode to CameraMode.TRACKING.

Modify setCameraPosition to enable zooming into the user’s location.

map.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location.latitude,
location.longitude), 30.0))

Here, you call animateCamera to move the camera map to that exact position based on location.latitude and location.longitude values, then you set the zoom value to 30.0.

Next, set up onExplanationNeeded, onPermissionResult , onLocationChanged and onConnected.

Add the following code inside onExplanationNeeded.

Toast.makeText(this, "This app needs location permission to be able to show your location on the map", Toast.LENGTH_LONG).show()

The app will now show a Toast message while asking the user to give permission to access the location.

Add the following code inside onPermissionResult.

if (granted) {
   enableLocation()
} else {
  Toast.makeText(this, "User location was not granted", Toast.LENGTH_LONG).show()
  finish()
}

Check if the location permission was granted, then initiate location tracking. If the permission wasn’t granted, the app will show a Toast message and close the app.

Next, add the following code inside onLocationChanged.

location?.run {
  originLocation = this
  setCameraPosition(this)
}

Pass the user’s latest location to the setCameraPosition method so the map will show the current user’s location all the time.

Add the following code inside onConnected.

locationEngine?.requestLocationUpdates()

This will call locationEngine to track the user’s location.

Now you need to add this code inside onRequestPermissionsResult.

permissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults)

permissionManager handles all the permission related work.

Now you need to modify a few more methods before you can run the app. First you need to include these lines of code inside onStart before mapbox.onStart().

if (PermissionsManager.areLocationPermissionsGranted(this)) {
  locationEngine?.requestLocationUpdates()
  locationComponent?.onStart()
}

Now, the app will retrieve the user’s location only if the location permission was granted by the user.

Next, you need to modify onStop.

locationEngine?.removeLocationUpdates()
locationComponent?.onStop()

The app will stop retrieving the user’s location updates when the onStop method is called.

Finally, you need to add the following line in the onDestroy method.

locationEngine?.deactivate()

This means that the app will disconnect from locationEngine and will no longer receive any location updates after the onDestroy() method is called. For information on MVP and lifecycle, see this tutorial.

Now it’s time to build and run the app to see the result.

mapbox android project running with map loaded

Customizing the Map Appearance

Open the MainActivity.kt file, and modify the class header by adding the MapboxMap.OnMapClickListener interface.

Now Android Studio will complain, telling you to implement the required method. When you choose to implement the method, make sure you select onMapClick and click OK.

After you click the OK button, Android Studio will automatically add this new override method.

override fun onMapClick(point: LatLng) {
        
}

First you need to modify the enableLocation function by adding the following code.

map.addOnMapClickListener(this)

You need to add this because you want the map to respond to user taps only when the user’s location is visible on the map, and because this class implements the MapboxMap.OnMapClickListener interface.

Modify the onMapClick method by adding the following code.

map.addMarker(MarkerOptions().position(point))

Here you add a marker at a particular position by passing the point variable.

Now build and run the app to see the marker. :]

mapbox android project with multiple android markers

It’s cool to finally see a marker on the map, but it doesn’t look right when adding multiple markers all over the map, right?

You can fix this by adding the following code inside the onMapClick method.

if(!map.markers.isEmpty()){
  map.clear()
}

This will remove any marker before adding a new one on the map.

Build and run the app to see the result.

mapbox android project with one map marker

You can further customize the marker by adding various cool things to it.

map.addMarker(MarkerOptions().setTitle("I'm a marker :]").position(point))

Here, you add a string title that appears on the marker. Go ahead and run the app to see it for yourself.

You can also add a snippet to the marker.

map.addMarker(MarkerOptions().setTitle("I'm a marker :]").setSnippet("This is a snippet about this marker that will show up here").position(point))

Now build and run the app to see the snippet.

Using the Mapbox Navigation API

In this section, you’ll learn how to use the Mapbox navigation api to add turn by turn navigation inside the app.

Direct the User Between Locations

Open the MainActivity.kt file, and add the following code at the top, before onCreate.

var navigationMapRoute: NavigationMapRoute? = null
var currentRoute: DirectionsRoute? = null

Here, you declare the required variables, which you’ll use later for navigation.

Next, you need to create a function called getRoute that takes two arguments, originPoint and endPoint. Make sure to import the org.mapbox.Point class, instead of the default Android implementation.

fun getRoute(originPoint: Point, endPoint: Point) {

}

You will use this function later for user navigation.

Now, you need to modify the onMapClick function by adding the following code.

checkLocation()
originLocation?.run {
  val startPoint = Point.fromLngLat(longitude, latitude)
  val endPoint = Point.fromLngLat(point.longitude, point.latitude)

  getRoute(startPoint, endPoint)
}

And create the checkLocation function, which will try to set the originLocation field to the last known location, if there isn’t any location present.

@SuppressLint("MissingPermission")
private fun checkLocation() {
  if (originLocation == null) {
    map.locationComponent.lastKnownLocation?.run {
      originLocation = this
    }
  }
}

In this code, you initialize startPoint by passing originLocation the longitude and latitude, while you initialize endPoint by passing point the same.

Finally, you’ll call getRoute by passing these two parameters: startPoint and endPoint.

At the moment, the getRoute function doesn’t do anything. You can make it do something by adding the following code.

Make sure to import the retrofit2 classes when importing Callback, Call and Response types.

NavigationRoute.builder(this) //1
  .accessToken(Mapbox.getAccessToken()!!) //2
  .origin(originPoint) //3
  .destination(endPoint) //4
  .build() //5
  .getRoute(object : Callback<DirectionsResponse> { //6
    override fun onFailure(call: Call<DirectionsResponse>, t: Throwable) {

    }

    override fun onResponse(call: Call<DirectionsResponse>,
      response: Response<DirectionsResponse>) {

    }
  })

Let’s go through that code block together.

  1. NavigationRoute.builder: Start the navigation by first passing the current Context.
  2. accessToken: This gets Mapbox an access token
  3. origin: Set the start point for this navigation
  4. destination: Set the ending/destination point for this navigation
  5. build: Call this to build up the navigation
  6. getRoute: Call this when you want to handle success and failure cases

Now you want to handle the onFailure case by adding the following code.

Log.d("MainActivity", t.localizedMessage)

Here the app will print a message in the Logcat when a failure case happens.

Finally, you need to handle the onResponse case by adding the code below.

if (navigationMapRoute != null) {
  navigationMapRoute?.updateRouteVisibilityTo(false)
} else {
  navigationMapRoute = NavigationMapRoute(null, mapbox, map)
}
       
currentRoute = response.body()?.routes()?.first()
if (currentRoute != null) {
  navigationMapRoute?.addRoute(currentRoute)
}

The navigationMapRoute field is responsible for drawing a line on the map, starting from current location to the destination.

You first check if navigationMapRoute is empty or not. If it’s not empty, you need to remove the route. Otherwise, initialize navigationMapRoute by passing three parameters:

  1. MapboxNavigation: This is a navigation instance of MapboxNavigation that you pass in case if you want to reroute during the navigation session. For this example you would pass a null.
  2. mapbox: This is the map that you want to draw the route on top of.
  3. MapboxMap: This will apply the route.

Initialize currentRoute by accessing the response body and getting the first route from the routes() list.

Finally, before you add any route, check whether there is already a route and then add that route to navigationMapRoute.

Now build and run the app to see the user’s route on the map! :]

It’s cool to see that blue line on the map showing the user route, right? But it’ll be really cool if you can actually start the navigation from a starting point until you reach the destination. Continue reading to find out how. :]

Inside btnNavigate, the setOnClickListener method is where you will add the following code.

val navigationLauncherOptions = NavigationLauncherOptions.builder() //1
  .directionsRoute(currentRoute) //2
  .shouldSimulateRoute(true) //3
  .build()

NavigationLauncher.startNavigation(this, navigationLauncherOptions) //4

Let’s go through that code.

  1. You create a constant navigationLauncherOptions, which you then initialize with NavigationLauncherOptions. NavigationLauncherOptions is part of Mapbox’s classes, which allow you to build the navigation route.
  2. directionsRoute: This is the actual route that you initialized earlier.
  3. shouldSimulateRoute: This is used to simulate the actual turn by turn navigation. You can enable or disable this as you like, and then call build().
  4. NavigationLauncher: This is a class that you can use to start the navigation by passing two parameters: Context and navigationLauncherOptions.

Now build and run the app.

mapbox android app with turn by turn navigation

It feels awesome to finally see the navigation working, but what will happen when you tap on the navigation button without first adding any marker on the map? The result is that the app will crash.

Here is how you can fix it. Open the MainActivity.kt file and add the following code to OnCreate.

btnNavigate.isEnabled = false

Here you disable the navigation button first in order to avoid starting the navigation before adding any marker on the map.

Now you can enable it inside the getRoute method, at the end of the onResponse block.

btnNavigate.isEnabled = true

You can now build and run the app. You can tap on the navigation button only when there’s a marker visible on the map. Otherwise, the button will be disabled.