Skip to content

Commit

Permalink
Compare routes using step names
Browse files Browse the repository at this point in the history
  • Loading branch information
kmadsen committed Jul 8, 2020
1 parent fcc9280 commit b53d874
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 24 deletions.
Expand Up @@ -23,6 +23,7 @@ import com.mapbox.navigation.base.internal.extensions.applyDefaultParams
import com.mapbox.navigation.base.internal.extensions.coordinates
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.core.directions.session.RoutesRequestCallback
import com.mapbox.navigation.core.fasterroute.FasterRouteObserver
import com.mapbox.navigation.core.replay.MapboxReplayer
import com.mapbox.navigation.core.replay.ReplayLocationEngine
import com.mapbox.navigation.core.replay.route.ReplayRouteMapper
Expand All @@ -39,7 +40,7 @@ import kotlinx.android.synthetic.main.activity_replay_route_layout.*

/**
* This activity shows how to use the MapboxNavigation
* class with the Navigation SDK's [ReplayHistoryLocationEngine].
* class with the Navigation SDK's [MapboxReplayer] and [ReplayLocationEngine].
*/
class ReplayActivity : AppCompatActivity(), OnMapReadyCallback {

Expand Down Expand Up @@ -82,6 +83,13 @@ class ReplayActivity : AppCompatActivity(), OnMapReadyCallback {
navigationMapboxMap?.restoreFrom(state)
}
initializeFirstLocation()

mapboxNavigation?.attachFasterRouteObserver(object : FasterRouteObserver {
override fun onFasterRoute(currentRoute: DirectionsRoute, alternatives: List<DirectionsRoute>, isAlternativeFaster: Boolean) {
navigationMapboxMap?.drawRoutes(alternatives)
mapboxNavigation?.setRoutes(alternatives)
}
})
}
mapboxMap.addOnMapLongClickListener { latLng ->
mapboxMap.locationComponent.lastKnownLocation?.let { originLocation ->
Expand Down Expand Up @@ -110,7 +118,7 @@ class ReplayActivity : AppCompatActivity(), OnMapReadyCallback {
override fun onRoutesReady(routes: List<DirectionsRoute>) {
MapboxLogger.d(Message("route request success $routes"))
if (routes.isNotEmpty()) {
navigationMapboxMap?.drawRoute(routes[0])
navigationMapboxMap?.drawRoutes(routes)

val replayEvents = replayRouteMapper.mapGeometry(routes[0].geometry()!!)
mapboxReplayer.pushEvents(replayEvents)
Expand Down
Expand Up @@ -23,6 +23,7 @@ import com.mapbox.navigation.base.internal.extensions.applyDefaultParams
import com.mapbox.navigation.base.internal.extensions.coordinates
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.core.directions.session.RoutesRequestCallback
import com.mapbox.navigation.core.fasterroute.FasterRouteObserver
import com.mapbox.navigation.core.replay.MapboxReplayer
import com.mapbox.navigation.core.replay.ReplayLocationEngine
import com.mapbox.navigation.core.replay.history.CustomEventMapper
Expand Down Expand Up @@ -171,6 +172,13 @@ class ReplayHistoryActivity : AppCompatActivity() {
}
})

mapboxNavigation.attachFasterRouteObserver(object : FasterRouteObserver {
override fun onFasterRoute(currentRoute: DirectionsRoute, alternatives: List<DirectionsRoute>, isAlternativeFaster: Boolean) {
navigationContext?.navigationMapboxMap?.drawRoutes(alternatives)
navigationContext?.mapboxNavigation?.setRoutes(alternatives)
}
})

playReplay.setOnClickListener {
mapboxReplayer.play()
mapboxNavigation.startTripSession()
Expand Down Expand Up @@ -235,7 +243,7 @@ class ReplayHistoryActivity : AppCompatActivity() {
override fun onRoutesReady(routes: List<DirectionsRoute>) {
MapboxLogger.d(Message("route request success $routes"))
if (routes.isNotEmpty()) {
navigationContext?.navigationMapboxMap?.drawRoute(routes[0])
navigationContext?.navigationMapboxMap?.drawRoutes(routes)
navigationContext?.startNavigation()
}
}
Expand Down
Expand Up @@ -33,7 +33,9 @@ import com.mapbox.navigation.core.directions.session.DirectionsSession
import com.mapbox.navigation.core.directions.session.RoutesObserver
import com.mapbox.navigation.core.directions.session.RoutesRequestCallback
import com.mapbox.navigation.core.fasterroute.FasterRouteController
import com.mapbox.navigation.core.fasterroute.FasterRouteDetector
import com.mapbox.navigation.core.fasterroute.FasterRouteObserver
import com.mapbox.navigation.core.fasterroute.RouteComparator
import com.mapbox.navigation.core.internal.MapboxDistanceFormatter
import com.mapbox.navigation.core.internal.accounts.MapboxNavigationAccounts
import com.mapbox.navigation.core.internal.trip.service.TripService
Expand Down Expand Up @@ -195,7 +197,12 @@ class MapboxNavigation(
)
}

fasterRouteController = FasterRouteController(directionsSession, tripSession, logger)
fasterRouteController = FasterRouteController(
directionsSession,
tripSession,
logger,
FasterRouteDetector(RouteComparator())
)
routeRefreshController = RouteRefreshController(directionsSession, tripSession, logger)
routeRefreshController.start()

Expand Down
Expand Up @@ -15,11 +15,11 @@ import java.util.concurrent.TimeUnit
internal class FasterRouteController(
private val directionsSession: DirectionsSession,
private val tripSession: TripSession,
private val logger: Logger
private val logger: Logger,
private val fasterRouteDetector: FasterRouteDetector
) {

private val fasterRouteTimer = MapboxTimer()
private val fasterRouteDetector = FasterRouteDetector()
private var fasterRouteObserver: FasterRouteObserver? = null

fun attach(fasterRouteObserver: FasterRouteObserver) {
Expand Down
Expand Up @@ -3,11 +3,15 @@ package com.mapbox.navigation.core.fasterroute
import com.mapbox.api.directions.v5.models.DirectionsRoute
import com.mapbox.navigation.base.trip.model.RouteProgress

internal class FasterRouteDetector {
fun isRouteFaster(newRoute: DirectionsRoute, routeProgress: RouteProgress): Boolean {
val newRouteDuration = newRoute.duration() ?: return false
internal class FasterRouteDetector(
private val routeComparator: RouteComparator
) {

fun isRouteFaster(alternativeRoute: DirectionsRoute, routeProgress: RouteProgress): Boolean {
val alternativeDuration = alternativeRoute.duration() ?: return false
val weightedDuration = routeProgress.durationRemaining * PERCENTAGE_THRESHOLD
return newRouteDuration < weightedDuration
val isNewRouteFaster = alternativeDuration < weightedDuration
return isNewRouteFaster && routeComparator.isNewRoute(routeProgress, alternativeRoute)
}

companion object {
Expand Down
@@ -0,0 +1,50 @@
package com.mapbox.navigation.core.fasterroute

import com.mapbox.api.directions.v5.models.DirectionsRoute
import com.mapbox.api.directions.v5.models.LegStep
import com.mapbox.navigation.base.trip.model.RouteProgress

/**
* Compares if an alternative route is different from the current route progress.
*/
internal class RouteComparator {

private val mapLegStepToName: (LegStep) -> String = { it.name() ?: "" }

/**
* @param routeProgress current route progress
* @param alternativeRoute suggested new route
*
* @return true when the alternative route has different
* geometry from the current route progress
*/
fun isNewRoute(routeProgress: RouteProgress, alternativeRoute: DirectionsRoute): Boolean {
val currentDescription = routeDescription(routeProgress)
val alternativeDescription = alternativeDescription(alternativeRoute)
alternativeDescription.ifEmpty {
return false
}

return isNewRoute(currentDescription, alternativeDescription)
}

private fun routeDescription(routeProgress: RouteProgress): String {
val routeLeg = routeProgress.currentLegProgress?.routeLeg
val steps = routeLeg?.steps()
val stepIndex = routeProgress.currentLegProgress?.currentStepProgress?.stepIndex
?: return ""

return steps?.listIterator(stepIndex)?.asSequence()
?.joinToString(transform = mapLegStepToName) ?: ""
}

private fun alternativeDescription(directionsRoute: DirectionsRoute): String {
val steps = directionsRoute.legs()?.firstOrNull()?.steps()
return steps?.asSequence()
?.joinToString(transform = mapLegStepToName) ?: ""
}

private fun isNewRoute(currentGeometry: String, alternativeGeometry: String): Boolean {
return currentGeometry != alternativeGeometry
}
}
Expand Up @@ -27,15 +27,18 @@ class FasterRouteControllerTest {
val coroutineRule = MainCoroutineRule()

private val directionsSession: DirectionsSession = mockk()
private val tripSession: TripSession = mockk()
private val tripSession: TripSession = mockk {
every { getRouteProgress() } returns mockk()
}
private val fasterRouteObserver: FasterRouteObserver = mockk {
every { restartAfterMillis() } returns FasterRouteObserver.DEFAULT_INTERVAL_MILLIS
every { onFasterRoute(any(), any(), any()) } returns Unit
}
private val routesRequestCallbacks = slot<RoutesRequestCallback>()

private val logger: Logger = mockk()
private val fasterRouteController = FasterRouteController(directionsSession, tripSession, logger)
private val fasterRouteDetector: FasterRouteDetector = mockk()

private val fasterRouteController = FasterRouteController(directionsSession, tripSession, logger, fasterRouteDetector)

@Before
fun setup() {
Expand Down Expand Up @@ -111,25 +114,21 @@ class FasterRouteControllerTest {

@Test
fun `should notify observer of a faster route`() = coroutineRule.runBlockingTest {
every { fasterRouteDetector.isRouteFaster(any(), any()) } returns true
val currentRoute: DirectionsRoute = mockk {
every { routeIndex() } returns "0"
every { duration() } returns 801.332
}
every { directionsSession.routes } returns listOf(currentRoute)
every { tripSession.getEnhancedLocation() } returns mockk {
every { latitude } returns -33.874308
every { longitude } returns 151.206087
}
every { tripSession.getRouteProgress() } returns mockk {
every { durationRemaining } returns 601.334
}
every { directionsSession.requestFasterRoute(any(), capture(routesRequestCallbacks)) } returns mockk()

fasterRouteController.attach(fasterRouteObserver)
coroutineRule.testDispatcher.advanceTimeBy(TimeUnit.MINUTES.toMillis(6))
val routes = listOf<DirectionsRoute>(mockk {
every { routeIndex() } returns "0"
every { duration() } returns 351.013
})
routesRequestCallbacks.captured.onRoutesReady(routes)

Expand All @@ -141,25 +140,21 @@ class FasterRouteControllerTest {

@Test
fun `should notify observer if current route is fastest`() = coroutineRule.runBlockingTest {
every { fasterRouteDetector.isRouteFaster(any(), any()) } returns false
val currentRoute: DirectionsRoute = mockk {
every { routeIndex() } returns "0"
every { duration() } returns 801.332
}
every { directionsSession.routes } returns listOf(currentRoute)
every { tripSession.getEnhancedLocation() } returns mockk {
every { latitude } returns -33.874308
every { longitude } returns 151.206087
}
every { tripSession.getRouteProgress() } returns mockk {
every { durationRemaining } returns 751.334
}
every { directionsSession.requestFasterRoute(any(), capture(routesRequestCallbacks)) } returns mockk()

fasterRouteController.attach(fasterRouteObserver)
coroutineRule.testDispatcher.advanceTimeBy(TimeUnit.MINUTES.toMillis(6))
val routes = listOf<DirectionsRoute>(mockk {
every { routeIndex() } returns "0"
every { duration() } returns 951.013
})
routesRequestCallbacks.captured.onRoutesReady(routes)

Expand Down
Expand Up @@ -9,11 +9,15 @@ import org.junit.Assert.assertTrue
import org.junit.Test

class FasterRouteDetectorTest {
private val routeComparator: RouteComparator = mockk {
every { isNewRoute(any(), any()) } returns true
}

private val fasterRouteDetector = FasterRouteDetector()
private val fasterRouteDetector = FasterRouteDetector(routeComparator)

@Test
fun shouldDetectWhenRouteIsFaster() {
every { routeComparator.isNewRoute(any(), any()) } returns true
val newRoute: DirectionsRoute = mockk()
every { newRoute.duration() } returns 402.6
val routeProgress: RouteProgress = mockk()
Expand All @@ -24,6 +28,19 @@ class FasterRouteDetectorTest {
assertTrue(isFasterRoute)
}

@Test
fun shouldDetectWhenRouteIsFasterOnlyIfDifferent() {
every { routeComparator.isNewRoute(any(), any()) } returns false
val newRoute: DirectionsRoute = mockk()
every { newRoute.duration() } returns 402.6
val routeProgress: RouteProgress = mockk()
every { routeProgress.durationRemaining } returns 797.447

val isFasterRoute = fasterRouteDetector.isRouteFaster(newRoute, routeProgress)

assertFalse(isFasterRoute)
}

@Test
fun shouldDetectWhenRouteIsSlower() {
val newRoute: DirectionsRoute = mockk()
Expand Down

0 comments on commit b53d874

Please sign in to comment.