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

Add waypoint handling #270

Merged
merged 71 commits into from
Aug 24, 2017
Merged

Add waypoint handling #270

merged 71 commits into from
Aug 24, 2017

Conversation

bsudekum
Copy link
Contributor

@bsudekum bsudekum commented Jun 8, 2017

To do:

  • Add waypoint handling to example app
  • Improve instructions upon approaching leg destination
  • [x] Add UI elements for arriving and leaving waypoint

/cc @1ec5 @frederoni @cammace @ericrwolfe

@bsudekum bsudekum added the ⚠️ DO NOT MERGE PR should not be merged! label Jun 8, 2017
@@ -72,14 +72,14 @@ extension RouteTableViewController: UITableViewDelegate, UITableViewDataSource {
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return routeController.routeProgress.currentLeg.steps.count
return routeController.routeProgress.allSteps.count
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of flattening the list of steps, we should have multiple sections (instead of just one), each section listing the steps for a single leg.

@bsudekum
Copy link
Contributor Author

Some notes,

  • I deprecated destination. It was never really needed and with waypoints, things get a little more confusing if there is a destination option.
  • didArriveAt changed to now return the route step since there really is not a destination annotation anymore. It also will only return when the user arrives at the end of the route. However, we could change this to return at ever waypoint.
  • In terms of how this will fit into existing code bases, I think it would just work. There are improvements we could make to how waypoints are handled (adding a UI namely), but as a first step, I think this is good and would unblock any users looking for waypoint support.

/cc @1ec5 @frederoni

routeProgress.remainingWaypoints.count > 1 {
routeProgress.legIndex += 1
routeProgress.currentLegProgress.stepIndex = 0
routeProgress.currentLegProgress.alertUserLevel = .depart
Copy link
Contributor

Choose a reason for hiding this comment

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

Changing legIndex automatically resets currentLegProgress.

@@ -124,8 +124,10 @@ public class NavigationViewController: NavigationPulleyViewController, RouteMapV
on the destination of your route. The last coordinate of the route will be
used if no destination is given.
*/
@available(*, deprecated, message: "Destination is no longer support nor necessary. A destination annotation will automatically be added to map given the route.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that this shouldn’t have much of an impact on most developers: it isn’t possible to customize this annotation (#213) other than changing its title, subtitle, and coordinates, and none of those properties are likely to be modified by the developer anyways.

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 it was actually added in #268

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I missed that we’re exposing navigationMapView(_:imageFor:). In any case, that method will continue to work; it just won’t be possible to say something like if annotation == navigationViewController.destination.

@@ -124,8 +124,10 @@ public class NavigationViewController: NavigationPulleyViewController, RouteMapV
on the destination of your route. The last coordinate of the route will be
used if no destination is given.
*/
@available(*, deprecated, message: "Destination is no longer support nor necessary. A destination annotation will automatically be added to map given the route.")
Copy link
Contributor

Choose a reason for hiding this comment

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

s/support/supported/

annotation.coordinate = route.coordinates!.last!
destination = annotation
for leg in route.legs {
if let last = leg.steps.last {
Copy link
Contributor

Choose a reason for hiding this comment

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

This code only places an annotation at the last destination. Should we annotate the intermediate waypoints as well?

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 hrm, no this annotates the last step of each leg. Or at least it's supposed to.

Copy link
Contributor

Choose a reason for hiding this comment

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

You’re right, I misread this line.

let annotation = MGLPointAnnotation()
annotation.coordinate = route.coordinates!.last!
destination = annotation
for leg in route.legs {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we’re only looking at the last step, there’s no need to iterate over all the steps:

if let lastStep = route.legs.last?.steps.last {

navigationDelegate?.navigationViewController?(self, didArriveAt: destination)

if routeProgress.currentLegProgress.alertUserLevel == .arrive,
routeProgress.remainingWaypoints.count == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the developer be notified when the user reaches an intermediate waypoint? I could see use cases for only responding to the final arrival and for responding to intermediate arrivals as well. Perhaps the delegate method should have an additional argument, numberOfWaypointsRemaining, to allow the developer to distinguish between the two cases.

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 I was thinking about this but wanted to keep the API the same. I think if we want to notify them when they reach a waypoint, we should either rename the method or add a new method for waypoints.

Copy link
Contributor

Choose a reason for hiding this comment

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

The method’s current name implies that it’s called whenever the user reaches a waypoint once we added support for multiple waypoints. Why else would it take a second argument?

How about we add a new method, navigationViewControllerDidArrive(_:), that gets called after navigationViewController(_:didArriveAt:) when arriving at the final waypoint?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed it to a single method that returns the waypoint the user is arriving at for each and every waypoint.

}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Leg \(section + 1)"
Copy link
Contributor

Choose a reason for hiding this comment

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

The leg number is unimportant to the user. Instead, we should title the section with either of the following formats, preferably the first one:

  • “Biloxi to Mobile” (see leg.source, leg.destination)
  • “I-10” (see leg.name)

Copy link
Contributor

Choose a reason for hiding this comment

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

}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Leg \(section + 1)"
Copy link
Contributor

Choose a reason for hiding this comment

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

If there is only one leg, there’s no need for a section header.


cell.step = leg.steps[indexPath.row]
cell.step = legs[indexPath.section].steps[indexPath.row]
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: funky indentation.


if routeController.routeProgress.currentLegProgress.stepIndex + 1 > indexPath.row {
if routeController.routeProgress.legIndex + 1 > indexPath.section && routeController.routeProgress.currentLegProgress.stepIndex + 1 > indexPath.row {
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 is on step 5 of leg 1, only the first five steps of section 0 are dimmed out.

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 I think that's how it should work though. Are you saying the current step should be dimmed?

Copy link
Contributor

@1ec5 1ec5 Jun 14, 2017

Choose a reason for hiding this comment

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

If leg 0 has 10 steps, leg 1 has 10 steps, and the user is on step 5 of leg 2 which has 10 steps, why would all three legs be halfway dimmed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, this code is incorrect but in a different way than I first thought. This code is equivalent to: if currentLegIndex >= section && currentLegProgress.stepIndex >= row { dim the cell }. Now if the user is on step 5 of leg 2, the only dimmed cells are those that meet both the following criteria:

  1. the section number is at least 2; and
  2. the row number is at least 5

In other words:

  • Sections 0 and 1 are completely lit, because they fail criteria (1). 🙅‍♂️
  • Section 2 is dimmed up to row 4 and lit beginning with row 5. 👍
  • Sections 3, 4, etc. are each dimmed up to row 4 but lit beginning with row 5, because rows 5 and beyond fail criteria (2). 🙅‍♂️

return nil
}

let legToFrom = routeController.routeProgress.route.legs[section].description.components(separatedBy: ", ")
Copy link
Contributor

Choose a reason for hiding this comment

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

RouteLeg.description is equivalent to RouteLeg.name. name lists the main streets along the leg, not the leg’s origin and destination. For example, name might be “Market Street, I-10, Main Street”. If you want “Biloxi” and “Mobile”, you need to look at the name properties of RouteLeg’s source and destination properties, which won’t be very useful until mapbox/mapbox-directions-swift#135 is fixed.

@bsudekum bsudekum removed the ⚠️ DO NOT MERGE PR should not be merged! label Jun 16, 2017
@bsudekum bsudekum changed the title Add waypoint handling [WIP] Add waypoint handling Jun 16, 2017
@bsudekum
Copy link
Contributor Author

This is ready for another round of reviews

@@ -20,7 +20,7 @@ public protocol NavigationViewControllerDelegate {
/**
Called when the user arrives at the destination.
*/
@objc optional func navigationViewController(_ navigationViewController : NavigationViewController, didArriveAt destination: MGLAnnotation)
@objc optional func navigationViewController(_ navigationViewController : NavigationViewController, didArriveAt destination: Waypoint)
Copy link
Contributor

Choose a reason for hiding this comment

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

If this gets called on every waypoint, then destination should be waypoint.

@@ -20,7 +20,7 @@ public protocol NavigationViewControllerDelegate {
/**
Called when the user arrives at the destination.
*/
@objc optional func navigationViewController(_ navigationViewController : NavigationViewController, didArriveAt destination: MGLAnnotation)
@objc optional func navigationViewController(_ navigationViewController : NavigationViewController, didArriveAt destination: Waypoint)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also add navigationViewControllerDidArrive(_:) (with only one argument of type NavigationViewController)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think one method is good enough here.

@@ -332,6 +328,10 @@ public class NavigationViewController: NavigationPulleyViewController, RouteMapV
mapViewController?.notifyAlertLevelDidChange(routeProgress: routeProgress)
tableViewController?.notifyAlertLevelDidChange()

if routeProgress.currentLegProgress.alertUserLevel == .arrive {
navigationDelegate?.navigationViewController?(self, didArriveAt: routeProgress.route.routeOptions.waypoints[routeProgress.legIndex + 1])
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 pass in the current route leg’s destination waypoint. Otherwise, if we pass in one of the waypoints used to request the route, its location won’t necessarily correspond to what the Directions API thinks the destination should be (after snapping or whatever).

for leg in route.legs {
if let last = leg.steps.last {
let annotation = MGLPointAnnotation()
annotation.coordinate = last.coordinates!.last!
Copy link
Contributor

Choose a reason for hiding this comment

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

}

let sectionName = routeController.routeProgress.route.legs[section].name
let legToFrom = sectionName.components(separatedBy: ", ")
Copy link
Contributor

Choose a reason for hiding this comment

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

The suggestion in #270 (comment) was to use source and destination, not name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added using source and destination but I also left this code in place for cases where we do not have source and destination.


if routeController.routeProgress.currentLegProgress.stepIndex + 1 > indexPath.row {
if routeController.routeProgress.legIndex + 1 > indexPath.section && routeController.routeProgress.currentLegProgress.stepIndex + 1 > indexPath.row {
Copy link
Contributor

Choose a reason for hiding this comment

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

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 having a hard time getting something to work here. Have any ideas how to accomplish this?

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to dim a cell for either of two reasons:

  1. Either it represents a step that’s part of a leg that the user has already completed,
  2. Or it represents a completed step in the current leg.

So the correct logic would be:

if indexPath.section < routeController.routeProgress.legIndex
    || (indexPath.section == routeProgress.legIndex
        && indexPath.row <= routeProgress.currentLegProgress.stepIndex) {
    // dim the cell
}

return "\(sourceName) to \(destinationName)"
}

let legToFrom = leg.name.components(separatedBy: ", ")
if legToFrom.count == 2 {
return "\(legToFrom[0]) to \(legToFrom[1])"
Copy link
Contributor

Choose a reason for hiding this comment

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

Since these are two road names, “Main Street and Market Street” would be clearer than “Main Street to Market Street”.

@bsudekum bsudekum mentioned this pull request Jun 23, 2017
1 task
@1ec5
Copy link
Contributor

1ec5 commented Jul 6, 2017

getRouteButton.isHidden = false
clearMapButton.isHidden = false
if waypoints.count > 1 {
waypoints = Array(waypoints.suffix(1))
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 dropFirst().

@IBAction func didTapClearmap(_ sender: Any) {
mapView.removeAnnotations(mapView.annotations ?? [])
@IBAction func replay(_ sender: Any) {
let bundle = Bundle(for: ViewController.self)
Copy link
Contributor

Choose a reason for hiding this comment

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

This class is inside the application target, so you can say simply Bundle.main. It’s only necessary to use Bundle(for:) inside a framework, since the framework isn’t the main bundle.

}

extension ViewController: WaypointConfirmationViewControllerDelegate {
func confirmationControllerDidConfirm(controller confirmationController: WaypointConfirmationViewController) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The first part of this selector, confirmationControllerDidConfirm, already contains “confirmation controller”, so controller can be _.

This code path would be a lot cleaner with an unwind segue instead of the delegate pattern…

Intializes a new `RouteProgress`.

- parameter route: The route to follow.
- parameter legIndex: Zero-based index indicating the current leg the user is on.
Copy link
Contributor

Choose a reason for hiding this comment

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

legIndex is the index of the leg to start on.


let legToFrom = leg.name.components(separatedBy: ", ")
if legToFrom.count == 2 {
return "\(legToFrom[0]) to \(legToFrom[1])"
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure why GitHub thinks #270 (comment) is outdated, but it’s still current.

let sourceName = leg.source.name
let destinationName = leg.destination.name

if let sourceName = sourceName?.nonEmptyString, let destinationName = destinationName?.nonEmptyString {
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 source is the user’s unnamed current location and the destination is a named location, we should say “Destination via Street Name and Street Name”.

If the source is a named location and the destination is unnamed for some reason, we should say “Street Name and Street Name”, same as below.

@ericrwolfe ericrwolfe modified the milestones: v0.7.0-3, v0.7.0-2 Aug 16, 2017
@bsudekum
Copy link
Contributor Author

More reviews are welcome if anyone is up for it @1ec5 @frederoni.

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.

Thanks for your patience. Some more feedback below.

@@ -24,7 +24,7 @@ open class NavigationMapView: MGLMapView {
let arrowLayerStrokeIdentifier = "arrowStrokeLayer"
let arrowCasingSymbolLayerIdentifier = "arrowCasingSymbolLayer"
let arrowSymbolSourceIdentifier = "arrowSymbolSource"
let isOpaqueIdentifier = "isOpaqueIdentifier"
let isCurrentLeg = "isCurrentLeg"
Copy link
Contributor

Choose a reason for hiding this comment

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

The name isCurrentLeg implies that it’s a Boolean. Since this string is being used as an attribute name, call it currentLegAttribute but give it the value isCurrentLeg.

@@ -239,7 +239,7 @@ open class NavigationMapView: MGLMapView {

func shape(for waypoints: [Waypoint]) -> MGLShape? {
var features = [MGLPointFeature]()
let letters = (97...122).map({Character(UnicodeScalar($0))}).map { String(describing:$0).uppercased() }
let letters = String.localizedStringWithFormat(NSLocalizedString("ALPHABET", bundle: .mapboxNavigation, value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", comment: "Format string for alphabet;")).components(separatedBy: "")
Copy link
Contributor

Choose a reason for hiding this comment

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

Use an actual separator like a space, because some locales may have multi-character letters in the alphabet, like “IJ” in Dutch.

@@ -239,7 +239,7 @@ open class NavigationMapView: MGLMapView {

func shape(for waypoints: [Waypoint]) -> MGLShape? {
var features = [MGLPointFeature]()
let letters = (97...122).map({Character(UnicodeScalar($0))}).map { String(describing:$0).uppercased() }
let letters = String.localizedStringWithFormat(NSLocalizedString("ALPHABET", bundle: .mapboxNavigation, value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", comment: "Format string for alphabet;")).components(separatedBy: "")
Copy link
Contributor

Choose a reason for hiding this comment

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

Stray semicolon at the end of the comment. Make sure to point out that each letter should be separated by a space.

for (waypointIndex, waypoint) in waypoints.enumerated() {
let feature = MGLPointFeature()
feature.coordinate = waypoint.coordinate
feature.attributes = [ "name": letters[waypointIndex] ]
Copy link
Contributor

Choose a reason for hiding this comment

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

A reminder to simplify this code by inlining the letters array, as requested in #270 (comment) and #270 (comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems a bit unreadable IMO:

for (waypointIndex, waypoint) in waypoints.enumerated() {
  let feature = MGLPointFeature()
  feature.coordinate = waypoint.coordinate
  feature.attributes = [ "name": String.localizedStringWithFormat(NSLocalizedString("ALPHABET", bundle: .mapboxNavigation, value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", comment: "Format string for alphabet;")).characters.map { String(describing: $0) }[waypointIndex] ]
  features.append(feature)
}

Copy link
Contributor

@1ec5 1ec5 Aug 24, 2017

Choose a reason for hiding this comment

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

Ah, the code I suggested in #270 (comment) was much more readable, but it no longer makes sense with the localization that we’re doing. How about this:

let letters = NSLocalizedString("ALPHABET", bundle: .mapboxNavigation, value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", comment: "Base letters in the alphabet for labeling waypoints, separated by spaces").components(separatedBy: " ")

// then in the for loop
feature.attributes = ["name": letters[waypointIndex]]

Note that there doesn’t need to be a format string, just an plain localized string.

for (waypointIndex, waypoint) in waypoints.enumerated() {
let feature = MGLPointFeature()
feature.coordinate = waypoint.coordinate
feature.attributes = [ "name": letters[waypointIndex] ]
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 waypoint has a name, how about using the first letter of the name by default? That would be in keeping with how iOS represents contacts who lack photos by their initials.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What if one waypoint has a name and the other does not? So it'd look like B (for picking up bobby) and then a waypoint with no name B again? Some sort of order makes more sense to me here.

Copy link
Contributor

Choose a reason for hiding this comment

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

If any of the waypoints has a name, I think it would be better to label the named ones with initials and leave the unnamed ones… unlabeled.

let legToFrom = leg.name.components(separatedBy: ", ")

if let destinationName = destinationName?.nonEmptyString, legToFrom.count > 1 {
return String.localizedStringWithFormat(NSLocalizedString("WAYPOINT_DESTINATION_VIA_WAYPOINTS_FORMAT", bundle: .mapboxNavigation, value: "%@, via %@", comment: "Format for displaying destination and intermediate waypoints; 1 = source ; 2 = destinations"), destinationName, leg.name.replacingOccurrences(of: ", ", with: " \(String.localizedStringWithFormat(NSLocalizedString("AND", bundle: .mapboxNavigation, value: "and", comment: "Format string for and;"))) "))
Copy link
Contributor

Choose a reason for hiding this comment

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

Stray semicolon at the end of the comment.

let legToFrom = leg.name.components(separatedBy: ", ")

if let destinationName = destinationName?.nonEmptyString, legToFrom.count > 1 {
return String.localizedStringWithFormat(NSLocalizedString("WAYPOINT_DESTINATION_VIA_WAYPOINTS_FORMAT", bundle: .mapboxNavigation, value: "%@, via %@", comment: "Format for displaying destination and intermediate waypoints; 1 = source ; 2 = destinations"), destinationName, leg.name.replacingOccurrences(of: ", ", with: " \(String.localizedStringWithFormat(NSLocalizedString("AND", bundle: .mapboxNavigation, value: "and", comment: "Format string for and;"))) "))
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that Chinese and some other languages don’t delimit words with spaces, I think we should use a format string instead of interpolating the word “and” into a hard-coded string:

let majorWays = leg.name.components(separatedBy: ", ")
let summary = String.localizedStringWithFormat(NSLocalizedString("LEG_MAJOR_WAYS_FORMAT", bundle: .mapboxNavigation, value: "%@ and %@", comment: ), majorWays[0], majorWays[1])

The downside is that this code would only be able to handle two major ways, instead of saying something like “A and B and C and D”, but I think the Directions API only gives us two ways anyways.

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.

Almost there! Just one request to rename a constant and a few documentation suggestions.

/*
Returns an` MGLStyleLayer` that determines the appearance of the circles used to symbolize the remaining waypoints.

If this method is unimplemented, the navigation map view draws the route remaining waypoints using an `MGLSymbolStyleLayer` whose text is equal to the layer returned from `navigationMapView(_:shapeFor:)` and will be inserted in the style below the symbol layer in navigationMapView(:waypointSymbolStyleLayerWithIdentifier:).
Copy link
Contributor

Choose a reason for hiding this comment

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

If I’m not mistaken, this method is called only once, the result doesn’t have to be a circle, and the result is applied to all the waypoints (except the origin), right? In that case, how about:

Returns an MGLStyleLayer that marks the location of each destination along the route when there are multiple destinations. The returned layer is added to the map below the layer returned by navigationMapView(_:waypointSymbolStyleLayerWithIdentifier:source:).

If this method is unimplemented, the navigation map view marks each destination waypoint with a circle.

@@ -95,6 +98,28 @@ public protocol NavigationViewControllerDelegate {
*/
@objc optional func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeDescribing route: Route) -> MGLShape?

/*
Returns an` MGLStyleLayer` that determines the appearance of the circles used to symbolize the remaining waypoints.
Copy link
Contributor

Choose a reason for hiding this comment

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

There’s a syntax error here caused by a space being placed after a backtick instead of before.

Returns an` MGLStyleLayer` that determines the appearance of the text used to symbolize the remaining waypoints.

If this method is unimplemented, the navigation map view draws the route remaining waypoints using an `MGLSymbolStyleLayer` whose text is equal to the layer returned from `navigationMapView(_:shapeFor:)`.
This text will start at the number `1` and continue on label each consecutive waypoint.
Copy link
Contributor

Choose a reason for hiding this comment

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

Returns an MGLStyleLayer that places an identifying symbol on each destination along the route when there are multiple destinations. The returned layer is added to the map above the layer returned by navigationMapView(_:waypointStyleLayerWithIdentifier:source:).

If this method is unimplemented, the navigation map view labels each destination waypoint with a number, starting with 1 at the first destination, 2 at the second destination, and so on.

@objc optional func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer?

/**
Returns an `MGLShape` that represents the routes waypoints
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: “route’s”, and a missing period.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since the first (origin) waypoint is excluded:

Returns an MGLShape that represents the destination waypoints along the route (that is, excluding the origin).

/**
Returns an `MGLShape` that represents the routes waypoints

If this method is unimplemented, the navigation map view represnts the route waypoints using `navigationMapView(_:shapeFor:)`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: “represents”.

let arrowLayerStrokeIdentifier = "arrowStrokeLayer"
let arrowCasingSymbolLayerIdentifier = "arrowCasingSymbolLayer"
let arrowSymbolSourceIdentifier = "arrowSymbolSource"
let isCurrentLeg = "isCurrentLeg"
Copy link
Contributor

Choose a reason for hiding this comment

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

#270 (comment) got collapsed for some reason: the name isCurrentLeg implies that it’s a Boolean. Since this string is being used as an attribute name, call it currentLegAttribute but give it the value isCurrentLeg.

@bsudekum bsudekum merged commit 5d0e4fd into master Aug 24, 2017
@bsudekum bsudekum deleted the waypoints branch August 24, 2017 18:40
@hervouinc
Copy link

🍾🥂

@juanwall
Copy link

When will multiple waypoints be available?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature request. platform parity Required to keep on par with Android.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants