Tutorials
intermediate
Java

Data-driven styling for Android

Prerequisite
An Android application with a simple map view set up and familiarity with Android Studio and Java.

Data-driven styling is a powerful feature within the Mapbox Maps SDK for Android that allows you to use data attributes to style your maps. With data-driven styling, you can automatically style map features based on their individual attributes. In this tutorial, you'll build a map for Android that includes a circle layer styled based on a data attribute.

map with data styled by attribute on an Android device

Getting started

This guide assumes that you are familiar with Java and Android Studio. Here are the resources that you’ll need before getting started:

  • An application including the Mapbox Maps SDK for Android. This guide also assumes that you have already begun building an Android application that uses the Mapbox Maps SDK for Android. If you're new to the Maps SDK for Android, check out the First steps with the Mapbox Maps SDK for Android guide to set up a simple map view first.
  • Data. We collected data from the District of Columbia's Open Data DC that shows the location of street trees in Washington, D.C. Each tree has a DBH attribute that represents the diameter at breast height, a common metric for expressing the size of trees.
Download Shapefile

Upload data to Mapbox

In this tutorial, you will be using a vector tileset to display data in your application. You can create a vector tileset by uploading the Open Data DC Shapefile Mapbox Studio:

  1. Log into Mapbox Studio.
  2. Visit the Tilesets page.
  3. Click New tileset.
  4. Select the Shapefile you downloaded at the beginning of this tutorial and click Confirm.
  5. A popover will appear in the bottom right showing the progress of your upload.
  6. Once the upload has Succeeded, the tileset will be ready to use! Click on the name of the tileset in the popover, which will open the tileset information page.
  7. Take note of the Map ID on the right side of the tileset information page. You will use the Map ID to add this tileset to your application later in this guide.

Initialize a MapView

Start by creating a new project in Android Studio and initializing a MapView. There are five files you'll be working with in your Android Studio project to set up a Mapbox map and add custom data to be styled using data-driven styling. The five files you'll be working with include:

  • build.gradle: Android Studio uses a toolkit called Gradle to compile resources and source code into an APK. The build.gradle file is used to configure the build and list dependencies, including the Mapbox Maps SDK for Android.
  • AndroidManifest.xml: The AndroidManifest.xml file is where you'll describe components of the application, including Mapbox-related permissions.
  • activity_main.xml: The activity_main.xml file is where you'll set the properties for your MapView (for example, the center of the map view, the zoom level, and the map style used).
  • strings.xml: You'll store your access token in the strings.xml file.
  • MainActivity.java: MainActivity.java is a Java file where you'll specify Mapbox-specific interactions.
Manifest.xml
// in addition to the rest of your build.gradle contents
// you should include the following repository and dependency

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:6.8.1@aar') {
        transitive = true
    }
}
Manifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
mapbox:layout_constraintBottom_toBottomOf="parent"
mapbox:layout_constraintEnd_toEndOf="parent"
mapbox:layout_constraintStart_toStartOf="parent"
mapbox:layout_constraintTop_toTopOf="parent"
mapbox:mapbox_cameraTargetLat="38.9098"
mapbox:mapbox_cameraTargetLng="-77.0295"
mapbox:mapbox_cameraZoom="15"
mapbox:mapbox_styleUrl="@string/mapbox_style_dark" />
</android.support.constraint.ConstraintLayout>
strings.xml
<string name="access_token" translatable="false">YOUR_MAPBOX_ACCESS_TOKEN</string>
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
public class DataDrivenStylingActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_dds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final MapboxMap mapboxMap) {
VectorSource vectorSource = new VectorSource(
"trees-source",
// replace examples.8mj5l1r9 with the map ID for the tileset
// you created by uploading data to your Mapbox account
"http://api.mapbox.com/v4/examples.8mj5l1r9.json?access_token=" + getString(R.string.access_token)
);
mapboxMap.addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("trees-style", "trees-source");
// replace street-trees-DC-9gvg5l with the name of your source layer
circleLayer.setSourceLayer("street-trees-DC-9gvg5l");
circleLayer.withProperties(
circleOpacity(0.6f),
circleColor(Color.parseColor("#ffffff")),
circleRadius(
interpolate(exponential(1.0f), get("DBH"),
stop(0, 0f),
stop(1, 1f),
stop(110, 11f)
)
)
);
mapboxMap.addLayer(circleLayer);
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

More details about setting up an Android Studio project to be used with the Maps SDK for Android can be found in the First steps with the Mapbox Maps SDK for Android guide.

Run your application, and you should see a map in the Mapbox Dark style centered on Logan Circle.

initialized map on an Android device

Load the source

Next, you'll load the tileset that you added to your Mapbox Studio account into the application using the tileset's map ID. Find your tileset's map ID on the Tilesets page in Mapbox Studio by clicking the next to the tileset you just uploaded.

First you'll need to import the correct classes in the MainActivity.java file. This will allow you to add a vector source and create a circle layer from that data.

Then, add the code that actually pulls in the data in your tileset and adds it as a layer to your map. In the MainActivity.java file, inside protected void onCreate(Bundle savedInstanceState) { ... } add the following code:

MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
public class DataDrivenStylingActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_dds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final MapboxMap mapboxMap) {
VectorSource vectorSource = new VectorSource(
"trees-source",
// replace examples.8mj5l1r9 with the map ID for the tileset
// you created by uploading data to your Mapbox account
"http://api.mapbox.com/v4/examples.8mj5l1r9.json?access_token=" + getString(R.string.access_token)
);
mapboxMap.addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("trees-style", "trees-source");
// replace street-trees-DC-9gvg5l with the name of your source layer
circleLayer.setSourceLayer("street-trees-DC-9gvg5l");
circleLayer.withProperties(
circleOpacity(0.6f),
circleColor(Color.parseColor("#ffffff")),
circleRadius(
interpolate(exponential(1.0f), get("DBH"),
stop(0, 0f),
stop(1, 1f),
stop(110, 11f)
)
)
);
mapboxMap.addLayer(circleLayer);
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Rerun your application and you'll see the data from your tileset displayed on the dark map. Notice that the black circles are difficult to see on top of the dark map style. Next, you'll style the data to be more visible.

map with data on an Android device

Style the layer

Now you'll change the color and opacity of each circle to make the data more visible against the dark map style. Then, you'll also change the size of each circle to reflect the DBH.

Change color and opacity

Start by importing the correct classes in the MainActivity.java file. In this step you'll need the circleColor and circleOpacity classes.

Then, add the following code to the inside the public void onMapReady(final MapboxMap mapboxMap) {...} method to specify the opacity and color that the layer should use:

MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
public class DataDrivenStylingActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_dds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final MapboxMap mapboxMap) {
VectorSource vectorSource = new VectorSource(
"trees-source",
// replace examples.8mj5l1r9 with the map ID for the tileset
// you created by uploading data to your Mapbox account
"http://api.mapbox.com/v4/examples.8mj5l1r9.json?access_token=" + getString(R.string.access_token)
);
mapboxMap.addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("trees-style", "trees-source");
// replace street-trees-DC-9gvg5l with the name of your source layer
circleLayer.setSourceLayer("street-trees-DC-9gvg5l");
circleLayer.withProperties(
circleOpacity(0.6f),
circleColor(Color.parseColor("#ffffff")),
circleRadius(
interpolate(exponential(1.0f), get("DBH"),
stop(0, 0f),
stop(1, 1f),
stop(110, 11f)
)
)
);
mapboxMap.addLayer(circleLayer);
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Rerun your application, and you will see the same map view with the same point data, but now the circles will be white and semi-transparent.

initialized map on an Android device

Specify radius based on a data attribute

Finally, you'll specify that the radius of circle should be determined by the DBH value. Again, you'll start by importing the necessary classes. You'll need the Function and exponential classes to create a property function, the Stop class to establish what the circle radius should be at which DBH, and the circleRadius class to affect the circleRadius paint property.

Then, replace the code used to specify paint properties for the layer, inside circleLayer.withProperties( ... ), to use an exponential property function:

MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
public class DataDrivenStylingActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_dds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final MapboxMap mapboxMap) {
VectorSource vectorSource = new VectorSource(
"trees-source",
// replace examples.8mj5l1r9 with the map ID for the tileset
// you created by uploading data to your Mapbox account
"http://api.mapbox.com/v4/examples.8mj5l1r9.json?access_token=" + getString(R.string.access_token)
);
mapboxMap.addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("trees-style", "trees-source");
// replace street-trees-DC-9gvg5l with the name of your source layer
circleLayer.setSourceLayer("street-trees-DC-9gvg5l");
circleLayer.withProperties(
circleOpacity(0.6f),
circleColor(Color.parseColor("#ffffff")),
circleRadius(
interpolate(exponential(1.0f), get("DBH"),
stop(0, 0f),
stop(1, 1f),
stop(110, 11f)
)
)
);
mapboxMap.addLayer(circleLayer);
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Rerun your application, and you will see the map with your data styled in a way that the radius of each circle is determined by the DBH value for that point.

map with data styled by attribute on an Android device

Finished product

Great work! You've created a data visualization that illustrates the location and size of street trees all over Washington DC.

map with data styled by attribute on an Android device
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
public class DataDrivenStylingActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_dds);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final MapboxMap mapboxMap) {
VectorSource vectorSource = new VectorSource(
"trees-source",
// replace examples.8mj5l1r9 with the map ID for the tileset
// you created by uploading data to your Mapbox account
"http://api.mapbox.com/v4/examples.8mj5l1r9.json?access_token=" + getString(R.string.access_token)
);
mapboxMap.addSource(vectorSource);
CircleLayer circleLayer = new CircleLayer("trees-style", "trees-source");
// replace street-trees-DC-9gvg5l with the name of your source layer
circleLayer.setSourceLayer("street-trees-DC-9gvg5l");
circleLayer.withProperties(
circleOpacity(0.6f),
circleColor(Color.parseColor("#ffffff")),
circleRadius(
interpolate(exponential(1.0f), get("DBH"),
stop(0, 0f),
stop(1, 1f),
stop(110, 11f)
)
)
);
mapboxMap.addLayer(circleLayer);
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Next steps

There are many possibilities when using data-driven styling to create beautiful and informative data visualizations for Android applications. Read more about data-driven styling more generally in our Map design guide and dig into some data-driven styling examples specifically for Android: