Skip to content
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 night style when inside tunnel #1127

Merged
merged 62 commits into from
Mar 13, 2018
Merged

Conversation

vincethecoder
Copy link
Contributor

@vincethecoder vincethecoder commented Feb 17, 2018

This detects tunnels on a routeStep and sets the style to DayStyle.
cc @mapbox/navigation-ios

Issue addressed: #559, #1170


NOTE:
When calculating the distance to the tunnel entrance using Polyline's distance(from:to:), there was inconsistent results. This issue is similar to the reported issue here: mapbox/turf-swift#27

In this case, the distance to the tunnel entrance reports lower values as it approaches the entrance. However, in the mid-way of the tunnel commute, it suddenly begins to jump to higher values (greater than the tunnel distance) and then reduces to lower values before it exits the tunnel. The screenshot below highlights these inconsistencies:

screen shot 2018-02-19 at 4 00 39 pm

@@ -635,6 +646,10 @@ extension RouteController: CLLocationManagerDelegate {
updateRouteStepProgress(for: location)
updateRouteLegProgress(for: location)

if routeProgress.currentLegProgress.currentStep.containsTunnel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this if into detectRouteStepInTunnel(for:) and return early if it's true.

// - Current location to exit must be greater than zero meters.
// - Distance to tunnel entrance must be less than tunnel distance.
// - Distance to tunnel exit must also be less than tunnel distance.
if (tunnelDistance - distanceToExit) > 0 && distanceToEntrance <= tunnelDistance && distanceToExit <= tunnelDistance {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's possible to have only 2 conditions here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum Yes indeed. Good looks 👍

/**
Returns the entry and exit intersections of the tunnel on the current route step
*/
var tunnelIntersection: (entry: Intersection, exit: Intersection)? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tunnelIntersection denotes a single intersection, while in reality, we're calculating the entry and exit of a tunnel here. Could you come up with a more descriptive variable name for this variable?

@@ -561,6 +561,14 @@ extension NavigationViewController: RouteControllerDelegate {
}
return advancesToNextLeg
}

@objc public func routeController(_ routeController: RouteController, didEnterTunnelAt location: CLLocation?) {
if let _ = location {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact we're not using location here might clue us in that it may not be necessary to send this param over.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

*/
var tunnelDistance: CLLocationDistance? {
guard let tunnelIntersection = tunnelIntersection else { return nil }
return tunnelIntersection.entry.location.distance(to: tunnelIntersection.exit.location)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This distance calculation is an absolute point to point distance. Instead, we should be using a function which calculates distance along a line. I believe Polyline is a struct and contains a distance variable. It may be possible to remove this and just use tunnelSlice.distance.

guard let intersections = intersections, containsTunnel else { return nil }
for i in 0..<(intersections.count) where intersections.count > 1 {
if intersections[i].outletRoadClasses == .tunnel {
return (entry: intersections[i], exit: intersections[i+1])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check that i + 1 is always within bounds before returning here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line #77 already does that check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't line 77 only checking where the number of intersections is greater that 1? Not necessarily that the current index in the loop has a follow on index.


@objc public func routeController(_ routeController: RouteController, didEnterTunnelAt location: CLLocation?) {
if let _ = location {
self.styleManager.applyStyle(NightStyle())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could first check if the style needs to be updated before continually updating the style on every location update?

Copy link
Contributor Author

@vincethecoder vincethecoder Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum I do a safety check before I apply the style in applyStyle(_:) on line #111.

@@ -96,7 +96,7 @@ open class StyleManager: NSObject {
resetTimeOfDayTimer()
}

func applyStyle() {
func applyStyle(_ newStyle: Style? = nil) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to avoid the special case for custom style below if we’d take a StyleType instead of a specific style here.

Copy link
Contributor Author

@vincethecoder vincethecoder Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@frederoni you're right 👍

@vincethecoder
Copy link
Contributor Author

@bsudekum and @frederoni Thanks for your feedback 👍

@@ -561,6 +561,14 @@ extension NavigationViewController: RouteControllerDelegate {
}
return advancesToNextLeg
}

@objc public func didEnterTunnel(_ darkModeEnabled: Bool) {
Copy link
Contributor

@bsudekum bsudekum Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darkModeEnabled makes it seem like it was already enabled.

@@ -96,6 +96,21 @@ open class StyleManager: NSObject {
resetTimeOfDayTimer()
}

func applyStyle(type styleType: StyleType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this func be combined with the applyStyle below?

Copy link
Contributor Author

@vincethecoder vincethecoder Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I considered it. However, this func applyStyle(type:) is cleaner, since it requires the styleType. The other func applyStyle scope is getting a bit bigger and complex with more conditions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UPDATES: I also refactored applyStyle so that we have a function applyStyle(type:) to contain styleType related logic.

- parameter darkModeEnabled: The darkModeEnabled flag determines whether a `DarkStyle` should apply.
*/
@objc(didEnterTunnel:)
optional func didEnterTunnel(_ darkModeEnabled: Bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the notion of dark mode from RouteController because MapboxCoreNavigation deals with non-UI related functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Will do.

// Pre-conditions:
// - Current location to exit must be greater than zero meters.
// - Distance to tunnel entrance must be less than tunnel distance.
if (tunnelDistance - distanceToExit) > 0 && distanceToEntrance <= tunnelDistance {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would something like

if distanceToEntrance <= tunnelDistance && distanceToExit <= tunnelDistance {
...

I only suggest this since it's easier to understand what we're comparing on each side of the && operator.

let distanceToEntrance = currentLocation.distance(to: tunnelStartCoordinate)
let distanceToExit = currentLocation.distance(to: tunnelEndCoordinate)

// Capute any location which lies within the tunnel route.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: no need for route in tunnel route. Also, Capute -> Compute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capture -- misspelt

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great – thanks for managing this deluge of feedback and iterating on the approach. 😄 Since this has been a pretty long review, here are a few outstanding pieces of feedback to consider before merging:

@@ -417,21 +417,10 @@ open class RouteStepProgress: NSObject {
}

/**
Returns all the intersection distances on the current step.
Returns an array of the calculated distances from the current intersection to the next intersection on the current step.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this is a property, not a method, so it is set to the array rather than returning the array.

/**
A Boolean value that determines whether the dark style should be automatically activated when a route controller enters a tunnel.
*/
@objc public var automaticallySwitchStyleForTunnels: Bool = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use the declarative form instead of the imperative form when naming a property. In Objective-C, a property’s getter can look identical to an action method:

// Does this line immediately switch the style?
[navigationViewController automaticallySwitchStyleForTunnels];

A clearer name would be automaticallySwitchesStyleInsideTunnels or usesNightStyleInsideTunnels.

Copy link
Contributor Author

@vincethecoder vincethecoder Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I prefer to use enableNightStyleInsideTunnelFeatureEnabled to hint that variable is a feature flag. Just my .

@@ -388,21 +388,39 @@ open class RouteStepProgress: NSObject {
/**
The next intersection the user will travel through.

The step must contains `Intersections` for this value not be `nil`.
The step must contain `intersectionsIncludingUpcomingManeuverIntersection` for this value not be `nil`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still unclear, because intersectionsIncludingUpcomingManeuverIntersection is a property of RouteStepProgress, not RouteStep.

If intersectionsIncludingUpcomingManeuverIntersection is nil, this property is also nil.

/**
The minimum distance of a tunnel intersection on a given route step.
*/
public var MBMinimumTunnelLengthForUsingNightStyle: CLLocationDistance = 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the intent was to address the feedback in #1127 (comment) by moving this constant, but I don’t see where it got moved to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@1ec5 we're not using this constant at the moment. The tunnel distance check will be done on my upcoming PR for simulation of the tunnel navigation.

…and upcoming intersections, along with their corresponding calculated distances.
@1ec5 1ec5 assigned vincethecoder and unassigned 1ec5 Mar 8, 2018
…ersections bounds. Modified description text for current and upcoming intersections. Modified variable to indicate when night style should be enabled inside a tunnel.
…ck on intersections bounds to initial state.
@@ -409,7 +409,7 @@ public class NavigationViewController: UIViewController {

mapViewController?.notifyDidChange(routeProgress: routeProgress, location: location, secondsRemaining: secondsRemaining)

if automaticallySwitchStyleForTunnels,
if enableNightStyleInsideTunnelFeatureEnabled,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property has the word enable(d) in it twice. I too am a fan of suggestion - automaticallySwitchesStyleInsideTunnels.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our discussion, this variable will be usesNightStyleInsideTunnels.

cc @bsudekum @1ec5

@@ -628,6 +628,10 @@ extension RouteController: CLLocationManagerDelegate {
let currentStepProgress = routeProgress.currentLegProgress.currentStepProgress
let currentStep = currentStepProgress.step

let intersectionDistances = routeProgress.currentLegProgress.currentStepProgress.intersectionDistances
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user has set NavigationViewController.automaticallyChangeStyleForTunnel to false, is it necessary to perform these calculations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum FYI

  • It shouldn't be restricted to automaticallyChangeStyleForTunnel. It should be wrapped within a isDeadReckoningEnabled flag.
  • This calculation updates the intersectionIndex - fix for issue Intersection index is always 0 #1170. @1ec5 do you need to use this fix for a task unrelated to dead reckoning?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vincethecoder I don't think this code should be intertwined with isDeadReckoningEnabled. Let's keep these as separate ideas for now.

IMO, the fix here is to move NavigationViewController.automaticallyChangeStyleForTunnel to RouteController and rename it to something more appropriate.

Copy link
Contributor Author

@vincethecoder vincethecoder Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum I believe it was mentioned in one of my PR comments to separate the UI-related things in NavigationViewController from RouteController. Now, I'm totally thrown for loop 🤔
cc @1ec5 - do you recall this discussion?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in #1127 (comment) I recommended keeping UI-related stuff out of Core Navigation. I stand by that recommendation.

@vincethecoder vincethecoder changed the title [Tunnel Navigation] - Enable night mode when inside a tunnel Enable night style when inside tunnel Mar 8, 2018
routeController.isDeadReckoningEnabled = true
} else {
styleManager.timeOfDayChanged()
routeController.isDeadReckoningEnabled = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is isDeadReckoningEnabled being mutated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to continue mark the intersection as a candidate for dead reckoning.

@@ -942,6 +948,12 @@ extension RouteController: CLLocationManagerDelegate {
} else {
routeProgress.currentLegProgress.stepIndex += 1
}

if isDeadReckoningEnabled, let coordinates = routeProgress.currentLegProgress.currentStep.coordinates, let intersections = routeProgress.currentLegProgress.currentStep.intersections {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If intersectionDistances is only set when dead reckoning is enabled, then the documentation for that property should say so.

Copy link
Contributor Author

@vincethecoder vincethecoder Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. As soon as we (@bsudekum, @1ec5, myself) gain clarity on whether we are wrapping this inside a isDeadReckoningEnabled flag, I will make a final update on the intersectionDistances property.

Copy link
Contributor

@bsudekum bsudekum Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code should not be influenced by dead reckoning. Remove if isDeadReckoningEnabled from this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed - post our discussion on slack 😺

@bsudekum bsudekum modified the milestones: v0.15.0, v0.16.x Mar 9, 2018
@bsudekum bsudekum modified the milestone: v0.16.x Mar 12, 2018
@vincethecoder vincethecoder merged commit 2acbe46 into master Mar 13, 2018
This was referenced Mar 20, 2018
@vincethecoder vincethecoder deleted the tunnel/dark_mode_style branch May 2, 2018 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants