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
  • AndroidManifest.xml
  • activity_main.xml
  • strings.xml
  • MainActivity.java

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. In your build.gradle file, add Mapbox as a dependency:

build.gradle
// 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.7.0@aar') {
        transitive = true
    }
}

The AndroidManifest.xml file is where you’ll describe components of the application, including Mapbox-related permissions. In app > manifests > AndroidManifest.xml add the necessary ACCESS_FINE_LOCATION permission. Don’t forget to adjust the package to the name of your project if you copy/paste all of the text below:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mapbox.data_drivenstylingforandroid"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The activity_main.xml file is where you’ll set the properties for your MapView. In app > res > layout > activity_main.xml, specify the center of the map view, the zoom level, and the map style used when the application is initialized:

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  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="match_parent"
    android:layout_height="match_parent"
    mapbox:mapbox_cameraTargetLat="38.9098"
    mapbox:mapbox_cameraTargetLng="-77.0295"
    mapbox:mapbox_styleUrl="@string/mapbox_style_dark"
    mapbox:mapbox_cameraZoom="15" />
</RelativeLayout>

You’ll update the name of the application and store your access token in the strings.xml file. In app > res > values > strings.xml add a name that describes the data you’ll be showing and your access token:

strings.xml
<string name="access_token" translatable="false"><your access token here></string>

MainActivity.java is a Java file where you’ll specify Mapbox-specific interactions. In app > java > yourcompany.yourproject > MainActivity.java initialize your map:

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 MainActivity 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_main);
    mapView = (MapView) findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);

    
    mapView.getMapAsync(new OnMapReadyCallback() {
      @Override
      public void onMapReady(final MapboxMap mapboxMap) {
       
    
    
    }
    });
    

}

  // 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.

Import classes

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.

MainActivity.java

import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;

Add a source and a circle layer

Next, 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

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");

mapboxMap.addLayer(circleLayer);


}
});

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.

MainActivity.java

import android.graphics.Color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;

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

circleLayer.withProperties(
  circleOpacity(0.6f),
  circleColor(Color.parseColor("#ffffff")),

);

mapboxMap.addLayer(circleLayer);

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.

MainActivity.java

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;

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

MainActivity.java

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);

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

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 MainActivity 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_main);
    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: