Introduction

Google maps has been one of the developers’ preferred choices when thinking of adding maps to their applications but as prices increased and a reduction of free API calls took place, developers had to start looking for an alternative that let them work with a cheaper map tool and that’s when Mapbox started to gain popularity.

Mapbox is one of the largest platforms that provide designed maps for websites and mobile applications; according to its documentation, it offers features such as maps, search, and navigation as well as better map customization compared to other options such as LeafLet or OpenLayers (If you want to know how many maps platforms exists you can visits this link). It’s powered by OpenStreetMap, a massive collaborative project to create free, editable maps.

 

What you should already know

This tutorial assumes a basic knowledge of:

  • Kotlin
  • MVVM (Model-View-ViewModel)
  • Android coroutines
  • Android Studio

 

What you’ll learn

In this tutorial, we’ll cover the most common implementations of Mapbox in our applications:

  • Ask for location permissions.
  • Get users’ addresses by their current location (geocoder).
  • Get both latitude and longitude and set a pin on the map by typing an address on a certain radius (reverse geocoder).

 

Overview

You’ll be working on an application that allows you to keep track of the user’s current location by setting a pin over the map. Also, you can search for addresses by using the Places API.

 

Getting started

Download the base code here or you can clone the repository on Github instead.

Note: Since the project you’ll download contains the main view file already made, we’ll just focus on adding the functionality to our application.

 

Dependencies

This project already has added dependencies that we need to work through this tutorial; if you downloaded or cloned the project the build.gradle file has to look like this:

Mapbox dependencies

These dependencies are needed to start working with Mapbox SDK and build our application using view models, remember that we are going to use MVVM as our design pattern.

 

Step 1: Getting Mapbox Access Token

Before coding, we have to get a Mapbox access token; this token allows us to use the SDK on our Android application. According to the official Mapbox documentation regarding access token:

“To use any of Mapbox’s tools, APIs, or SDKs, you’ll need a Mapbox access token. Mapbox uses access tokens to associate API requests with your account. You can find your access tokens, create new ones, or delete existing ones on your Access Tokens page or programmatically using the Mapbox Tokens API.”

To get an access token you need to sign up to your Mapbox account or create a new one if you don’t have it yet. Once you are logged in, go to the dashboard page and copy the default token.

For this tutorial, it’s ok if you use the default token.

Mapbox access tokens

Copy the access token to your clipboard and go to your build.gradle file inside your app folder, here we’ll create a custom field at the end of the defaultConfig block; in this way, you can use this variable in the whole project.

 

Mapbox Access Tokens. buildConfigField

 

Step 2 – Ask for locations permissions

Open your AndroidManifest.xml and add the ACCESS_FINE_LOCATION permission. We’ll use this permission to access the user’s current location.

 

 

Since Android 6 Marshmallow is required to ask for permissions at runtime, we have to write some extra code for doing so, if we don’t do it, the app will crash because access to the current location is dangerous permission that needs to be approved by the user.

In this case, we’ll use PermissionsManager, which is a useful class that helps request permissions at runtime and it’s part of Mapbox SDK, so, you don’t need to add an extra dependency. 

Go to your MainActivity.kt and create a new nullable variable permissionsManager

 

 

Then let’s create a new function on which we’ll do two things; a) create a new instance for permissionManager and b) request for the current user location.

 

 

enableLocationComponent(loadedMapStyle: Style) is a function that will be called whenever our map it’s ready to be used, and we’ll pass the map style as a parameter, but we’ll see that later on.

PermissionsManager.areLocationPermissionsGranted(this) is a method that will verify if the user has already granted the location’s permissions. If those permissions haven’t been granted yet, we have to create a new PermissionManager instance and then request for those permissions; this process will display a dialog asking the user if he wants to allow the Mapbox App access to the location.

 

Mapbox App access

 

When the user hits on either DENY or ALLOW, the system invokes onRequestPermissionsResult() method, passing it the user response. Here, we would have to know whether the user accepted the permissions or not, but since we’re using PermissionManager, we don’t have to do that, the only thing for us to do is calling onRequestPermissionsResult() method from the permissionManager instance.

Override onRequestPermissionResult() method under onCreate() method

 

 

Finally, the last thing for us to do is adding PermissionListener to MainActicvity class and implement the two functions required: onExplanationNeeded and onPermissionResult

 

 

onExplanationNeeded() method will be called when the user has previously denied the permission and we have to show an explanation asynchronously without blocking the main thread. After the user sees the explanation, we can request the permissions again.

onPermissionResult() method will be called when the user denies or accepts the permissions; here, we’ll show a message when permissions have been denied, otherwise we’ll get the user’s current location.

Once you’ve finished adding permissions, your MainActivity class has to look like this:

 

MainActivity class

 

Step 3 – Setting up Mapbox

Next, we have to create variables that will handle the instance of our map:

 

 

Once you are done adding the lines of code above, you will see that getMapAsync(this) is throwing a complaining error, this is due to a listener that we don’t have implemented on MainActivity class yet; to solve this problem let MainActivity implement interface OnMapReadyCallback.

 

OnMapReadyCallback.

 

onMapReady() is a method that will trigger every time when our map is ready to be used, here is when we have to set the map style and assign the value of our mapboxMap variable.

Inside of the onMapReady() method add these lines of code:

 

 

setStyle() method needs two parameters, the style of our map and a callback that will tell us when the style is already available for use.

The available Mapbox styles are listed below; you can use your favorite style or the one that fits your project’s preferences. Also, if none of the default styles are what you need, you can create a custom style from the Mapbox dashboard.

  • MAPBOX_STREETS
  • OUTDOORS
  • LIGHT
  • DARK
  • SATELLITE
  • SATELLITE_STREETS
  • TRAFFIC_DAY
  • TRAFFIC_NIGHT

 

Also, the Mapbox map has its own lifecycle the same as an activity, it’s important to add it because by doing this, we avoid any error or memory leaks on our application when using the map. 

We have to set up the Mapbox lifecycle by adding the next methods just under onCreate() method

 

 

We need to add just one more thing to run the application and see the map in action. To do so, Mapbox has to be initialized before getting it started, we can accomplish it by setting the next line of code before setContentView(R.layout.activity_main). This is important because if we don’t do that, we’ll get a RunTimeException

 

Mapbox.getInstance

 

Once you’ve added this line, you may run the application. You have to see something like this:

 

Mapbox

 

Step 4 – Get the user’s current location

In order to enable the user’s current location, we’ll use Mapbox’s locationComponent method; this method can be used to display the user’s location on the map.

Go to enableLocationComponent(loadedMapStyle: Style) and set this locationComponent configuration under the if which we use for asking for permission.

 

 

activateLocationComponent method initializes the component and needs to be called before any other operations are performed.

We used locationComponent?.isLocationComponentEnabled method for enabling location updates.

locationComponent?.cameraMode camera mode determines how the camera will track the user’s current location, in this case, we set it not to track the user’s location, that way we set CameraMode.NONE. If you need another mode for your camera, you can visit the official documentation here, where the different modes for the camera are shown.

locationComponent?.renderMode we finally have the renderMode method, which is how the user’s current location will be rendered on the map.

We have already set the configurations for enabling the location component to our map, now we’re going to get the last known location and move the camera to it.

Add these lines of code just below the locationComponent?.renderMode = RenderMode.COMPASS line

 

 

With the code above, we’re getting the user’s location and creating a CameraPosition.Builder that we’ll use to move the camera map to the location found. Now, run the project again to see how this works!

 

Mapbox

 

Step 5 – Searching addresses

Note: For this step of the tutorial, we won’t explain how coroutines work and the way they were implemented. We’ll only see how to search for addresses and how to handle the Places API response.

First, let’s create the variables that we’re going to need; add them above of onCreate() method

 

 

Inside of onCreate() method, create the instance for the mainViewModel and then, let’s define our observe method as well.

 

 

Also, let’s create the adapter’s instance that will handle the address found.

 

 

The way the application works is as follows: on every character the user is typing, a new request has to be triggered in order to get the addresses that match with that letter or word; in that way, the Places API can give us results matching with the string that is being added by the user.

 

 

afterTextChanged(s: Editable?) is the function that we’ll use to send a request on every character typed by the user. There are two things that are inside this function

  1. Cleaning the search adapter when there isn’t any text on the edit text and hiding the addresses list.
  2. Starting the request using the string on edit text in conjunction with centerLocation.

 

afterTextChanged

 

When a user clicks on etAddress, we have to get the location that the camera is pointing at.

Let’s add a touch listener to the Edit Text so when a user clicks on the address field we’ll get the values of centerLocation.

 

 

Now, you are ready to run the application and start looking for addresses.

 

Mapbox looking for addresses

 

Step 6 – Looking for addresses near the user’s current location

If you run the application and look for an address, you can see the response is returning addresses far from your location or even from another continent; this behavior is completely fine and it’s because we haven’t configured the Places API, which just looks in a certain area by default.

Go to the MainRepository class, then navigate to the getMapboxGeocoding() method, here we’re creating a MapboxGeocoding object needed to search for addresses; this object must have two required parameters, the Mapbox access token, and the location query. 

To start looking for addresses near the user’s location you can use the proximity() method, passing in the user’s location as a Point object to bias results to around the location.

First, let’s validate if centerLocation isn’t null, to prevent the application from crashing due to a NullPointerException.

 

 

Your getMapboxGeocoding() function has to look like this:

 

getMapboxGeocoding() function

 

Step 7 – add a marker

Once we get the list of addresses, we’re going to print a marker over the map when a user clicks on an address. 

On the latest version of Mapbox, for drawing any visual property over the map, we have to create them as Annotations.

According to Mapbox’s documentation, Annotation simplifies the way to set and adjust the visual properties of annotations on a Mapbox map;  “annotations” means circles, polygons, lines, text, and icons that we, as developers, can draw over the map.

For this tutorial, we’ll use the Symbol annotation. First, in order to create a new symbol, let’s create a symbol manager, add this line just above the onCreate() method.

 

 

This manager will let us create symbols over the map with specific methods and properties that we can use to configure every symbol.

Next, let’s create a new function in which we’ll create the instance of symbolManager.

 

 

Now, go to the onMapReady() function and inside the setStyle block add initMarkerIconSymbolManager function. When the map is ready to be used, a new SymbolManager instance will be created, and we’ll be allowed to set symbols on that new map instance.

 

map instance

 

Next, create an addMarker() function that will receive a Point object as a parameter; inside this function, we’re creating a new SymbolOption object, this object will let us define the new symbol that will be created.

 

 

Also, add this function, it will work to hide the keyboard once the user selects an address from the list.

 

 

Finally, override the onSelectAddress() method of the SearchListener that belongs to the SearchAdapter.

 

 

And that’s it! Now, run your application, search for an address, and select one of the options on the list.

 

Mapbox gif

 

EOF

You just learned the basics of adding Mapbox to your project! I hope that you enjoyed this tutorial as much as I did. I invite you to share this tutorial so more people understand Mapbox. If you have questions, suggestions or you think something is missing in this tutorial, or you just want to share your thoughts, leave a comment below, I’d appreciate the feedback. Happy coding!

 

Cover image: Bobby Sudekum