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 the Intersection.outletMapboxStreetsRoadClass property that indicates a more detailed road classification than the existing Intersection.outletRoadClasses property. (#507).
* Added the `RouteLeg.incidents` property that indicates known traffic incidents, toll collection points, rest areas, and border crossings along the route leg. ([#466](https://github.com/mapbox/mapbox-directions-swift/pull/466), [#506](https://github.com/mapbox/mapbox-directions-swift/pull/506))
* Added the `RouteLeg.administrativeRegions` property that indicates the administrative regions traversed by the route leg. ([#466](https://github.com/mapbox/mapbox-directions-swift/pull/466), [#506](https://github.com/mapbox/mapbox-directions-swift/pull/506))
* Added the `Intersection.tunnelName`, `Intersection.tollCollection, `Intersection.restStop`, and `Intersection.isUrban` properties. ([#466](https://github.com/mapbox/mapbox-directions-swift/pull/466), [#506](https://github.com/mapbox/mapbox-directions-swift/pull/506))
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 /* MapboxStreetsRoadClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */; };
2BBBD05F257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */; };
2BBBD060257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */; };
2BBBD061257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */; };
2BA2E746257A667500D7AFC6 /* incidents.json in Resources */ = {isa = PBXBuildFile; fileRef = 2BA2E745257A667500D7AFC6 /* incidents.json */; };
2BA2E747257A667500D7AFC6 /* incidents.json in Resources */ = {isa = PBXBuildFile; fileRef = 2BA2E745257A667500D7AFC6 /* incidents.json */; };
2BA2E748257A667500D7AFC6 /* incidents.json in Resources */ = {isa = PBXBuildFile; fileRef = 2BA2E745257A667500D7AFC6 /* incidents.json */; };
Expand Down Expand Up @@ -459,6 +463,7 @@
2B674DCA2541AF410026CE4B /* Turf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Turf.framework; path = Carthage/Build/Mac/Turf.framework; sourceTree = "<group>"; };
2BA2E745257A667500D7AFC6 /* incidents.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = incidents.json; sourceTree = "<group>"; };
2BA98970253F007600B643F6 /* mapbox-directions-swift */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "mapbox-directions-swift"; sourceTree = BUILT_PRODUCTS_DIR; };
2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapboxStreetsRoadClass.swift; sourceTree = "<group>"; };
2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedLanes.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>"; };
Expand Down Expand Up @@ -803,6 +808,7 @@
35828C9D217A003F00ED546E /* OfflineDirections.swift */,
DAD06E3823A008EB001A917D /* QuickLook.swift */,
C59426061F1EA6C400C8E59C /* RoadClasses.swift */,
2BBBD05D257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift */,
DAC05F171CFC075300FA0071 /* Route.swift */,
DAC05F191CFC077C00FA0071 /* RouteLeg.swift */,
2B5407F6245302AB006C820B /* RouteLegAttributes.swift */,
Expand Down Expand Up @@ -1458,6 +1464,7 @@
8D381B6B1FDB3D8A008D5A58 /* String.swift in Sources */,
F457FA7B252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084C2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD05F257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */,
2BBBD08E257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC3233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C582523B66300A6D0B6 /* TollCollection.swift in Sources */,
Expand Down Expand Up @@ -1545,6 +1552,7 @@
8D381B6C1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7C252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084D2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD060257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */,
2BBBD08F257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC4233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C592523B66300A6D0B6 /* TollCollection.swift in Sources */,
Expand Down Expand Up @@ -1632,6 +1640,7 @@
8D381B6D1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7D252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084E2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD061257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */,
2BBBD090257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC5233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C5A2523B66300A6D0B6 /* TollCollection.swift in Sources */,
Expand Down Expand Up @@ -1686,6 +1695,7 @@
C58EA7AA1E9D7EAD008F98CE /* Congestion.swift in Sources */,
F457FA7A252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084B2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD05E257E61ED004EB3D6 /* MapboxStreetsRoadClass.swift in Sources */,
2BBBD08D257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
8D381B6A1FDB101F008D5A58 /* String.swift in Sources */,
F4CF2C572523B66300A6D0B6 /* TollCollection.swift in Sources */,
Expand Down
41 changes: 40 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,
outletMapboxStreetsRoadClass: MapboxStreetsRoadClass? = 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.outletMapboxStreetsRoadClass = outletMapboxStreetsRoadClass
}

// MARK: Getting the Location of the Intersection
Expand Down Expand Up @@ -87,6 +89,13 @@ public struct Intersection {
*/
public let outletRoadClasses: RoadClasses?

/**
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.
*/
public let outletMapboxStreetsRoadClass: MapboxStreetsRoadClass?

/**
The name of the tunnel that this intersection is a part of.

Expand Down Expand Up @@ -156,12 +165,36 @@ 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.outletMapboxStreetsRoadClass`
private struct MapboxStreetClassCodable: Codable {
private enum CodingKeys: String, CodingKey {
case streetClass = "class"
}

let streetClass: MapboxStreetsRoadClass?

init(streetClass: MapboxStreetsRoadClass?) {
self.streetClass = streetClass
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

if let classString = try container.decodeIfPresent(String.self, forKey: .streetClass) {
streetClass = MapboxStreetsRoadClass(rawValue: classString)
} else {
streetClass = nil
Copy link
Contributor

@1ec5 1ec5 Dec 11, 2020

Choose a reason for hiding this comment

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

I think this is fine because adding new road classes is a rare event and the new value wouldn’t be immediately useful in the navigation SDK anyways. However, if we ever learn of a plan to add new road classes in Streets source v8.x, we should proactively implement the other case with an associated value, similar to what’s proposed in #506 (comment). Not necessary just yet though.

}

}
}

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

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

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

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

outletMapboxStreetsRoadClass = 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
52 changes: 52 additions & 0 deletions Sources/MapboxDirections/MapboxStreetsRoadClass.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

import Foundation

/// A road classification according to the [Mapbox Streets source](https://docs.mapbox.com/vector-tiles/reference/mapbox-streets-v8/#road) , version 8.
public enum MapboxStreetsRoadClass: String, Codable {
/// High-speed, grade-separated highways
case motorway = "motorway"
/// 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"
Comment on lines +42 to +51
Copy link
Contributor

@1ec5 1ec5 Dec 10, 2020

Choose a reason for hiding this comment

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

In case you’re wondering how these classes would ever come up: railroads are sometimes used as driveways in rural areas (but would have to be tagged with the appropriate access tags to be included in a route), and theoretically a ski lift could be part of a pedestrian route.

}
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.outletMapboxStreetsRoadClass, MapboxStreetsRoadClass.streetLimited)
}

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