Skip to content

Commit

Permalink
Dynamic reroute tolerance method (#428)
Browse files Browse the repository at this point in the history
* adds validation utils class

* Added initial logic that adjust the tolerance value when the users close to an intersection

* moved turf nearest to MAS and fixed classes

* nit extra line
  • Loading branch information
Cameron Mace authored Oct 27, 2017
1 parent 0f646c9 commit 6af3488
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 4,379 deletions.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ dex-count:
navigation-fixtures:
# Navigation: Taylor street to Page street
curl "https://api.mapbox.com/directions/v5/mapbox/driving/-122.413165,37.795042;-122.433378,37.7727?geometries=polyline6&overview=full&steps=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \
-o libandroid-navigation/src/test/res/navigation.json
-o libandroid-navigation/src/test/resources/navigation.json

# Directions: polyline geometry with precision 5
curl "https://api.mapbox.com/directions/v5/mapbox/driving/-122.416667,37.783333;-121.900000,37.333333?geometries=polyline&steps=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \
-o libandroid-navigation/src/test/res/directions_v5.json
-o libandroid-navigation/src/test/resources/directions_v5.json

# Intersection:
curl "https://api.mapbox.com/directions/v5/mapbox/driving/-101.70939001157072,33.62145406099651;-101.68721910152767,33.6213852194939?geometries=polyline6&steps=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \
-o libandroid-navigation/src/test/resources/single_intersection.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private NavigationConstants() {
*
* @since 0.1.0
*/
static final int MANEUVER_ZONE_RADIUS = 40;
public static final int MANEUVER_ZONE_RADIUS = 40;

/**
* Maximum number of meters the user can travel away from step before the
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.navigation.MapboxNavigationOptions;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.services.android.navigation.v5.utils.RingBuffer;
import com.mapbox.services.android.navigation.v5.utils.ToleranceUtils;
import com.mapbox.services.constants.Constants;
import com.mapbox.turf.TurfConstants;
import com.mapbox.turf.TurfMeasurement;
Expand All @@ -29,8 +30,9 @@ public boolean isUserOffRoute(Location location, RouteProgress routeProgress,
MapboxNavigationOptions options,
RingBuffer<Integer> recentDistancesFromManeuverInMeters) {
Point futurePoint = getFuturePosition(location, options);
double radius = Math.max(options.maximumDistanceOffRoute(),
location.getAccuracy() + options.userLocationSnapDistance());

double radius = ToleranceUtils.dynamicRerouteDistanceTolerance(
Point.fromLngLat(location.getLongitude(), location.getLatitude()), routeProgress);

LegStep currentStep = routeProgress.currentLegProgress().currentStep();
boolean isOffRoute = userTrueDistanceFromStep(futurePoint, currentStep) > radius;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ public abstract class RouteLegProgress {
*/
static RouteLegProgress create(RouteLeg routeLeg, int stepIndex, double legDistanceRemaining,
double stepDistanceRemaining) {

LegStep nextStep
= stepIndex == (routeLeg.steps().size() - 1) ? null : routeLeg.steps().get(stepIndex + 1);

RouteStepProgress stepProgress = RouteStepProgress.create(
routeLeg.steps().get(stepIndex), stepDistanceRemaining);
routeLeg.steps().get(stepIndex), nextStep, stepDistanceRemaining);
return new AutoValue_RouteLegProgress(
routeLeg, stepIndex, legDistanceRemaining, stepProgress);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.mapbox.services.android.navigation.v5.routeprogress;


import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.auto.value.AutoValue;
import com.mapbox.directions.v5.models.LegStep;
import com.mapbox.directions.v5.models.StepIntersection;

import java.util.ArrayList;
import java.util.List;


/**
Expand All @@ -20,8 +27,12 @@ public abstract class RouteStepProgress {

abstract LegStep step();

public static RouteStepProgress create(LegStep step, double stepDistanceRemaining) {
return new AutoValue_RouteStepProgress(step, stepDistanceRemaining);
@Nullable
abstract LegStep nextStep();

public static RouteStepProgress create(@NonNull LegStep step, @Nullable LegStep nextStep,
double stepDistanceRemaining) {
return new AutoValue_RouteStepProgress(step, nextStep, stepDistanceRemaining);
}

/**
Expand Down Expand Up @@ -77,4 +88,21 @@ public float fractionTraveled() {
public double durationRemaining() {
return (1 - fractionTraveled()) * step().duration();
}

/**
* A collection of all the current steps intersections and the next steps maneuver location
* (if one exist).
*
* @return a list of {@link StepIntersection}s which may include the next steps maneuver
* intersection if it exist
* @since 0.7.0
*/
public List<StepIntersection> intersections() {
List<StepIntersection> intersectionsWithNextManeuver = new ArrayList<>();
intersectionsWithNextManeuver.addAll(step().intersections());
if (nextStep() != null && !nextStep().intersections().isEmpty()) {
intersectionsWithNextManeuver.add(nextStep().intersections().get(0));
}
return intersectionsWithNextManeuver;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mapbox.services.android.navigation.v5.utils;

import com.mapbox.directions.v5.models.StepIntersection;
import com.mapbox.geojson.Point;
import com.mapbox.services.android.navigation.v5.navigation.NavigationConstants;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.turf.TurfClassification;
import com.mapbox.turf.TurfConstants;
import com.mapbox.turf.TurfMeasurement;

import java.util.ArrayList;
import java.util.List;

public final class ToleranceUtils {

private ToleranceUtils() {
// Utils class therefore, shouldn't be initialized.
}

public static double dynamicRerouteDistanceTolerance(Point snappedPoint,
RouteProgress routeProgress) {
List<StepIntersection> intersections
= routeProgress.currentLegProgress().currentStepProgress().intersections();
List<Point> intersectionsPoints = new ArrayList<>();
for (StepIntersection intersection : intersections) {
intersectionsPoints.add(intersection.location());
}

Point closestIntersection = TurfClassification.nearest(snappedPoint, intersectionsPoints);

if (closestIntersection.equals(snappedPoint)) {
return NavigationConstants.MINIMUM_DISTANCE_BEFORE_REROUTING;
}

double distanceToNextIntersection = TurfMeasurement.distance(snappedPoint, closestIntersection,
TurfConstants.UNIT_METERS);

if (distanceToNextIntersection <= NavigationConstants.MANEUVER_ZONE_RADIUS) {
return NavigationConstants.MINIMUM_DISTANCE_BEFORE_REROUTING / 2;
}
return NavigationConstants.MINIMUM_DISTANCE_BEFORE_REROUTING;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.mapbox.directions.v5.models.RouteLeg;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.utils.PolylineUtils;
import com.mapbox.services.android.navigation.v5.BaseTest;
import com.mapbox.services.constants.Constants;
import com.mapbox.turf.TurfConstants;
Expand All @@ -22,7 +23,9 @@
import org.junit.Test;

import java.io.IOException;
import java.util.List;

import static junit.framework.Assert.*;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;

Expand Down Expand Up @@ -326,7 +329,7 @@ public void getDurationRemaining_equalsCorrectValueAtIntervals() {

RouteStepProgress routeStepProgress = routeProgress.currentLegProgress().currentStepProgress();
double fractionRemaining = (firstStep.distance() - distance) / firstStep.distance();
Assert.assertEquals((1.0 - fractionRemaining) * firstStep.duration(),
assertEquals((1.0 - fractionRemaining) * firstStep.duration(),
routeStepProgress.durationRemaining(), BaseTest.LARGE_DELTA);
}

Expand All @@ -345,4 +348,47 @@ public void getDurationRemaining_equalsZeroAtEndOfStep() {
RouteStepProgress routeStepProgress = routeProgress.currentLegProgress().currentStepProgress();
assertEquals(0, routeStepProgress.durationRemaining(), BaseTest.DELTA);
}

@Test
public void stepIntersections_includesAllStepIntersectionsAndNextManeuver() throws Exception {
RouteProgress routeProgress = RouteProgress.builder()
.stepDistanceRemaining(0)
.legDistanceRemaining(firstLeg.distance())
.distanceRemaining(route.distance())
.directionsRoute(route)
.stepIndex(3)
.legIndex(0)
.build();
RouteStepProgress routeStepProgress = routeProgress.currentLegProgress().currentStepProgress();

int currentStepTotal = route.legs().get(0).steps().get(3).intersections().size();
Point maneuverLocation = route.legs().get(0).steps().get(4).maneuver().location();

assertEquals(currentStepTotal + 1, routeStepProgress.intersections().size());
assertEquals(routeStepProgress.intersections().get(16).location().latitude(), maneuverLocation.latitude());
assertEquals(routeStepProgress.intersections().get(16).location().longitude(), maneuverLocation.longitude());
}

@Test
public void stepIntersections_handlesNullNextManeuverCorrectly() throws Exception {
int lastStepIndex = (route.legs().get(0).steps().size() - 1);

RouteProgress routeProgress = RouteProgress.builder()
.stepDistanceRemaining(0)
.legDistanceRemaining(firstLeg.distance())
.distanceRemaining(route.distance())
.directionsRoute(route)
.stepIndex(lastStepIndex)
.legIndex(0)
.build();
RouteStepProgress routeStepProgress = routeProgress.currentLegProgress().currentStepProgress();

int currentStepTotal = route.legs().get(0).steps().get(lastStepIndex).intersections().size();
List<Point> lastStepLocation = PolylineUtils.decode(
route.legs().get(0).steps().get(lastStepIndex).geometry(), Constants.PRECISION_6);

assertEquals(currentStepTotal, routeStepProgress.intersections().size());
assertEquals(routeStepProgress.intersections().get(0).location().latitude(), lastStepLocation.get(0).latitude());
assertEquals(routeStepProgress.intersections().get(0).location().longitude(), lastStepLocation.get(0).longitude());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.mapbox.services.android.navigation.v5.utils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mapbox.directions.v5.DirectionsAdapterFactory;
import com.mapbox.directions.v5.models.DirectionsResponse;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.utils.PolylineUtils;
import com.mapbox.services.android.navigation.v5.BaseTest;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.services.constants.Constants;
import com.mapbox.turf.TurfConstants;
import com.mapbox.turf.TurfMeasurement;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import java.util.List;

import static com.mapbox.services.constants.Constants.PRECISION_6;
import static junit.framework.Assert.assertEquals;

public class ToleranceUtilsTest extends BaseTest {

private static final String DIRECTIONS_FIXTURE = "single_intersection.json";

private DirectionsResponse response;

@Before
public void setUp() throws Exception {
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(DirectionsAdapterFactory.create()).create();
String json = loadJsonFixture(DIRECTIONS_FIXTURE);
response = gson.fromJson(json, DirectionsResponse.class);
}

@Test
public void dynamicRerouteDistanceTolerance_userFarAwayFromIntersection() throws Exception {
RouteProgress routeProgress = RouteProgress.builder()
.directionsRoute(response.routes().get(0))
.legDistanceRemaining(0)
.distanceRemaining(0)
.stepIndex(0)
.legIndex(0)
.build();

// Get a point on the route step which isn't close to an intersection.
List<Point> stepPoints = PolylineUtils.decode(response.routes().get(0).geometry(), PRECISION_6);
Point midPoint = TurfMeasurement.midpoint(stepPoints.get(0), stepPoints.get(1));

double tolerance = ToleranceUtils.dynamicRerouteDistanceTolerance(midPoint, routeProgress);

assertEquals(50.0, tolerance, DELTA);
}


@Test
public void dynamicRerouteDistanceTolerance_userCloseToIntersection() throws Exception {
RouteProgress routeProgress = RouteProgress.builder()
.directionsRoute(response.routes().get(0))
.legDistanceRemaining(0)
.distanceRemaining(0)
.stepIndex(0)
.legIndex(0)
.build();

double distanceToIntersection = response.routes().get(0).distance() - 39;
LineString lineString = LineString.fromPolyline(response.routes().get(0).geometry(), Constants.PRECISION_6);
Point closePoint
= TurfMeasurement.along(lineString, distanceToIntersection, TurfConstants.UNIT_METERS);

double tolerance = ToleranceUtils.dynamicRerouteDistanceTolerance(closePoint, routeProgress);

assertEquals(25.0, tolerance, DELTA);
}

@Test
public void dynamicRerouteDistanceTolerance_userJustPastTheIntersection() throws Exception {
RouteProgress routeProgress = RouteProgress.builder()
.directionsRoute(response.routes().get(0))
.legDistanceRemaining(0)
.distanceRemaining(0)
.stepIndex(0)
.legIndex(0)
.build();

double distanceToIntersection = response.routes().get(0).distance();
LineString lineString = LineString.fromPolyline(response.routes().get(0).geometry(), Constants.PRECISION_6);
Point closePoint
= TurfMeasurement.along(lineString, distanceToIntersection, TurfConstants.UNIT_METERS);

double tolerance = ToleranceUtils.dynamicRerouteDistanceTolerance(closePoint, routeProgress);

assertEquals(25.0, tolerance, DELTA);
}
}
Loading

0 comments on commit 6af3488

Please sign in to comment.