-
Notifications
You must be signed in to change notification settings - Fork 313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable simulated navigation for tunnel segments. #1218
Conversation
# Conflicts: # MapboxCoreNavigation/Constants.swift
# Conflicts: # MapboxCoreNavigation/RouteController.swift # MapboxNavigation/NavigationViewController.swift # MapboxNavigation/StyleManager.swift
…to start/stop animation.
if let currentIntersection = routeProgress.currentLegProgress.currentStepProgress.currentIntersection, | ||
let classes = currentIntersection.outletRoadClasses { | ||
if classes.contains(.tunnel) { | ||
startTunnelAnimation(for: manager, routeProgress: routeProgress, distanceTraveled: distanceTraveled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is distanceTraveled
being passed in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When location simulation is needed in special cases such as dead reckoning
, the distanceTraveled
on the given route determines where the SimulatedLocationManager
needs to begin the simulation.
… SimulatedLocationManager. Additional logic to stop simulated location updates when user halts navigation.
- parameter distanceTraveled: The distance traveled on the current route. | ||
- returns: A `SimulatedLocationManager` | ||
*/ | ||
@objc public init(route: Route, distanceTraveled: CLLocationDistance) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be more versatile if we would pass a from:CLLocationCoordinate2D,to:CLLocationCoordinate2D
?
As it stands now, we don't stop simulation after simulating through a tunnel if you would get stuck or slow down in the middle, right? Slicing the route could possibly fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frederoni a couple of things.
- Yes, we stop simulation after simulating through a tunnel
stopTunnelAnimation(for: manager) - We don't need to slice, as the
SimulatedLocationManager
provides us with a robust data based on thecurrentDistance
(a.k.a. distanceTraveled). Given thecurrentDistance
, the manager can determine the current location on the givenroute
- which is used to determine factors such as expectedSegmentTravelTimes, closestCoordinateOnRoute, nextCoordinateOnRoute and time.
guard let newCoordinate = polyline.coordinateFromStart(distance: currentDistance) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
I overlooked this part:
mapbox-navigation-ios/MapboxCoreNavigation/RouteController.swift
Lines 583 to 585 in 544c5d1
} else { | |
stopTunnelAnimation(for: manager) | |
} |
and thought we would just continue simulating to the destination.
simulatedLocationManager?.delegate = self | ||
simulatedLocationManager?.routeProgress = routeProgress | ||
|
||
let dispatchGroup = DispatchGroup() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the reasoning behind using this dispatch group?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frederoni Sure.
This ensures we don't encounter any concurrency (dispatch) race between the local locationManager
and the simulatedLocationManager
. Basically, when we dispatch using a group, we are assured that our local locationManager
will stop stopUpdatingLocation
before the simulatedLocationManager
starts startUpdatingLocation
.
This facilitates a smooth handshake between using our RouteController.locationManager
and RouteController.simulatedLocationManager
where we will not have both running at the same time or vice versa.
Would replaying using ReplayLocationManager also make use of SimulatedLocationManager? |
@frederoni Yes, I've considered using a delegate approach in Also,
|
@@ -232,7 +232,9 @@ open class RouteController: NSObject { | |||
} | |||
|
|||
var userSnapToStepDistanceFromManeuver: CLLocationDistance? | |||
|
|||
|
|||
var simulatedLocationManager: SimulatedLocationManager? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this to tunnelLocationManager or similar to avoid confusion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frederoni this simulatedLocationManager
is intentional. It can be used for other context of dead reckoning (such as highways, motorways, tolls etc.) not just tunnel navigation. I had followed up with @brsbl regarding this simulated navigation effort for dead reckoning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, how about animatedLocationManager
then? simulator
should be reserved for the actual simulator in the sdk I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 maybe we should have a MapboxLocationManager
to manage all of these real/faux CLLocationManager
objects? Something to combine the (three now?) native managers (simulated, native, and tunnel) into one concern/interface. There would also be some advantages here in testability, dynamically enabling/disabling route sim, and handoff between one location manager and another, say when entering/exiting a tunnel.
It doesn't have to happen now (or ever), just an idea.
/cc @akitchen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JThramer I concur. We should have a LocationManager to handle the 3 variations of location manager. @frederoni, @1ec5 and I had a chat and we concluded there is definitely candidacy for ReplayLocationManager
, SimulatedLocationManager
to be tied to the location manager, MapboxLocationManager
. I intend to track this in a new issue ticket for an upcoming PR.
/cc @bsudekum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still disagree about this. Names are cheap, and this variable name tips off the developer that this SimulatedLocationManager
is used for simulation. While we are simulating, this is particular kind of simulation that we should distinguish from others.
Even if this variable will be used for other types of animation in the future, we can still come up with a better name now that does not lead to confusion.
@@ -353,6 +355,11 @@ open class RouteController: NSObject { | |||
@objc public func suspendLocationUpdates() { | |||
locationManager.stopUpdatingLocation() | |||
locationManager.stopUpdatingHeading() | |||
|
|||
// In case simulated navigation is abruptly stopped before completion, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this sentence is missing a few the
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frederoni Haha yeah - facepalm (an old habit of eliminating a few prepositions to keep the comments concise 😸) Good looks. Thanks 👍
… for qualified coordinates.
…de clean up. Fixed a few bugs for simulation.
…TunnelAnimation(for:routeController:routeProgress:callback:)
# Conflicts: # MapboxNavigation.xcodeproj/project.pbxproj
|
||
var tunnelIntersectionManagerCompletionHandler: RouteControllerSimulationCompletionBlock? | ||
|
||
public var tunnelSimulationFeatureEnabled: Bool = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say either make these private if they can be or document them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, rename this to tunnelSimulationEnabled
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum we need to make it public. The developer needs a way to enable the tunnel simulation feature.
currentDistance = 0 | ||
currentSpeed = 30 | ||
currentSpeed = 0 | ||
currentDistance = distance + (currentSpeed * speedMultiplier) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add a new function here for these changes. Also, the units here do not match. We're adding a distance (meters) to a speed (meters per second).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum this is an already existing logic. Remember we had a prior discussion: #1218 (comment)
Should we probably leave some comment in the code for it, just to prevent a confusion in the future?
} | ||
|
||
updateDistanceToIntersection(from: location) | ||
updateRouteStepProgress(for: location) | ||
updateRouteLegProgress(for: location) | ||
|
||
guard userIsOnRoute(location) || !(delegate?.routeController?(self, shouldRerouteFrom: location) ?? true) else { | ||
guard tunnelAnimationEnabled || userIsOnRoute(location) || !(delegate?.routeController?(self, shouldRerouteFrom: location) ?? true) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly concerned with this. If the tunnel animation taking place, and the user is obviously outside of the tunnel, someplace else, we won't reroute them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum We will reroute. Basically, whenever we get a valid location update we suspend tunnel animation demonstrated here: https://github.com/mapbox/mapbox-navigation-ios/pull/1218/files#diff-987c4bbd828e654bd63d59215f890022R129
Hence, the default navigation location manager will kick in and reroute if needed.
|
||
guard isAnimationEnabled else { return } | ||
|
||
// Disable the tunnel animation after at least 3 bad location updates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It think it might make more sense to disable tunnel animation after 3 good location updates. If we get 3 bad location updates in a row, we should not do anything IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum just for the record, this might delay style switches and delay in puck movements in case the driver exits the tunnel very quickly. (PS: I will still proceed with the suggested changes.)
tunnelIntersectionManager?.delegate = self | ||
tunnelIntersectionManagerCompletionHandler = { enabled, _ in | ||
self.tunnelIntersectionManager?.isAnimationEnabled = enabled | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be cleaner to wrap this in an initTunnelManager
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Allow the user puck to advance. A stationary puck is not great. | ||
self.rawLocation = lastLocation | ||
|
||
// Check for a tunnel intersection at the current step we found the bad location update. | ||
checkForTunnelIntersection(at: lastLocation, for: manager) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move this this out of this if statement and put it below. This function should use location
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum shouldn't the simulation move along with the puck as indicated in the comment above
Allow the user puck to advance. A stationary puck is not great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum also when you say "I'd move this out of this if statement and put it below."... do you mean, moving this out of the conditional statement:
if let lastLocation = locations.last, delegate?.routeController?(self, shouldDiscard: lastLocation) ?? true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to at least here:
self.rawLocation = location |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum It already resides here:
checkForTunnelIntersection(at: location, for: manager) |
(Thus where step’s remaining distance has changed.)
The reason the check for tunnel lives here is to ensure whenever:
filteredLocations
does not contain good locations and we have found at least one good location previously.
and... to follow suit as we advance the puck as indicated here:
https://github.com/mapbox/mapbox-navigation-ios/pull/1218/files#diff-1a2149692b3d7c58b6a0392120d6011aL549
... unless I'm missing something, here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per discussion via slack, we decided to keep this function here.
/cc @bsudekum
callback: callback) | ||
} | ||
|
||
public func tunnelIntersectionManager(_ manager: CLLocationManager, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions need documentation since they are public.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bsudekum Hey, it's already documented in the TunnelIntersectionManagerDelegate
class: https://github.com/mapbox/mapbox-navigation-ios/pull/1218/files#diff-987c4bbd828e654bd63d59215f890022R20
…ded a documentation to the tunnel simulation enabled property.
…ction Manager class responsible for enabling simulated navigation through tunnels.
…tion through tunnels feature.
…g. Added a function determine the current distance. Updated custom initializer for simulated location manager.
This will enable the simulated navigation whenever the commuter enters a tunnel until they exit the tunnel.
Closes #1275
TODO: