Skip to content

Commit

Permalink
Merge pull request #74 from mapbox/route-progress-test
Browse files Browse the repository at this point in the history
route progress test added
  • Loading branch information
Cameron Mace authored Jun 8, 2017
2 parents d76c3d8 + b201b1f commit 9f6f7e7
Show file tree
Hide file tree
Showing 12 changed files with 655 additions and 160 deletions.
3 changes: 2 additions & 1 deletion navigation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand All @@ -20,4 +21,4 @@ allprojects {

task clean(type: Delete) {
delete rootProject.buildDir
}
}
5 changes: 5 additions & 0 deletions navigation/libandroid-navigation/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
apply plugin: 'com.neenbedankt.android-apt'

android {
compileSdkVersion 25
Expand Down Expand Up @@ -36,6 +37,10 @@ dependencies {
// Logging
compile 'com.jakewharton.timber:timber:4.5.1'

// AutoValues
provided 'com.google.auto.value:auto-value:1.4.1'
apt 'com.google.auto.value:auto-value:1.4.1'

// Testing
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-junit:2.0.0.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.mapbox.services.android.navigation.v5.listeners.OffRouteListener;
import com.mapbox.services.android.navigation.v5.listeners.ProgressChangeListener;
import com.mapbox.services.api.directions.v5.models.DirectionsRoute;
import com.mapbox.services.commons.models.Position;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -60,7 +61,9 @@ class NavigationEngine {
void onLocationChanged(DirectionsRoute directionsRoute, Location location) {
// if the previousRouteProgress is null, the route has just begun and one needs to be created
if (previousRouteProgress == null) {
previousRouteProgress = new RouteProgress(directionsRoute, location, 0, 0, NavigationConstants.NONE_ALERT_LEVEL);
Position currentPosition = Position.fromCoordinates(location.getLongitude(), location.getLatitude());
previousRouteProgress = RouteProgress.create(directionsRoute, currentPosition,
0, 0, NavigationConstants.NONE_ALERT_LEVEL);
}

if (!TextUtils.equals(directionsRoute.getGeometry(), previousRouteProgress.getRoute().getGeometry())) {
Expand All @@ -81,17 +84,21 @@ void onLocationChanged(DirectionsRoute directionsRoute, Location location) {
stepIndex = alertLevelState.getStepIndex();
legIndex = alertLevelState.getLegIndex();

// Create a new RouteProgress object using the latest user location
RouteProgress routeProgress = new RouteProgress(directionsRoute, location, legIndex, stepIndex, alertLevel);
SnapLocation snapLocation = new SnapLocation(location,
previousRouteProgress.getCurrentLegProgress().getCurrentStep(), options);

// Create a RouteProgress.create object using the latest user location
RouteProgress routeProgress = RouteProgress.create(directionsRoute, snapLocation.getUsersCurrentSnappedPosition(),
legIndex, stepIndex, alertLevel);

// Determine if the user is off route or not
UserOffRouteState userOffRouteState = new UserOffRouteState(location, routeProgress, options);
boolean isUserOffRoute = userOffRouteState.isUserOffRoute();

// Snap location to the route if they aren't off route and return the location object
if (isSnapEnabled && !isUserOffRoute) {
SnapLocation snapLocation = new SnapLocation(location, routeProgress, options);
location = snapLocation.getSnappedLocation();
location.setBearing(snapLocation.snapUserBearing(routeProgress));
}

notifyAlertLevelChange(routeProgress);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.mapbox.services.android.navigation.v5;

import android.location.Location;

import com.google.auto.value.AutoValue;
import com.mapbox.services.Constants;
import com.mapbox.services.Experimental;
import com.mapbox.services.android.navigation.v5.models.RouteLegProgress;
import com.mapbox.services.api.directions.v5.models.DirectionsRoute;
import com.mapbox.services.api.directions.v5.models.RouteLeg;
import com.mapbox.services.api.utils.turf.TurfConstants;
import com.mapbox.services.api.utils.turf.TurfMeasurement;
import com.mapbox.services.api.utils.turf.TurfMisc;
import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.LineString;
import com.mapbox.services.commons.geojson.Point;
import com.mapbox.services.commons.models.Position;
import com.mapbox.services.commons.utils.PolylineUtils;
Expand All @@ -29,44 +29,35 @@
* @since 0.1.0
*/
@Experimental
public class RouteProgress {
@AutoValue
public abstract class RouteProgress {

public abstract RouteLegProgress currentLegProgress();

public abstract DirectionsRoute route();

public abstract Position userSnappedPosition();

private RouteLegProgress currentLegProgress;
private DirectionsRoute route;
private Location location;
private int LegIndex;
private int alertUserLevel;
private double routeDistance;
private int stepIndex;
public abstract int legIndex();

public abstract int alertUserLevel();

/**
* Constructor for the route routeProgress information.
*
* @param route the {@link DirectionsRoute} being used for the navigation session. When a user is
* rerouted this route is updated.
* @param location the users location most recently used when creating this object.
* @param stepIndex an {@code integer} representing the current step index the user is on.
* @param alertUserLevel the most recently calculated alert level.
* @param route the {@link DirectionsRoute} being used for the navigation session. When a user is
* rerouted this route is updated.
* @param userSnappedPosition the users position most recently used when creating this object.
* @param stepIndex an {@code integer} representing the current step index the user is on.
* @param alertUserLevel the most recently calculated alert level.
* @since 0.1.0
*/
RouteProgress(DirectionsRoute route, Location location, int legIndex, int stepIndex, int alertUserLevel) {
this.route = route;
this.alertUserLevel = alertUserLevel;
this.location = location;
this.LegIndex = legIndex;
this.stepIndex = stepIndex;
currentLegProgress = new RouteLegProgress(getCurrentLeg(), stepIndex, getUsersCurrentSnappedPosition());
initialize();
}

private void initialize() {
// Measure route from beginning to end. This is done since the directions API gives a different distance then the
// one we measure using turf.
routeDistance = RouteUtils.getDistanceToEndOfRoute(
route.getLegs().get(0).getSteps().get(0).getManeuver().asPosition(),
route,
TurfConstants.UNIT_METERS
);
public static RouteProgress create(
DirectionsRoute route, Position userSnappedPosition, int legIndex, int stepIndex, int alertUserLevel) {
RouteLegProgress routeLegProgress
= RouteLegProgress.create(route.getLegs().get(legIndex), stepIndex, userSnappedPosition);
return new AutoValue_RouteProgress(
routeLegProgress, route, userSnappedPosition, legIndex, alertUserLevel);
}

/**
Expand All @@ -76,7 +67,7 @@ private void initialize() {
* @since 0.1.0
*/
public RouteLegProgress getCurrentLegProgress() {
return currentLegProgress;
return currentLegProgress();
}

/**
Expand All @@ -86,7 +77,7 @@ public RouteLegProgress getCurrentLegProgress() {
* @since 0.1.0
*/
public int getLegIndex() {
return LegIndex;
return legIndex();
}

/**
Expand All @@ -95,7 +86,7 @@ public int getLegIndex() {
* @return a {@link RouteLeg} the user is currently on.
*/
public RouteLeg getCurrentLeg() {
return route.getLegs().get(getLegIndex());
return route().getLegs().get(getLegIndex());
}

/**
Expand All @@ -105,7 +96,11 @@ public RouteLeg getCurrentLeg() {
* @since 0.1.0
*/
public double getDistanceTraveled() {
return routeDistance - getDistanceRemaining();
double distanceTraveled = route().getDistance() - getDistanceRemaining();
if (distanceTraveled < 0) {
distanceTraveled = 0;
}
return distanceTraveled;
}

/**
Expand All @@ -114,8 +109,8 @@ public double getDistanceTraveled() {
* @return {@code long} value representing the duration remaining till end of route, in unit seconds.
* @since 0.1.0
*/
public long getDurationRemaining() {
return (long) ((1 - getFractionTraveled()) * route.getDuration());
public double getDurationRemaining() {
return (1 - getFractionTraveled()) * route().getDuration();
}

/**
Expand All @@ -126,7 +121,12 @@ public long getDurationRemaining() {
* @since 0.1.0
*/
public float getFractionTraveled() {
return (float) (getDistanceTraveled() / routeDistance);
float fractionRemaining = 1;

if (route().getDistance() > 0) {
fractionRemaining = (float) (getDistanceTraveled() / route().getDistance());
}
return fractionRemaining;
}

/**
Expand All @@ -136,7 +136,22 @@ public float getFractionTraveled() {
* @since 0.1.0
*/
public double getDistanceRemaining() {
return RouteUtils.getDistanceToEndOfRoute(getUsersCurrentSnappedPosition(), route, TurfConstants.UNIT_METERS);
double distanceRemaining = 0;

List<Position> coords = PolylineUtils.decode(currentLegProgress().getCurrentStep().getGeometry(),
Constants.PRECISION_6);
if (coords.size() > 1) {
LineString slicedLine = TurfMisc.lineSlice(
Point.fromCoordinates(userSnappedPosition()),
Point.fromCoordinates(coords.get(coords.size() - 1)),
LineString.fromCoordinates(coords)
);
distanceRemaining += TurfMeasurement.lineDistance(slicedLine, TurfConstants.UNIT_METERS);
}
for (int i = currentLegProgress().getStepIndex() + 1; i < getCurrentLeg().getSteps().size(); i++) {
distanceRemaining += getCurrentLeg().getSteps().get(i).getDistance();
}
return distanceRemaining;
}

/**
Expand All @@ -146,7 +161,7 @@ public double getDistanceRemaining() {
* @since 0.1.0
*/
public int getAlertUserLevel() {
return alertUserLevel;
return alertUserLevel();
}

/**
Expand All @@ -156,25 +171,6 @@ public int getAlertUserLevel() {
* @since 0.1.0
*/
public DirectionsRoute getRoute() {
return route;
}

/**
* Provides the users location snapped to the current route they are navigating on.
*
* @return {@link Position} object with coordinates snapping the user to the route.
* @since 0.1.0
*/
public Position getUsersCurrentSnappedPosition() {
Point locationToPoint = Point.fromCoordinates(new double[] {location.getLongitude(), location.getLatitude()});
String stepGeometry = route.getLegs().get(getLegIndex()).getSteps().get(stepIndex).getGeometry();

// Decode the geometry
List<Position> coords = PolylineUtils.decode(stepGeometry, Constants.PRECISION_6);

// Uses Turf's pointOnLine, which takes a Point and a LineString to calculate the closest
// Point on the LineString.
Feature feature = TurfMisc.pointOnLine(locationToPoint, coords);
return ((Point) feature.getGeometry()).getCoordinates();
return route();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,70 @@

import android.location.Location;

import com.mapbox.services.Constants;
import com.mapbox.services.android.telemetry.utils.MathUtils;
import com.mapbox.services.api.directions.v5.models.LegStep;
import com.mapbox.services.api.utils.turf.TurfConstants;
import com.mapbox.services.api.utils.turf.TurfMeasurement;
import com.mapbox.services.api.utils.turf.TurfMisc;
import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.LineString;
import com.mapbox.services.commons.geojson.Point;
import com.mapbox.services.commons.models.Position;
import com.mapbox.services.commons.utils.PolylineUtils;

import java.util.List;

public class SnapLocation {

private MapboxNavigationOptions options;
private RouteProgress routeProgress;
private LegStep currentStep;

private Location location;

SnapLocation(Location location, RouteProgress routeProgress, MapboxNavigationOptions options) {
SnapLocation(Location location, LegStep currentStep, MapboxNavigationOptions options) {
this.location = location;
this.routeProgress = routeProgress;
this.currentStep = currentStep;
this.options = options;
}

Location getSnappedLocation() {
// Pass in the snapped location with all the other location data remaining intact for their use.
location.setLatitude(routeProgress.getUsersCurrentSnappedPosition().getLatitude());
location.setLongitude(routeProgress.getUsersCurrentSnappedPosition().getLongitude());

location.setBearing(snapUserBearing());

location.setLatitude(getUsersCurrentSnappedPosition().getLatitude());
location.setLongitude(getUsersCurrentSnappedPosition().getLongitude());
return location;
}

private float snapUserBearing() {
/**
* Provides the users location snapped to the current route they are navigating on.
*
* @return {@link Position} object with coordinates snapping the user to the route.
* @since 0.1.0
*/
Position getUsersCurrentSnappedPosition() {
Point locationToPoint = Point.fromCoordinates(new double[] {location.getLongitude(), location.getLatitude()});
String stepGeometry = currentStep.getGeometry();

// Decode the geometry
List<Position> coords = PolylineUtils.decode(stepGeometry, Constants.PRECISION_6);

// Uses Turf's pointOnLine, which takes a Point and a LineString to calculate the closest
// Point on the LineString.
Feature feature = TurfMisc.pointOnLine(locationToPoint, coords);
return ((Point) feature.getGeometry()).getCoordinates();
}

float snapUserBearing(RouteProgress routeProgress) {
LineString lineString = LineString.fromPolyline(routeProgress.getRoute().getGeometry(),
com.mapbox.services.Constants.PRECISION_6);

Position newCoordinate;
newCoordinate = routeProgress.getUsersCurrentSnappedPosition();
newCoordinate = getUsersCurrentSnappedPosition();

double userDistanceBuffer = location.getSpeed() * options.getDeadReckoningTimeInterval();

if (routeProgress.getDistanceTraveled() + userDistanceBuffer
> RouteUtils.getDistanceToEndOfRoute(
routeProgress.getRoute().getLegs().get(0).getSteps().get(0).getManeuver().asPosition(),
routeProgress.getRoute(),
TurfConstants.UNIT_METERS)) {
> routeProgress.getRoute().getDistance()) {
// If the user is near the end of the route, take the remaining distance and divide by two
userDistanceBuffer = routeProgress.getDistanceRemaining() / 2;
}
Expand Down
Loading

0 comments on commit 9f6f7e7

Please sign in to comment.