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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Mapbox Tutorial For Android: Getting Started
25 mins
- Getting Started
- Register For an Account with Mapbox
- Adding the Mapbox Dependency
- Working with MapBox
- Getting a Mapbox Access Token
- Configuring Mapbox to Show the Map
- Detect the User’s Current Location
- Displaying the Location on Map
- Customizing the Map Appearance
- Using the Mapbox Navigation API
- Direct the User Between Locations
- Where to Go from Here?
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
toCameraMode.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.
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. :]
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.
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.
-
NavigationRoute.builder
: Start the navigation by first passing the current Context. -
accessToken
: This gets Mapbox an access token -
origin
: Set the start point for this navigation -
destination
: Set the ending/destination point for this navigation -
build
: Call this to build up the navigation -
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:
-
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 anull
. -
mapbox
: This is the map that you want to draw the route on top of. -
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.
- You create a constant
navigationLauncherOptions
, which you then initialize withNavigationLauncherOptions
.NavigationLauncherOptions
is part of Mapbox’s classes, which allow you to build the navigation route. -
directionsRoute
: This is the actual route that you initialized earlier. -
shouldSimulateRoute
: This is used to simulate the actual turn by turn navigation. You can enable or disable this as you like, and then callbuild()
. -
NavigationLauncher
: This is a class that you can use to start the navigation by passing two parameters: Context andnavigationLauncherOptions
.
Now build and run the app.
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.