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

Detailed Road Classification #507

Merged
merged 10 commits into from
Dec 11, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* `Intersection.regionCode` - A 2-letter region code to identify corresponding country that this intersection lies in.
* `RouteLeg.regionCode(atStepIndex:, intersectionIndex:)` - Returns the ISO 3166-1 alpha-2 region code for the administrative region through which the given intersection passes.
* Added `RouteStep.segmentIndicesByIntersection` for navigating `Intersection`s segments along the step; ([#490](https://github.com/mapbox/mapbox-directions-swift/pull/490))
* Added Mapbox Streets V8 Road Classification info to `Intersection.roadClass` property. See [Mapbox Streets V8 Road classification doc](https://docs.mapbox.com/vector-tiles/reference/mapbox-streets-v8/#road) for more info on the classes ([#507](https://github.com/mapbox/mapbox-directions-swift/pull/507)).
Udumft marked this conversation as resolved.
Show resolved Hide resolved

## v1.1.0

Expand Down
10 changes: 10 additions & 0 deletions MapboxDirections.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
2B674DC22541AF2E0026CE4B /* Polyline.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F448F83A1DDCC6EB000BC343 /* Polyline.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
2B674DCB2541AF410026CE4B /* Turf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B674DCA2541AF410026CE4B /* Turf.framework */; };
2B674DCC2541AF410026CE4B /* Turf.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2B674DCA2541AF410026CE4B /* Turf.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
2BBBD05E257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */; };
2BBBD05F257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */; };
2BBBD060257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */; };
2BBBD061257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */; };
35828C9E217A003F00ED546E /* OfflineDirections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35828C9D217A003F00ED546E /* OfflineDirections.swift */; };
35828C9F217A003F00ED546E /* OfflineDirections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35828C9D217A003F00ED546E /* OfflineDirections.swift */; };
35828CA0217A003F00ED546E /* OfflineDirections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35828C9D217A003F00ED546E /* OfflineDirections.swift */; };
Expand Down Expand Up @@ -451,6 +455,7 @@
2B540808245B23BE006C820B /* incorrectRouteRefreshResponse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = incorrectRouteRefreshResponse.json; sourceTree = "<group>"; };
2B674DCA2541AF410026CE4B /* Turf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Turf.framework; path = Carthage/Build/Mac/Turf.framework; sourceTree = "<group>"; };
2BA98970253F007600B643F6 /* mapbox-directions-swift */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "mapbox-directions-swift"; sourceTree = BUILT_PRODUCTS_DIR; };
2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapboxStreetClass.swift; sourceTree = "<group>"; };
2BFD4D962540604000D0E14D /* SwiftCLI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCLI.framework; path = Carthage/Build/Mac/SwiftCLI.framework; sourceTree = "<group>"; };
3556CE9922649CF2009397B5 /* MapboxDirectionsTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "MapboxDirectionsTests-Bridging-Header.h"; path = "../objc/MapboxDirectionsTests-Bridging-Header.h"; sourceTree = "<group>"; };
35828C9D217A003F00ED546E /* OfflineDirections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineDirections.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -794,6 +799,7 @@
35828C9D217A003F00ED546E /* OfflineDirections.swift */,
DAD06E3823A008EB001A917D /* QuickLook.swift */,
C59426061F1EA6C400C8E59C /* RoadClasses.swift */,
2BBBD05D257E61ED004EB3D6 /* MapboxStreetClass.swift */,
DAC05F171CFC075300FA0071 /* Route.swift */,
DAC05F191CFC077C00FA0071 /* RouteLeg.swift */,
2B5407F6245302AB006C820B /* RouteLegAttributes.swift */,
Expand Down Expand Up @@ -1444,6 +1450,7 @@
8D381B6B1FDB3D8A008D5A58 /* String.swift in Sources */,
F457FA7B252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084C2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD05F257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */,
438BFEC3233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C582523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F508392524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1530,6 +1537,7 @@
8D381B6C1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7C252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084D2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD060257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */,
438BFEC4233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C592523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F5083A2524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1616,6 +1624,7 @@
8D381B6D1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7D252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084E2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD061257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */,
438BFEC5233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C5A2523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F5083B2524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1669,6 +1678,7 @@
C58EA7AA1E9D7EAD008F98CE /* Congestion.swift in Sources */,
F457FA7A252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084B2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD05E257E61ED004EB3D6 /* MapboxStreetClass.swift in Sources */,
8D381B6A1FDB101F008D5A58 /* String.swift in Sources */,
F4CF2C572523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F508382524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down
25 changes: 24 additions & 1 deletion Sources/MapboxDirections/Intersection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public struct Intersection {
tunnelName: String? = nil,
restStop: RestStop? = nil,
isUrban: Bool? = nil,
regionCode: String? = nil) {
regionCode: String? = nil,
streetClass: MapboxStreetClass? = nil) {
self.location = location
self.headings = headings
self.approachIndex = approachIndex
Expand All @@ -37,6 +38,7 @@ public struct Intersection {
self.isUrban = isUrban
self.restStop = restStop
self.regionCode = regionCode
self.streetClass = streetClass
}

// MARK: Getting the Location of the Intersection
Expand Down Expand Up @@ -111,6 +113,12 @@ public struct Intersection {
*/
public let restStop: RestStop?

/**
Street class according to Mapbox Streets V8 classification.

This value is set to `nil` of such info is not available.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/**
Street class according to Mapbox Streets V8 classification.
This value is set to `nil` of such info is not available.
*/
/**
The road classes of the road that the containing step uses to leave the intersection, according to the [Mapbox Streets source](https://docs.mapbox.com/vector-tiles/reference/mapbox-streets-v8/#road), version 8.
If detailed road class information is unavailable, this property is set to `nil`. This property only indicates the road classification; for other aspects of the road, use the `outletRoadClasses` property.
*/

And in the outletRoadClasses documentation, add a cross-reference like this:

For more detailed road classifications, use the outletMapboxStreetsRoadClass property.

public let streetClass: MapboxStreetClass?
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
/**
:nodoc:
Whether the intersection lays within the bounds of an urban zone.
Expand Down Expand Up @@ -160,12 +168,21 @@ extension Intersection: Codable {
case outletRoadClasses = "classes"
case tollCollection = "toll_collection"
case tunnelName = "tunnelName"
case mapboxStreets = "mapbox_streets_v8"
case isUrban = "is_urban"
case restStop = "rest_stop"
case administrativeRegionIndex = "admin_index"
case geometryIndex = "geometry_index"
}

/// Used to code `Intersection.streetClass`
private struct MapboxStreetClassCodable: Codable {
private enum CodingKeys: String, CodingKey {
case streetClass = "class"
}

let streetClass: MapboxStreetClass?
}
1ec5 marked this conversation as resolved.
Show resolved Hide resolved

static func encode(intersections: [Intersection],
to parentContainer: inout UnkeyedEncodingContainer,
Expand Down Expand Up @@ -234,6 +251,10 @@ extension Intersection: Codable {
try container.encode(tolls, forKey: .tollCollection)
}

if let streetClasses = streetClass {
try container.encode(MapboxStreetClassCodable(streetClass: streetClasses), forKey: .mapboxStreets)
}

if let isUrban = isUrban {
try container.encode(isUrban, forKey: .isUrban)
}
Expand Down Expand Up @@ -279,6 +300,8 @@ extension Intersection: Codable {

tunnelName = try container.decodeIfPresent(String.self, forKey: .tunnelName)

streetClass = try container.decodeIfPresent(MapboxStreetClassCodable.self, forKey: .mapboxStreets)?.streetClass

isUrban = try container.decodeIfPresent(Bool.self, forKey: .isUrban)

restStop = try container.decodeIfPresent(RestStop.self, forKey: .restStop)
Expand Down
65 changes: 65 additions & 0 deletions Sources/MapboxDirections/MapboxStreetClass.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

import Foundation

public enum MapboxStreetClass: String, Codable {
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
Udumft marked this conversation as resolved.
Show resolved Hide resolved
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
/// High-speed, grade-separated highways
case Motorway = "motorway"
Udumft marked this conversation as resolved.
Show resolved Hide resolved
/// Link roads/lanes/ramps connecting to motorways
case MotorwayLink = "motorway_link"
/// Important roads that are not motorways.
case Trunk = "trunk"
/// Link roads/lanes/ramps connecting to trunk roads
case TrunkLink = "trunk_link"
/// A major highway linking large towns.
case Primary = "primary"
/// Link roads/lanes connecting to primary roads
case PrimaryLink = "primary_link"
/// A highway linking large towns.
case Secondary = "secondary"
/// Link roads/lanes connecting to secondary roads
case SecondaryLink = "secondary_link"
/// A road linking small settlements, or the local centres of a large town or city.
case Tertiary = "tertiary"
/// Link roads/lanes connecting to tertiary roads
case TertiaryLink = "tertiary_link"
/// Standard unclassified, residential, road, and living_street road types
case Street = "street"
/// Streets that may have limited or no access for motor vehicles.
case StreetLimited = "street_limited"
/// Includes pedestrian streets, plazas, and public transportation platforms.
case Pedestrian = "pedestrian"
/// Includes motor roads under construction (but not service roads, paths, etc.
case Construction = "construction"
/// Roads mostly for agricultural and forestry use etc.
case Track = "track"
/// Access roads, alleys, agricultural tracks, and other services roads. Also includes parking lot aisles, public & private driveways.
case Service = "service"
/// Those that serves automobiles and no or unspecified automobile service.
case Ferry = "ferry"
/// Foot paths, cycle paths, ski trails.
case Path = "path"
/// Railways, including mainline, commuter rail, and rapid transit.
case MajorRail = "major_rail"
/// Includes light rail & tram lines.
case MinorRail = "minor_rail"
/// Yard and service railways.
case ServiceRail = "service_rail"
/// Ski lifts, gondolas, and other types of aerialway.
case Aerialway = "aerialway"
/// The approximate centerline of a golf course hole
case Golf = "golf"
/// Circular continuous-flow intersection
case Roundabout = "roundabout"
/// Smaller variation of a roundabout with no center island or obstacle
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
case MiniRoundabout = "mini_roundabout"
Copy link
Contributor

Choose a reason for hiding this comment

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

By definition, mini roundabouts are never mapped as ways in OpenStreetMap, so they wouldn’t appear in the routing graph as an edge to assign a Mapbox Streets road class to. It’s worth confirming that this is also true of other data sources, but I’d be hard-pressed to think of a situation where it would make sense to represent a mini roundabout as a linear feature (as opposed to a full-size roundabout).

/// (point Widened section at the end of a cul-de-sac for turning around a vehicle
case TurningCircle = "turning_circle"
/// (point Similar to a turning circle but with an island or other obstruction at the centerpoint
case TurningLoop = "turning_loop"
/// (point Lights or other signal controlling traffic flow at an intersection
case TrafficSignals = "traffic_signals"
/// (point A point indication for road junctions
case Junction = "junction"
/// (point Indicating the class and type of roads meeting at an intersection. Intersections are only available in Japan
case Intersection = "intersection"
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 we should omit these values. As these road classifications are currently used, they would only occur on linear features, not point features. They appear in the road layer of the Mapbox Streets source because that layer also contains point features. For example, some styles use the road layer’s traffic_signals-classed point features to label traffic lights with 🚦.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Directions team suggests to still have these values even though not all of them can really be present... So maybe we should leave it just to be safe?

Copy link
Contributor

@1ec5 1ec5 Dec 8, 2020

Choose a reason for hiding this comment

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

I’m inclined to say YAGNI, since we could always add more cases to this non-frozen enumeration without breaking backwards compatibility, and it’s difficult to imagine a situation where the Directions API would start adding these values to edges without breaking backwards compatibility in other ways.

If we really want to be on the safe side, I guess we could leave it in but keep it undocumented. But even then, the downside is that developers still have to deal with these dead code paths whenever they deal with Streets road classes. Swift will complain about these cases being missing from any switch statement unless the developer adds a default @unknown clause. So I’m inclined to omit these values; if on the off chance they occur, it would be the same as an unexpected new value that the Directions API adds after the fact.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the source code, there are more cases which will never make it to the real world navigation: pedestrian (this one is surprising, why not?) , construction, track, path, "major_rail", minor_rail, service_rail, aerialway, golf.
Maybe we should also not add them for now?

}
7 changes: 6 additions & 1 deletion Tests/MapboxDirectionsTests/IntersectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class IntersectionTests: XCTestCase {
"bearings": [80],
"location": [13.426579, 52.508068],
"classes": ["toll", "restricted"],
"mapbox_streets_v8": [
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
"class": "street_limited"
],
],
[
"out": 1,
Expand Down Expand Up @@ -51,6 +54,7 @@ class IntersectionTests: XCTestCase {
XCTAssertEqual(intersection.outletRoadClasses, [.toll, .restricted])
XCTAssertEqual(intersection.headings, [80.0])
XCTAssertEqual(intersection.location, CLLocationCoordinate2D(latitude: 52.508068, longitude: 13.426579))
XCTAssertEqual(intersection.streetClass, MapboxStreetClass.StreetLimited)
}

intersections = [
Expand All @@ -65,7 +69,8 @@ class IntersectionTests: XCTestCase {
tollCollection: nil,
tunnelName: nil,
restStop: nil,
isUrban: nil),
isUrban: nil,
streetClass: .StreetLimited),
Intersection(location: CLLocationCoordinate2D(latitude: 52.508022, longitude: 13.426688),
headings: [30.0, 120.0, 300.0],
approachIndex: 2,
Expand Down