Skip to main content

Directions route with gradient

Note

This example is a part of the Mapbox Android Demo app. You can find the values for all referenced resources in the res directory. For example, see res/values/activity_strings.xml for R.string.* references used in this example.

Directions API pricing
Products used within the Mapbox Java SDK, including the Directions API, are individually billed. For pricing information, see the documentation for the Directions API.
activity_javaservices_directions_gradient
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".examples.dds.LineGradientActivity">

<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="-6.161038"
mapbox:mapbox_cameraTargetLng="106.825398"
mapbox:mapbox_cameraZoom="9.8" />

</FrameLayout>
DirectionsGradientLineActivity.java
package com.mapbox.mapboxandroiddemo.examples.javaservices;

import android.os.Bundle;
import android.widget.Toast;

import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.MapboxDirections;
import com.mapbox.api.directions.v5.models.DirectionsResponse;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;

import static android.graphics.Color.parseColor;
import static com.mapbox.core.constants.Constants.PRECISION_6;
import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
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.lineProgress;
import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineCap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineGradient;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineJoin;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;

/**
* Make a directions request with the Mapbox Directions API and then draw a line behind a moving
* SymbolLayer icon which moves along the Directions response route.
*/
public class DirectionsGradientLineActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener {

private static final String ORIGIN_ICON_ID = "origin-icon-id";
private static final String DESTINATION_ICON_ID = "destination-icon-id";
private static final String ROUTE_LAYER_ID = "route-layer-id";
private static final String ROUTE_LINE_SOURCE_ID = "route-source-id";
private static final String ICON_LAYER_ID = "icon-layer-id";
private static final String ICON_SOURCE_ID = "icon-source-id";
private MapView mapView;
private MapboxMap mapboxMap;
private DirectionsRoute currentRoute;
private MapboxDirections client;

// Adjust variables below to change the example's UI
private static Point ORIGIN_POINT = Point.fromLngLat(106.7140180, -6.149120972);
private static Point DESTINATION_POINT = Point.fromLngLat(106.9687635, -6.304752436);
private static final float LINE_WIDTH = 6f;
private static final String ORIGIN_COLOR = "#2096F3";
private static final String DESTINATION_COLOR = "#F84D4D";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Mapbox access token is configured here. This needs to be called either in your application
// object or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token));

// This contains the MapView in XML and needs to be called after the access token is configured.
setContentView(R.layout.activity_javaservices_directions_gradient);

// Setup the MapView
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
DirectionsGradientLineActivity.this.mapboxMap = mapboxMap;
mapboxMap.setStyle(new Style.Builder().fromUri(Style.DARK)
.withImage(ORIGIN_ICON_ID, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(R.drawable.blue_marker)))
.withImage(DESTINATION_ICON_ID, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(R.drawable.red_marker))), new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
initSources(style);
initLayers(style);

// Get the directions route from the Mapbox Directions API
getRoute(mapboxMap, ORIGIN_POINT, DESTINATION_POINT);

mapboxMap.addOnMapClickListener(DirectionsGradientLineActivity.this);

Toast.makeText(DirectionsGradientLineActivity.this,
getString(R.string.tap_map_instruction_gradient_directions), Toast.LENGTH_SHORT).show();
}
});
}
});
}

/**
* Add the route and marker sources to the map
*/
private void initSources(@NonNull Style loadedMapStyle) {
loadedMapStyle.addSource(new GeoJsonSource(ROUTE_LINE_SOURCE_ID, new GeoJsonOptions().withLineMetrics(true)));
loadedMapStyle.addSource(new GeoJsonSource(ICON_SOURCE_ID, getOriginAndDestinationFeatureCollection()));
}

/**
* Util method that returns a {@link FeatureCollection} with the latest origin and destination locations.
*
* @return a {@link FeatureCollection} to be used for creating a new {@link GeoJsonSource} or
* updating a source's GeoJSON.
*/
private FeatureCollection getOriginAndDestinationFeatureCollection() {
Feature originFeature = Feature.fromGeometry(ORIGIN_POINT);
originFeature.addStringProperty("originDestination", "origin");
Feature destinationFeature = Feature.fromGeometry(DESTINATION_POINT);
destinationFeature.addStringProperty("originDestination", "destination");
return FeatureCollection.fromFeatures(new Feature[] {originFeature, destinationFeature});
}

/**
* Add the route and marker icon layers to the map
*/
private void initLayers(@NonNull Style loadedMapStyle) {
// Add the LineLayer to the map. This layer will display the directions route.
loadedMapStyle.addLayer(new LineLayer(ROUTE_LAYER_ID, ROUTE_LINE_SOURCE_ID).withProperties(
lineCap(Property.LINE_CAP_ROUND),
lineJoin(Property.LINE_JOIN_ROUND),
lineWidth(LINE_WIDTH),
lineGradient(interpolate(
linear(), lineProgress(),

// This is where the gradient color effect is set. 0 -> 1 makes it a two-color gradient
// See LineGradientActivity for an example of a 2+ multiple color gradient line.
stop(0f, color(parseColor(ORIGIN_COLOR))),
stop(1f, color(parseColor(DESTINATION_COLOR)))
))));

// Add the SymbolLayer to the map to show the origin and destination pin markers
loadedMapStyle.addLayer(new SymbolLayer(ICON_LAYER_ID, ICON_SOURCE_ID).withProperties(
iconImage(match(get("originDestination"), literal("origin"),
stop("origin", ORIGIN_ICON_ID),
stop("destination", DESTINATION_ICON_ID))),
iconIgnorePlacement(true),
iconAllowOverlap(true),
iconOffset(new Float[] {0f, -4f})));
}

/**
* Make a request to the Mapbox Directions API. Once successful, pass the route to the
* route layer.
*
* @param mapboxMap the Mapbox map object that the route will be drawn on
* @param origin the starting point of the route
* @param destination the desired finish point of the route
*/
private void getRoute(MapboxMap mapboxMap, Point origin, Point destination) {
client = MapboxDirections.builder()
.origin(origin)
.destination(destination)
.overview(DirectionsCriteria.OVERVIEW_FULL)
.profile(DirectionsCriteria.PROFILE_WALKING)
.accessToken(getString(R.string.access_token))
.build();
client.enqueueCall(new Callback<DirectionsResponse>() {
@Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
// You can get the generic HTTP info about the response
Timber.d("Response code: %s", response.code());

if (response.body() == null) {
Timber.e("No routes found, make sure you set the right user and access token.");
return;
} else if (response.body().routes().size() < 1) {
Timber.e("No routes found");
return;
}

// Get the Direction API response's route
currentRoute = response.body().routes().get(0);

if (currentRoute != null) {
if (mapboxMap != null) {
mapboxMap.getStyle(new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {

// Retrieve and update the source designated for showing the directions route
GeoJsonSource originDestinationPointGeoJsonSource = style.getSourceAs(ICON_SOURCE_ID);

if (originDestinationPointGeoJsonSource != null) {
originDestinationPointGeoJsonSource.setGeoJson(getOriginAndDestinationFeatureCollection());
}

// Retrieve and update the source designated for showing the directions route
GeoJsonSource lineLayerRouteGeoJsonSource = style.getSourceAs(ROUTE_LINE_SOURCE_ID);

// Create a LineString with the directions route's geometry and
// reset the GeoJSON source for the route LineLayer source
if (lineLayerRouteGeoJsonSource != null) {
// Create the LineString from the list of coordinates and then make a GeoJSON
// FeatureCollection so we can add the line to our map as a layer.
LineString lineString = LineString.fromPolyline(currentRoute.geometry(), PRECISION_6);
lineLayerRouteGeoJsonSource.setGeoJson(Feature.fromGeometry(lineString));
}
}
});
}
} else {
Timber.d("Directions route is null");
Toast.makeText(DirectionsGradientLineActivity.this,
getString(R.string.route_can_not_be_displayed), Toast.LENGTH_SHORT).show();
}
}

@Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Toast.makeText(DirectionsGradientLineActivity.this,
getString(R.string.route_call_failure), Toast.LENGTH_SHORT).show();
}
});
}


@Override
public boolean onMapClick(@NonNull LatLng mapClickPoint) {
// Move the destination point to wherever the map was tapped
DESTINATION_POINT = Point.fromLngLat(mapClickPoint.getLongitude(), mapClickPoint.getLatitude());

// Get a new Directions API route to that new destination and eventually re-draw the
// gradient route line.
getRoute(mapboxMap, ORIGIN_POINT, DESTINATION_POINT);
return true;
}

@Override
public void onResume() {
super.onResume();
mapView.onResume();
}

@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}

@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}

@Override
public void onPause() {
super.onPause();
mapView.onPause();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}

@Override
protected void onDestroy() {
super.onDestroy();
// Cancel the Directions API request
if (client != null) {
client.cancelCall();
}
if (mapboxMap != null) {
mapboxMap.removeOnMapClickListener(this);
}
mapView.onDestroy();
}

@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
}
Was this example helpful?