-
Notifications
You must be signed in to change notification settings - Fork 90
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
Intersections and lanes #80
Conversation
@1ec5 Added lanes, want to take a look? |
@@ -0,0 +1,56 @@ | |||
import Foundation | |||
|
|||
public class Intersection: NSObject, NSSecureCoding { |
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.
Use @objc
to pretend the MB class prefix for Objective-C usage.
import Foundation | ||
|
||
public class Intersection: NSObject, NSSecureCoding { | ||
public var inIndex: Int? |
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.
This is an awkward place for a preposition. How about entranceIndex
and exitIndex
?
Also, does [Bool].Index
work as the type for this property?
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.
Actually, to avoid confusion with the existing exitIndex
property on RouteStep, how about approachIndex
and outletIndex
, or ingressIndex
and egressIndex
?
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.
Does this property need to be mutable? If not, use let
.
public class Intersection: NSObject, NSSecureCoding { | ||
public var inIndex: Int? | ||
public var outIndex: Int? | ||
public var entry: [Bool] |
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.
Instead of an array of Booleans, this property should be an IndexSet called outlets
that contains the indices of the roads through which the user can exit the intersection.
import Foundation | ||
|
||
public enum LaneIndicationType: Int, CustomStringConvertible { | ||
case Left |
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.
In Swift 3, enum values start with lowercase letters.
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 forgot this is Swift 2.3. Just keep in mind that we'll need to lowercase the first letters when porting to Swift 3.
|
||
case SlightRight | ||
|
||
case Straight |
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.
straightAhead for consistency with ManeuverDirection.
} | ||
} | ||
|
||
public class Lane: NSObject { |
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.
@objc(MBLane)
} | ||
|
||
public class Lane: NSObject { | ||
public var validTurn: Bool |
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.
Whether a lane can be used for a particular maneuver is not an intrinsic property of a lane. Remove this property and add to Intersection a usableLanes
property of type IndexSet or Set<Lane>
, your pick.
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 unclear why this property would live on intersection. It is a specific value for a specific lane in the lanes array. ref docs
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 know that’s how it’s exposed in the API. But the API doesn’t use classes at all, so that’s comparing apples and oranges.
When you create a class, you make it possible for the developer to have an instance of that class all by itself. If the developer has a Lane object but not the Intersection or RouteStep object that contains it, validTurn
is meaningless or misleading. It’s saying that a physical lane on a road can always or never be used for all routes that may contain the lane, which is unrealistic.
Semantically, the information you’re trying to store must live in a parallel data structure. Specifically, Intersection should have a usableLanes
property that contains lanes or lane indices, and this validTurn
property should be removed.
@@ -0,0 +1,83 @@ | |||
import Foundation | |||
|
|||
public enum LaneIndicationType: Int, CustomStringConvertible { |
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.
Just LaneIndication is fine. But remember to use @objc
here too.
|
||
public class Lane: NSObject { | ||
public var validTurn: Bool | ||
public var indications: [LaneIndicationType] |
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.
An array of enums won't bridge to Objective-C. We'll need a helper property that converts it to an array of NSValues containing these enums.
@@ -513,6 +513,11 @@ public class RouteStep: NSObject, NSSecureCoding { | |||
*/ | |||
public let destinations: String? | |||
|
|||
/** | |||
Array of interersections |
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.
Typo. Also, explain that these are all the intersections along the step, starting with the one at the maneuver and ending with the one just before the next step's maneuver.
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.
And mention the relationship between the size of this array and the next step's exitIndex
property.
/** | ||
Index into bearings/entry array. | ||
|
||
Used to calculate the bearing just before the turn. Namely, the clockwise angle from true north to the direction of travel immediately before the maneuver/passing the intersection. Bearings are given relative to the intersection. To get the bearing in the direction of driving, the bearing has to be rotated by a value of 180. The value is not supplied for depart maneuvers. |
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.
Is it always 180°?
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 believe so
|
||
|
||
/** | ||
Objectice-c helper function for indication |
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.
indications
won’t even appear to an Objective-C developer. You’ll have to document this property as if indications
doesn’t even exist.
@@ -514,7 +514,9 @@ public class RouteStep: NSObject, NSSecureCoding { | |||
public let destinations: String? | |||
|
|||
/** | |||
Array of interersections | |||
An intersection gives a full representation of any cross-way the path passes bay. |
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.
You mean “of any road crossing the route”?
@1ec5 updated with |
|
||
Used to calculate the bearing just before the turn. Namely, the clockwise angle from true north to the direction of travel immediately before the maneuver/passing the intersection. Bearings are given relative to the intersection. To get the bearing in the direction of driving, the bearing has to be rotated by a value of 180. The value is not supplied for depart maneuvers. | ||
*/ | ||
public let approachIndex: Int? |
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.
Unfortunately, Int?
won’t bridge to Objective-C, so this will have to be Int
instead. Set it to -1
by default (for the v4 case). Unfortunate, but this is one of the tradeoffs of supporting Objective-C.
|
||
A value of true indicates that the respective road could be entered on a valid route. false indicates that the turn onto the respective road would violate a restriction. | ||
*/ | ||
public var entry: [Bool] |
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.
Does this property need to be mutable?
|
||
A value of true indicates that the respective road could be entered on a valid route. false indicates that the turn onto the respective road would violate a restriction. | ||
*/ | ||
public var entry: [Bool] |
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.
Instead of an array of Booleans, this property should be an IndexSet called outlets
that contains the indices of the roads through which the user can exit the intersection. These would be indices into the headings
array. The index set would be equal in size to or smaller than the headings
array.
public var lanes: [Lane]? | ||
|
||
/** | ||
Set of Lane objects that have a valid turn. |
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 mention that this is a subset of the Lane objects in the lanes
property.
public var headings: [CLLocationDirection] | ||
|
||
/** | ||
Array of Lane objects that denote the available turn lanes at the intersection. |
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.
This isn’t just for turn lanes.
@@ -0,0 +1,121 @@ | |||
import Foundation | |||
|
|||
@objc(MBLaneIndication) |
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.
This type needs a documentation comment.
public enum LaneIndication: Int, CustomStringConvertible { | ||
|
||
/** | ||
An indication indicating a turn to the left. |
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.
Indicates a turn to the left.
@objc(MBLane) | ||
public class Lane: NSObject { | ||
|
||
public let indications: [LaneIndication] |
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 occurs to me that we need to treat this as an option set rather than an array of enum values. After all, a lane can’t be [.Uturn, .Uturn]
. Option sets are really nice in Objective-C and really nice in Swift, but unfortunately Swift option sets don’t bridge to Objective-C. So you’ll have to implement LaneIndication in Objective-C. Use MapboxGeocoder.swift’s MBPlacemarkScope
option set and PlacemarkScope
extension as a model.
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.
@1ec5 are you saying use a set because the type cannot be repeated?
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.
Not a set, but an option set (NS_OPTIONS
in Objective-C, also known as a bitmask or bitfield). Not only because a type can’t be repeated, but also because using an indicationValues
property (of type [NSValue]
) in Objective-C would look like this:
BOOL isStraightAhead;
for (NSValue *indicationValue in lane.indicationValues) {
MBLaneIndication laneIndication;
[indicationValues.firstObject getValue:&laneIndication objCType:@encode(MBLaneIndication)];
if (laneIndication == MBLaneIndicationStraightAhead) {
isStraightAhead = YES;
break;
}
}
if (isStraightAhead) …
and there’s no clean way to get all the indications.
If we implement MBLaneIndication
using NS_OPTIONS
in Objective-C, then the Objective-C code becomes a lot simpler:
if (lane.indications & MBLaneIndicationStraightAhead) …
and the Swift code stays the same:
if lane.indications.contains(.StraightAhead) …
@@ -513,6 +513,13 @@ public class RouteStep: NSObject, NSSecureCoding { | |||
*/ | |||
public let destinations: String? | |||
|
|||
/** | |||
An intersection gives a full representation of any cross-way the path passes bay. |
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.
An array of intersections along the step.
Each item in the array corresponds to a cross street, starting with the intersection at the maneuver location indicated by the
coordinates
property and continuing with each cross street along the step.
@@ -86,6 +86,18 @@ class V5Tests: XCTestCase { | |||
XCTAssertEqual(round(coordinate.longitude), -122) | |||
|
|||
XCTAssertEqual(leg.steps[18].name, "Sycamore Valley Road West") | |||
|
|||
let intersection = step.intersections![0] |
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.
You can also say step.intersections.first!
. I find that easier to type because of code 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.
Along with some feedback from previous reviews that hasn’t been addressed yet.
*/ | ||
public let approachIndex: Int? | ||
public var approachIndex: Int = -1 |
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.
We probably don’t need to allow clients to modify these values. So you can either change this back to public let
and set it to -1
in the initializer (as you seem to do anyways), or you can say public private(set) var
.
|
||
If no lane information is available for an intersection, the lanes property will not be present. | ||
If no lane information is available for an intersection, the lanes property will not be present. The first lane represents the left most lane, while the last represents the right most lane. |
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.
👍
Is this ordering the same for countries that drive on the left?
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.
They should line up with what's printed on the road if you were standing on it looking at the arrows
let locationArray = json["location"] as! [Double] | ||
let location = CLLocationCoordinate2D(latitude: locationArray[0], longitude: locationArray[1]) | ||
let coords = json["location"] as! [Double] | ||
let location = CLLocationCoordinate2D.init(geoJSON: coords) |
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.
Nit: drop .init
.
approachIndex = decoder.decodeObjectForKey("approachIndex") as? Int | ||
outletIndex = decoder.decodeObjectForKey("outletIndex") as? Int | ||
approachIndex = decoder.decodeObjectForKey("approachIndex") as! Int | ||
outletIndex = decoder.decodeObjectForKey("outletIndex") as! Int |
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.
@@ -11,14 +11,14 @@ public class Intersection: NSObject, NSSecureCoding { | |||
|
|||
The index of the item in the headings array that corresponds to the road that the containing route step uses to approach the intersection. |
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.
Mention the default value here, since Objective-C clients won’t be able to see the initializer’s implementation.
@@ -45,13 +45,13 @@ public class Intersection: NSObject, NSSecureCoding { | |||
public var lanes: [Lane]? | |||
|
|||
/** | |||
Set of Lane objects that have a valid turn. | |||
Set of Lane objects that have a valid turn. This is a subset of the lanes object. |
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.
Nit: put lanes
in backticks. When we generate jazzy documentation, jazzy will be able to autolink it to the lanes
documentation.
@@ -45,13 +45,13 @@ public class Intersection: NSObject, NSSecureCoding { | |||
public var lanes: [Lane]? | |||
|
|||
/** | |||
Set of Lane objects that have a valid turn. | |||
Set of Lane objects that have a valid turn. This is a subset of the lanes object. |
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.
What makes a turn “valid”?
A subset of the objects in the
lanes
property representing the lanes that can be used to travel from the road indicated byapproachIndex
to the road indicated byoutletIndex
.
// Indicates no turn. | ||
StraightAhead = (1 << 7), | ||
// Indicates no turn | ||
Uturn = (1 << 8), |
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.
Rename to UTurn
to match ManeuverDirection.UTurn
.
|
||
typedef NS_OPTIONS(NSUInteger, MBLaneIndication) { | ||
// Indicates a turn to the left. | ||
Left = (1 << 1), |
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.
Use the same order as ManeuverDirection: SharpRight, Right, SlightRight, StraightAhead, SlightLeft, Left, SharpLeft, UTurn.
type = .None | ||
default: | ||
return nil | ||
public init?(descriptions: [String]) { |
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.
This public initializer needs a documentation comment.
case .None: | ||
return "None" | ||
var descriptions: [String] = [] | ||
if contains(LaneIndication.Left) { |
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.
Nit: you should be able to remove LaneIndication
for brevity.
SlightRight = (1 << 6), | ||
// Indicates no turn. | ||
StraightAhead = (1 << 7), | ||
// Indicates no turn |
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.
Indicates a U-turn.
StraightAhead = (1 << 7), | ||
// Indicates no turn | ||
Uturn = (1 << 8), | ||
// Indicates a uturn. |
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.
No explicit turn indication.
if contains(LaneIndication.Uturn) { | ||
descriptions.append("uturn") | ||
} | ||
if contains(LaneIndication.None) { |
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.
Is there a difference between []
and ["none"]
in the API response? Is it possible for the API to output either value depending on whether a way is tagged ||right
or none|none|right
in OSM? (The two are semantically equivalent.) If ["none"]
is output regardless, then let’s ignore the "none"
string and use an empty LaneIndication to represent that case. We can remove the None
value from the LaneIndication enumeration.
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 are the only values we return:
https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#properties-5
||right
and none|none|right
are the same for OSRM and will be converted to the fixed set of return types in the spec above.
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.
The other question was whether none;right
for a particular lane would turn into ["none", "right"]
or ["straight", "right"]
or just ["right"]
. This is a tagging practice that led to some discussion in mapbox/mapping#193 and on the talk-us and tagging mailing lists.
I’m inclined to remove the None
value and use []
to represent ["none"]
. If ["none", right"]
ever comes out of the API, we can ticket out turning that into [.StraightAhead, .Right]
.
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.
They should come out as none (cc @MoKob and flagging Project-OSRM/osrm-backend#2645).
Have a look at this and similar scenarios:
|
||
A value of true indicates that the respective road could be entered on a valid route. false indicates that the turn onto the respective road would violate a restriction. | ||
*/ | ||
public let entry: [Bool] |
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.
Still waiting for this method to be renamed to outlets
and changed to be an IndexSet.
|
||
typedef NS_OPTIONS(NSUInteger, MBLaneIndication) { | ||
// Indicates a sharp turn to the right. | ||
SharpRight = (1 << 4), |
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.
Prefix each of these values with MBLaneIndication
. In Swift, MBLaneIndication
will be removed automatically.
descriptions.append("slight left") | ||
} | ||
if contains(LaneIndication.Left) { | ||
descriptions.append("Left") |
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.
s/Left/left/
Added documentation for all new public symbols. Renamed and retyped Intersection properties. Corrected coding methods. Split out LaneIndication extension into a separate Swift file. Removed the None value for LaneIndication. Updated tests.
I’ve addressed all the feedback above and will merge this PR once most of the builds pass. Then I’ll update the swift3 branch. I’m going to let the watchOS build (the only one that actually tries to archive the framework) go red on master while we await #71. |
Note that this PR does not add support for “use lane” instructions. That’s tracked in #79. |
🎉 |
Merged to the swift3 branch in 67b8a95. |
This starts work for adding a lanes class to each step. Currently, this adds an
Intersections
class where the lanes class will live.todo:
/cc @1ec5