diff --git a/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java b/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java
index 566fd8a79..f4edea9d8 100644
--- a/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java
+++ b/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java
@@ -47,6 +47,7 @@
import com.mapbox.mapboxandroiddemo.examples.dds.KotlinStyleCirclesCategoricallyActivity;
import com.mapbox.mapboxandroiddemo.examples.dds.LineGradientActivity;
import com.mapbox.mapboxandroiddemo.examples.dds.MultipleGeometriesActivity;
+import com.mapbox.mapboxandroiddemo.examples.javaservices.MultipleGeometriesDirectionsRouteActivity;
import com.mapbox.mapboxandroiddemo.examples.dds.MultipleHeatmapStylingActivity;
import com.mapbox.mapboxandroiddemo.examples.dds.PolygonHolesActivity;
import com.mapbox.mapboxandroiddemo.examples.dds.PolygonSelectToggleActivity;
@@ -1032,6 +1033,14 @@ private void initializeModels() {
null,
R.string.activity_java_services_tilequery_url, false, BuildConfig.MIN_SDK_VERSION));
+ exampleItemModels.add(new ExampleItemModel(
+ R.id.nav_java_services,
+ R.string.activity_java_services_multiple_geometries_from_directions_route_title,
+ R.string.activity_java_services_multiple_geometries_from_directions_route_description,
+ new Intent(MainActivity.this, MultipleGeometriesDirectionsRouteActivity.class),
+ null,
+ R.string.activity_java_services_multiple_geometries_from_directions_route_url, false, BuildConfig.MIN_SDK_VERSION));
+
exampleItemModels.add(new ExampleItemModel(
R.id.nav_snapshot_image_generator,
R.string.activity_image_generator_snapshot_notification_title,
diff --git a/MapboxAndroidDemo/src/main/AndroidManifest.xml b/MapboxAndroidDemo/src/main/AndroidManifest.xml
index 93de4ba63..bc5b5d0ea 100644
--- a/MapboxAndroidDemo/src/main/AndroidManifest.xml
+++ b/MapboxAndroidDemo/src/main/AndroidManifest.xml
@@ -95,6 +95,13 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mapbox.mapboxandroiddemo.MainActivity" />
+
+
+
diff --git a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MultipleGeometriesDirectionsRouteActivity.java b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MultipleGeometriesDirectionsRouteActivity.java
new file mode 100644
index 000000000..202d9f11d
--- /dev/null
+++ b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MultipleGeometriesDirectionsRouteActivity.java
@@ -0,0 +1,303 @@
+package com.mapbox.mapboxandroiddemo.examples.javaservices;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+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.api.directions.v5.models.LegStep;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Geometry;
+import com.mapbox.geojson.GeometryCollection;
+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.geometry.LatLngBounds;
+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.CircleLayer;
+import com.mapbox.mapboxsdk.style.layers.HillshadeLayer;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.style.sources.RasterDemSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import timber.log.Timber;
+
+import static com.mapbox.core.constants.Constants.PRECISION_6;
+import static com.mapbox.mapboxsdk.camera.CameraUpdateFactory.newLatLngBounds;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.layers.Property.CIRCLE_PITCH_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.CIRCLE_PITCH_ALIGNMENT_VIEWPORT;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.hillshadeHighlightColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.hillshadeShadowColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineCap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;
+
+/**
+ * Use the Mapbox Directions API to request and retrieve a Directions route. Show the route line and
+ * place a circle where each of the route's waypoints are.
+ */
+public class MultipleGeometriesDirectionsRouteActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private MapView mapView;
+ private static final String GEOJSON_SOURCE_ID = "source-id";
+ private static final String STEPS_CIRCLE_LAYER_ID = "steps-circle-layer";
+ private static final String STEPS_BACKGROUND_CIRCLE_LAYER_ID = "steps-background-circle-layer";
+ private static final String DIRECTIONS_ROUTE_LINE_LAYER_ID = "directions-line-layer";
+
+ // Adjust the following static final variables to style this example's UI
+ private static final String LINE_COLOR = "#EE2E23";
+ private static final float LINE_WIDTH = 8f;
+ private static final float CIRCLE_RADIUS = 5f;
+ private static final float RADIUS_DIFFERENCE_BETWEEN_WAYPOINT_CIRCLES_AND_BACKGROUND_CIRCLES = 4f;
+ private static final int CIRCLE_COLOR = Color.WHITE;
+ private static final float MAX_ZOOM = 15f;
+ private static final int BACKGROUND_CIRCLE_COLOR = Color.parseColor(LINE_COLOR);
+ private static final boolean ALIGN_CIRCLES_WITH_MAP = true;
+
+ private MapboxMap mapboxMap;
+ private DirectionsRoute currentRoute;
+ private Point origin = Point.fromLngLat(-122.39648, 37.7914277);
+ private Point destination = Point.fromLngLat(-88.0430, 30.6944);
+
+ @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_dds_multiple_geometries_from_directions_route);
+
+ mapView = findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mapboxMap.setStyle(new Style.Builder().fromUri(Style.SATELLITE_STREETS)
+ .withSource(new GeoJsonSource(GEOJSON_SOURCE_ID)), new Style.OnStyleLoaded() {
+ @Override
+ public void onStyleLoaded(@NonNull Style style) {
+ MultipleGeometriesDirectionsRouteActivity.this.mapboxMap = mapboxMap;
+ initDirectionsRouteLineLayer(style);
+ initDirectionStepsCircleLayer(style);
+ initDirectionStepsBackgroundCircleLayer(style);
+ getRoute(origin, destination);
+ }
+ });
+ }
+
+ private void initDirectionsRouteLineLayer(@NonNull Style loadedMapStyle) {
+ // Create and style a LineLayer that will draw the Mapbox Directions API route line.
+ LineLayer directionsRouteLineLayer = new LineLayer(DIRECTIONS_ROUTE_LINE_LAYER_ID, GEOJSON_SOURCE_ID);
+ directionsRouteLineLayer.setProperties(
+ lineColor(Color.parseColor(LINE_COLOR)),
+ lineCap(Property.LINE_CAP_ROUND),
+ lineWidth(LINE_WIDTH)
+ );
+
+ directionsRouteLineLayer.setFilter(eq(literal("$type"), literal("LineString")));
+
+ // Add the layer below the "settlement-label" layer (city name labels, etc.)
+ if (loadedMapStyle.getLayer("settlement-label") != null) {
+ loadedMapStyle.addLayerBelow(directionsRouteLineLayer, "settlement-label");
+ } else {
+ loadedMapStyle.addLayer(directionsRouteLineLayer);
+ }
+ }
+
+ private void initDirectionStepsCircleLayer(@NonNull Style loadedMapStyle) {
+ // Create and style a CircleLayer that will place circles for each of the Mapbox Directions API route's
+ // waypoint locations
+ CircleLayer individualCirclesLayer = new CircleLayer(STEPS_CIRCLE_LAYER_ID, GEOJSON_SOURCE_ID);
+ individualCirclesLayer.setProperties(
+ circleColor(CIRCLE_COLOR),
+ circlePitchAlignment(ALIGN_CIRCLES_WITH_MAP ? CIRCLE_PITCH_ALIGNMENT_MAP : CIRCLE_PITCH_ALIGNMENT_VIEWPORT),
+ circleRadius(CIRCLE_RADIUS));
+ individualCirclesLayer.setFilter(eq(literal("$type"), literal("Point")));
+ individualCirclesLayer.setMaxZoom(MAX_ZOOM);
+ loadedMapStyle.addLayer(individualCirclesLayer);
+ }
+
+ private void initDirectionStepsBackgroundCircleLayer(@NonNull Style loadedMapStyle) {
+ // Create and style a CircleLayer that will place circles for each of the Mapbox Directions API route's
+ // waypoint locations
+ CircleLayer individualCirclesLayer = new CircleLayer(STEPS_BACKGROUND_CIRCLE_LAYER_ID, GEOJSON_SOURCE_ID);
+ individualCirclesLayer.setProperties(
+ circleColor(BACKGROUND_CIRCLE_COLOR),
+ circlePitchAlignment(ALIGN_CIRCLES_WITH_MAP ? CIRCLE_PITCH_ALIGNMENT_MAP : CIRCLE_PITCH_ALIGNMENT_VIEWPORT),
+ circleRadius(CIRCLE_RADIUS + RADIUS_DIFFERENCE_BETWEEN_WAYPOINT_CIRCLES_AND_BACKGROUND_CIRCLES));
+ individualCirclesLayer.setFilter(eq(literal("$type"), literal("Point")));
+ individualCirclesLayer.setMaxZoom(MAX_ZOOM);
+ loadedMapStyle.addLayerBelow(individualCirclesLayer, STEPS_CIRCLE_LAYER_ID);
+ }
+
+ /**
+ * Make a request to the Mapbox Directions API. Once successful, pass the route to the
+ * route layer.
+ *
+ * @param origin the starting point of the route
+ * @param destination the desired finish point of the route
+ */
+ private void getRoute(Point origin, Point destination) {
+
+ MapboxDirections directionsApiClient = MapboxDirections.builder()
+ .origin(origin)
+ .destination(destination)
+ .overview(DirectionsCriteria.OVERVIEW_FULL)
+ .profile(DirectionsCriteria.PROFILE_DRIVING)
+ .steps(true)
+ .accessToken(getString(R.string.access_token))
+ .build();
+
+ directionsApiClient.enqueueCall(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ System.out.println(call.request().url().toString());
+
+ // You can get the generic HTTP info about the response.
+ Timber.d("Response code: " + 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;
+ }
+
+ if (response.body() != null) {
+
+ // Get the directions route
+ currentRoute = response.body().routes().get(0);
+
+ mapboxMap.getStyle(new Style.OnStyleLoaded() {
+ @Override
+ public void onStyleLoaded(@NonNull Style style) {
+ // Retrieve and update the source designated for showing the directions route
+ GeoJsonSource source = style.getSourceAs(GEOJSON_SOURCE_ID);
+
+ // Create a LineString with the directions route's geometry and
+ // reset the GeoJSON source for the route LineLayer source.
+ if (source != null && response.body() != null) {
+
+ List geometryList = new ArrayList<>();
+
+ // Add each step maneuvers to the geometry list.
+ if (currentRoute.legs().size() > 0) {
+ for (LegStep singleRouteLeg : currentRoute.legs().get(0).steps()) {
+ Point stepManeuverLocationPoint = singleRouteLeg.maneuver().location();
+ geometryList.add(stepManeuverLocationPoint);
+ }
+ } else {
+ Timber.d("%s", getString(R.string.no_legs_toast));
+ }
+
+ // Add the Directions route LineString geometry to the geometry list.
+ if (currentRoute.geometry() != null) {
+ geometryList.add(LineString.fromPolyline(currentRoute.geometry(), PRECISION_6));
+ }
+
+ // Update the source's GeoJSON with a Feature that is of a GeometryCollection geometry.
+ // The GeometryCollection has the route LineString and the various step Points.
+ // The filters applied to the LineLayer and CircleLayer above, will be applied
+ // to the GeometryCollection.
+ source.setGeoJson(FeatureCollection.fromFeature(
+ Feature.fromGeometry(
+ GeometryCollection.fromGeometries(geometryList)
+ )));
+
+ // Ease the camera to fit to the Directions route.
+ easeCameraToShowEntireDirectionsRoute(new LatLng(origin.latitude(), origin.longitude()),
+ new LatLng(destination.latitude(), destination.longitude()));
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable throwable) {
+ Timber.e("Error: " + throwable.getMessage());
+ Toast.makeText(MultipleGeometriesDirectionsRouteActivity.this, "Error: " + throwable.getMessage(),
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private void easeCameraToShowEntireDirectionsRoute(LatLng origin, LatLng destination) {
+ mapboxMap.easeCamera(newLatLngBounds(new LatLngBounds.Builder()
+ .include(origin)
+ .include(destination)
+ .build(), 75), 2000);
+ }
+
+ // Add the mapView lifecycle to the activity's lifecycle methods
+ @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
+ 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);
+ }
+}
diff --git a/MapboxAndroidDemo/src/main/res/layout/activity_dds_multiple_geometries_from_directions_route.xml b/MapboxAndroidDemo/src/main/res/layout/activity_dds_multiple_geometries_from_directions_route.xml
new file mode 100644
index 000000000..4d883937c
--- /dev/null
+++ b/MapboxAndroidDemo/src/main/res/layout/activity_dds_multiple_geometries_from_directions_route.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MapboxAndroidDemo/src/main/res/values/activity_strings.xml b/MapboxAndroidDemo/src/main/res/values/activity_strings.xml
index 328266125..605eb8f83 100644
--- a/MapboxAndroidDemo/src/main/res/values/activity_strings.xml
+++ b/MapboxAndroidDemo/src/main/res/values/activity_strings.xml
@@ -406,4 +406,8 @@
- Radians
+
+ This directions route has no legs
+ The leg has no steps
+
\ No newline at end of file
diff --git a/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml b/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml
index aaf78a837..0824cad3a 100644
--- a/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml
+++ b/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml
@@ -94,6 +94,7 @@
Match raw GPS points to the map so they aligns with the roads/pathways.
Use Turf to calculate coordinates to eventually draw a ring around a center coordinate.
Use Turf to generate a circle with a radius expressed in physical units (e.g. miles, kilometers, etc).
+ Show multiple geometries based on a single Mapbox Directions API response.
Use the traffic plugin to display live car congestion data on top of a map.
Use the building plugin to easily display 3D building height
Easily retrieve GeoJSON data from a url, asset, or path
diff --git a/MapboxAndroidDemo/src/main/res/values/titles_strings.xml b/MapboxAndroidDemo/src/main/res/values/titles_strings.xml
index aa68e5bb0..b3e27d2ec 100644
--- a/MapboxAndroidDemo/src/main/res/values/titles_strings.xml
+++ b/MapboxAndroidDemo/src/main/res/values/titles_strings.xml
@@ -92,6 +92,7 @@
Map Matching
Hollow circle
Define a circle in physical units
+ Multiple geometries from Directions route
Display real-time traffic
Display buildings in 3D
Change map text to device language
diff --git a/MapboxAndroidDemo/src/main/res/values/urls_strings.xml b/MapboxAndroidDemo/src/main/res/values/urls_strings.xml
index 72091bb2f..84c7f70c2 100644
--- a/MapboxAndroidDemo/src/main/res/values/urls_strings.xml
+++ b/MapboxAndroidDemo/src/main/res/values/urls_strings.xml
@@ -93,6 +93,7 @@
https://i.imgur.com/ig8gGnY.png
https://i.imgur.com/nG8xeXH.png
https://i.imgur.com/nG8xeXH.png
+ https://i.imgur.com/Tz35fHA.png
http://i.imgur.com/HRriOVR.png
http://i.imgur.com/Vcu67UR.png
https://i.imgur.com/oKHx3bv.png