Basic AR navigation
Note
This example is a part of the Vision SDK Examples. You can find the values for all referenced resources in the res
directory. For example, see res/values/strings.xml
for R.string.*
references used in this example. The dependencies can be found here.The examples use View binding.See setup documention if necessary.
This example requires two additional dependencies in the build.gradle:
implementation "com.mapbox.mapboxsdk:mapbox-android-navigation:0.42.6"
implementation "com.mapbox.mapboxsdk:mapbox-android-core:1.3.0"
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.vision.ar.view.gl.VisionArView
android:id="@+id/mapbox_ar_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
package com.mapbox.vision.examples;
import android.location.Location;
import android.os.Looper;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.android.core.location.LocationEngineCallback;
import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.android.core.location.LocationEngineRequest;
import com.mapbox.android.core.location.LocationEngineResult;
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.api.directions.v5.models.RouteLeg;
import com.mapbox.core.constants.Constants;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.utils.PolylineUtils;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions;
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute;
import com.mapbox.services.android.navigation.v5.offroute.OffRouteListener;
import com.mapbox.services.android.navigation.v5.route.RouteFetcher;
import com.mapbox.services.android.navigation.v5.route.RouteListener;
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.vision.VisionManager;
import com.mapbox.vision.ar.VisionArManager;
import com.mapbox.vision.ar.core.models.ManeuverType;
import com.mapbox.vision.ar.core.models.Route;
import com.mapbox.vision.ar.core.models.RoutePoint;
import com.mapbox.vision.ar.view.gl.VisionArView;
import com.mapbox.vision.mobile.core.interfaces.VisionEventsListener;
import com.mapbox.vision.mobile.core.models.AuthorizationStatus;
import com.mapbox.vision.mobile.core.models.Camera;
import com.mapbox.vision.mobile.core.models.Country;
import com.mapbox.vision.mobile.core.models.FrameSegmentation;
import com.mapbox.vision.mobile.core.models.classification.FrameSignClassifications;
import com.mapbox.vision.mobile.core.models.detection.FrameDetections;
import com.mapbox.vision.mobile.core.models.position.GeoCoordinate;
import com.mapbox.vision.mobile.core.models.position.VehicleState;
import com.mapbox.vision.mobile.core.models.road.RoadDescription;
import com.mapbox.vision.mobile.core.models.world.WorldDescription;
import com.mapbox.vision.performance.ModelPerformance.On;
import com.mapbox.vision.performance.ModelPerformanceConfig.Merged;
import com.mapbox.vision.performance.ModelPerformanceMode;
import com.mapbox.vision.performance.ModelPerformanceRate;
import com.mapbox.vision.utils.VisionLogger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Example shows how Vision and VisionAR SDKs are used to draw AR lane over the video stream from camera.
* Also, Mapbox navigation services are used to build route and navigation session.
*/
public class ArActivity extends BaseActivity implements RouteListener, ProgressChangeListener, OffRouteListener {
private static final String TAG = ArActivity.class.getSimpleName();
// Handles navigation.
private MapboxNavigation mapboxNavigation;
// Fetches route from points.
private RouteFetcher routeFetcher;
private RouteProgress lastRouteProgress;
private LocationEngine locationEngine;
private LocationEngineCallback<LocationEngineResult> locationCallback;
private boolean visionManagerWasInit = false;
private boolean navigationWasStarted = false;
// This dummy points will be used to build route. For real world test this needs to be changed to real values for
// source and target locations.
private final Point ROUTE_ORIGIN = Point.fromLngLat(27.654285, 53.928057);
private final Point ROUTE_DESTINATION = Point.fromLngLat(27.655637, 53.935712);
@Override
protected void initViews() {
setContentView(R.layout.activity_ar_navigation);
}
protected void setArRenderOptions(@NotNull final VisionArView visionArView) {
visionArView.setFenceVisible(true);
}
@Override
protected void onPermissionsGranted() {
startVisionManager();
startNavigation();
}
@Override
protected void onStart() {
super.onStart();
startVisionManager();
startNavigation();
}
@Override
protected void onStop() {
super.onStop();
stopVisionManager();
stopNavigation();
}
private void startVisionManager() {
if (allPermissionsGranted() && !visionManagerWasInit) {
// Create and start VisionManager.
VisionManager.create();
VisionManager.setModelPerformanceConfig(new Merged(new On(ModelPerformanceMode.DYNAMIC, ModelPerformanceRate.LOW)));
VisionManager.start();
VisionManager.setVisionEventsListener(new VisionEventsListener() {
@Override
public void onAuthorizationStatusUpdated(@NotNull AuthorizationStatus authorizationStatus) {
}
@Override
public void onFrameSegmentationUpdated(@NotNull FrameSegmentation frameSegmentation) {
}
@Override
public void onFrameDetectionsUpdated(@NotNull FrameDetections frameDetections) {
}
@Override
public void onFrameSignClassificationsUpdated(@NotNull FrameSignClassifications frameSignClassifications) {
}
@Override
public void onRoadDescriptionUpdated(@NotNull RoadDescription roadDescription) {
}
@Override
public void onWorldDescriptionUpdated(@NotNull WorldDescription worldDescription) {
}
@Override
public void onVehicleStateUpdated(@NotNull VehicleState vehicleState) {
}
@Override
public void onCameraUpdated(@NotNull Camera camera) {
}
@Override
public void onCountryUpdated(@NotNull Country country) {
}
@Override
public void onUpdateCompleted() {
}
});
VisionArView visionArView = findViewById(R.id.mapbox_ar_view);
// Create VisionArManager.
VisionArManager.create(VisionManager.INSTANCE);
visionArView.setArManager(VisionArManager.INSTANCE);
setArRenderOptions(visionArView);
visionManagerWasInit = true;
}
}
private void stopVisionManager() {
if (visionManagerWasInit) {
VisionArManager.destroy();
VisionManager.stop();
VisionManager.destroy();
visionManagerWasInit = false;
}
}
private void startNavigation() {
if (allPermissionsGranted() && !navigationWasStarted) {
// Initialize navigation with your Mapbox access token.
mapboxNavigation = new MapboxNavigation(
this,
getString(R.string.mapbox_access_token),
MapboxNavigationOptions.builder().build()
);
// Initialize route fetcher with your Mapbox access token.
routeFetcher = new RouteFetcher(this, getString(R.string.mapbox_access_token));
routeFetcher.addRouteListener(this);
locationEngine = LocationEngineProvider.getBestLocationEngine(this);
LocationEngineRequest arLocationEngineRequest = new LocationEngineRequest.Builder(0)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(1000)
.build();
locationCallback = new LocationEngineCallback<LocationEngineResult>() {
@Override
public void onSuccess(LocationEngineResult result) {
}
@Override
public void onFailure(@NonNull Exception exception) {
}
};
try {
locationEngine.requestLocationUpdates(arLocationEngineRequest, locationCallback, Looper.getMainLooper());
} catch (SecurityException se) {
VisionLogger.Companion.e(TAG, se.toString());
}
initDirectionsRoute();
// Route need to be reestablished if off route happens.
mapboxNavigation.addOffRouteListener(this);
mapboxNavigation.addProgressChangeListener(this);
navigationWasStarted = true;
}
}
private void stopNavigation() {
if (navigationWasStarted) {
locationEngine.removeLocationUpdates(locationCallback);
mapboxNavigation.removeProgressChangeListener(this);
mapboxNavigation.removeOffRouteListener(this);
mapboxNavigation.stopNavigation();
navigationWasStarted = false;
}
}
private void initDirectionsRoute() {
// Get route from predefined points.
NavigationRoute.builder(this)
.accessToken(getString(R.string.mapbox_access_token))
.origin(ROUTE_ORIGIN)
.destination(ROUTE_DESTINATION)
.build()
.getRoute(new Callback<DirectionsResponse>() {
@Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
if (response.body() == null || response.body().routes().isEmpty()) {
return;
}
// Start navigation session with retrieved route.
DirectionsRoute route = response.body().routes().get(0);
mapboxNavigation.startNavigation(route);
// Set route progress.
VisionArManager.setRoute(new Route(
getRoutePoints(route),
route.duration().floatValue(),
"",
""
));
}
@Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
}
});
}
@Override
public void onErrorReceived(Throwable throwable) {
if (throwable != null) {
throwable.printStackTrace();
}
mapboxNavigation.stopNavigation();
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show();
}
@Override
public void onResponseReceived(@NotNull DirectionsResponse response, RouteProgress routeProgress) {
mapboxNavigation.stopNavigation();
if (response.routes().isEmpty()) {
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show();
} else {
DirectionsRoute route = response.routes().get(0);
mapboxNavigation.startNavigation(route);
// Set route progress.
VisionArManager.setRoute(new Route(
getRoutePoints(route),
(float) routeProgress.durationRemaining(),
"",
""
));
}
}
@Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
lastRouteProgress = routeProgress;
}
@Override
public void userOffRoute(Location location) {
routeFetcher.findRouteFromRouteProgress(location, lastRouteProgress);
}
private RoutePoint[] getRoutePoints(@NotNull DirectionsRoute route) {
ArrayList<RoutePoint> routePoints = new ArrayList<>();
List<RouteLeg> legs = route.legs();
if (legs != null) {
for (RouteLeg leg : legs) {
List<LegStep> steps = leg.steps();
if (steps != null) {
for (LegStep step : steps) {
RoutePoint point = new RoutePoint((new GeoCoordinate(
step.maneuver().location().latitude(),
step.maneuver().location().longitude()
)), mapToManeuverType(step.maneuver().type()));
routePoints.add(point);
List<Point> geometryPoints = buildStepPointsFromGeometry(step.geometry());
for (Point geometryPoint : geometryPoints) {
point = new RoutePoint((new GeoCoordinate(
geometryPoint.latitude(),
geometryPoint.longitude()
)), ManeuverType.None);
routePoints.add(point);
}
}
}
}
}
return routePoints.toArray(new RoutePoint[0]);
}
private List<Point> buildStepPointsFromGeometry(String geometry) {
return PolylineUtils.decode(geometry, Constants.PRECISION_6);
}
private ManeuverType mapToManeuverType(@Nullable String maneuver) {
if (maneuver == null) {
return ManeuverType.None;
}
switch (maneuver) {
case "turn":
return ManeuverType.Turn;
case "depart":
return ManeuverType.Depart;
case "arrive":
return ManeuverType.Arrive;
case "merge":
return ManeuverType.Merge;
case "on ramp":
return ManeuverType.OnRamp;
case "off ramp":
return ManeuverType.OffRamp;
case "fork":
return ManeuverType.Fork;
case "roundabout":
return ManeuverType.Roundabout;
case "exit roundabout":
return ManeuverType.RoundaboutExit;
case "end of road":
return ManeuverType.EndOfRoad;
case "new name":
return ManeuverType.NewName;
case "continue":
return ManeuverType.Continue;
case "rotary":
return ManeuverType.Rotary;
case "roundabout turn":
return ManeuverType.RoundaboutTurn;
case "notification":
return ManeuverType.Notification;
case "exit rotary":
return ManeuverType.RotaryExit;
default:
return ManeuverType.None;
}
}
}
package com.mapbox.vision.examples
import android.location.Location
import android.widget.Toast
import com.mapbox.android.core.location.LocationEngineCallback
import com.mapbox.android.core.location.LocationEngineProvider
import com.mapbox.android.core.location.LocationEngineRequest
import com.mapbox.android.core.location.LocationEngineResult
import com.mapbox.api.directions.v5.models.DirectionsResponse
import com.mapbox.api.directions.v5.models.DirectionsRoute
import com.mapbox.core.constants.Constants
import com.mapbox.geojson.Point
import com.mapbox.geojson.utils.PolylineUtils
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute
import com.mapbox.services.android.navigation.v5.offroute.OffRouteListener
import com.mapbox.services.android.navigation.v5.route.RouteFetcher
import com.mapbox.services.android.navigation.v5.route.RouteListener
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress
import com.mapbox.vision.VisionManager
import com.mapbox.vision.ar.VisionArManager
import com.mapbox.vision.ar.core.models.ManeuverType
import com.mapbox.vision.ar.core.models.Route
import com.mapbox.vision.ar.core.models.RoutePoint
import com.mapbox.vision.ar.view.gl.VisionArView
import com.mapbox.vision.mobile.core.interfaces.VisionEventsListener
import com.mapbox.vision.mobile.core.models.position.GeoCoordinate
import com.mapbox.vision.performance.ModelPerformance
import com.mapbox.vision.performance.ModelPerformanceConfig
import com.mapbox.vision.performance.ModelPerformanceMode
import com.mapbox.vision.performance.ModelPerformanceRate
import com.mapbox.vision.utils.VisionLogger
import kotlinx.android.synthetic.main.activity_ar_navigation.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
/**
* Example shows how Vision and VisionAR SDKs are used to draw AR lane over the video stream from camera.
* Also, Mapbox navigation services are used to build route and navigation session.
*/
open class ArActivityKt : BaseActivity(), RouteListener, ProgressChangeListener, OffRouteListener {
companion object {
private var TAG = ArActivityKt::class.java.simpleName
}
// Handles navigation.
private lateinit var mapboxNavigation: MapboxNavigation
// Fetches route from points.
private lateinit var routeFetcher: RouteFetcher
private lateinit var lastRouteProgress: RouteProgress
private lateinit var directionsRoute: DirectionsRoute
private var visionManagerWasInit = false
private var navigationWasStarted = false
private val arLocationEngine by lazy {
LocationEngineProvider.getBestLocationEngine(this)
}
private val arLocationEngineRequest by lazy {
LocationEngineRequest.Builder(0)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(1000)
.build()
}
private val locationCallback by lazy {
object : LocationEngineCallback<LocationEngineResult> {
override fun onSuccess(result: LocationEngineResult?) {}
override fun onFailure(exception: Exception) {}
}
}
// This dummy points will be used to build route. For real world test this needs to be changed to real values for
// source and target locations.
private val ROUTE_ORIGIN = Point.fromLngLat(27.654285, 53.928057)
private val ROUTE_DESTINATION = Point.fromLngLat(27.655637, 53.935712)
protected open fun setArRenderOptions(visionArView: VisionArView) {
// enable fence rendering
visionArView.setFenceVisible(true)
}
override fun onPermissionsGranted() {
startVisionManager()
startNavigation()
}
override fun initViews() {
setContentView(R.layout.activity_ar_navigation)
}
override fun onStart() {
super.onStart()
startVisionManager()
startNavigation()
}
override fun onStop() {
super.onStop()
stopVisionManager()
stopNavigation()
}
private fun startVisionManager() {
if (allPermissionsGranted() && !visionManagerWasInit) {
// Create and start VisionManager.
VisionManager.create()
VisionManager.setModelPerformanceConfig(
ModelPerformanceConfig.Merged(
ModelPerformance.On(
ModelPerformanceMode.DYNAMIC,
ModelPerformanceRate.LOW
)
)
)
VisionManager.start()
VisionManager.visionEventsListener = object : VisionEventsListener {}
// Create VisionArManager.
VisionArManager.create(VisionManager)
mapbox_ar_view.setArManager(VisionArManager)
setArRenderOptions(mapbox_ar_view)
visionManagerWasInit = true
}
}
private fun stopVisionManager() {
if (visionManagerWasInit) {
VisionArManager.destroy()
VisionManager.stop()
VisionManager.destroy()
visionManagerWasInit = false
}
}
private fun startNavigation() {
if (allPermissionsGranted() && !navigationWasStarted) {
// Initialize navigation with your Mapbox access token.
mapboxNavigation = MapboxNavigation(
this,
getString(R.string.mapbox_access_token),
MapboxNavigationOptions.builder().build()
)
// Initialize route fetcher with your Mapbox access token.
routeFetcher = RouteFetcher(this, getString(R.string.mapbox_access_token))
routeFetcher.addRouteListener(this)
try {
arLocationEngine.requestLocationUpdates(
arLocationEngineRequest,
locationCallback,
mainLooper
)
} catch (se: SecurityException) {
VisionLogger.e(TAG, se.toString())
}
initDirectionsRoute()
// Route need to be reestablished if off route happens.
mapboxNavigation.addOffRouteListener(this)
mapboxNavigation.addProgressChangeListener(this)
navigationWasStarted = true
}
}
private fun stopNavigation() {
if (navigationWasStarted) {
arLocationEngine.removeLocationUpdates(locationCallback)
mapboxNavigation.removeProgressChangeListener(this)
mapboxNavigation.removeOffRouteListener(this)
mapboxNavigation.stopNavigation()
navigationWasStarted = false
}
}
private fun initDirectionsRoute() {
NavigationRoute.builder(this)
.accessToken(getString(R.string.mapbox_access_token))
.origin(ROUTE_ORIGIN)
.destination(ROUTE_DESTINATION)
.build()
.getRoute(object : Callback<DirectionsResponse> {
override fun onResponse(
call: Call<DirectionsResponse>,
response: Response<DirectionsResponse>
) {
if (response.body() == null || response.body()!!.routes().isEmpty()) {
return
}
directionsRoute = response.body()!!.routes()[0]
mapboxNavigation.startNavigation(directionsRoute)
// Set route progress.
VisionArManager.setRoute(
Route(
directionsRoute.getRoutePoints(),
directionsRoute.duration()?.toFloat() ?: 0f,
"",
""
)
)
}
override fun onFailure(call: Call<DirectionsResponse>, t: Throwable) {
t.printStackTrace()
}
})
}
override fun onErrorReceived(throwable: Throwable?) {
throwable?.printStackTrace()
mapboxNavigation.stopNavigation()
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show()
}
override fun onResponseReceived(response: DirectionsResponse, routeProgress: RouteProgress?) {
mapboxNavigation.stopNavigation()
if (response.routes().isEmpty()) {
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show()
} else {
mapboxNavigation.startNavigation(response.routes()[0])
val route = response.routes()[0]
// Set route progress.
VisionArManager.setRoute(
Route(
route.getRoutePoints(),
route.duration()?.toFloat() ?: 0f,
"",
""
)
)
}
}
override fun onProgressChange(location: Location, routeProgress: RouteProgress) {
lastRouteProgress = routeProgress
}
override fun userOffRoute(location: Location) {
routeFetcher.findRouteFromRouteProgress(location, lastRouteProgress)
}
private fun DirectionsRoute.getRoutePoints(): Array<RoutePoint> {
val routePoints = arrayListOf<RoutePoint>()
legs()?.forEach { leg ->
leg.steps()?.forEach { step ->
val maneuverPoint = RoutePoint(
GeoCoordinate(
latitude = step.maneuver().location().latitude(),
longitude = step.maneuver().location().longitude()
),
step.maneuver().type().mapToManeuverType()
)
routePoints.add(maneuverPoint)
step.geometry()
?.buildStepPointsFromGeometry()
?.map { geometryStep ->
RoutePoint(
GeoCoordinate(
latitude = geometryStep.latitude(),
longitude = geometryStep.longitude()
)
)
}
?.let { stepPoints ->
routePoints.addAll(stepPoints)
}
}
}
return routePoints.toTypedArray()
}
private fun String.buildStepPointsFromGeometry(): List<Point> {
return PolylineUtils.decode(this, Constants.PRECISION_6)
}
private fun String?.mapToManeuverType(): ManeuverType = when (this) {
"turn" -> ManeuverType.Turn
"depart" -> ManeuverType.Depart
"arrive" -> ManeuverType.Arrive
"merge" -> ManeuverType.Merge
"on ramp" -> ManeuverType.OnRamp
"off ramp" -> ManeuverType.OffRamp
"fork" -> ManeuverType.Fork
"roundabout" -> ManeuverType.Roundabout
"exit roundabout" -> ManeuverType.RoundaboutExit
"end of road" -> ManeuverType.EndOfRoad
"new name" -> ManeuverType.NewName
"continue" -> ManeuverType.Continue
"rotary" -> ManeuverType.Rotary
"roundabout turn" -> ManeuverType.RoundaboutTurn
"notification" -> ManeuverType.Notification
"exit rotary" -> ManeuverType.RoundaboutExit
else -> ManeuverType.None
}
}
この{Type}は役に立ちましたか?