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

Route Alerts Refinement #506

Merged
merged 15 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions MapboxDirections.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
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 */; };
2BBBD08D257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.swift */; };
2BBBD08E257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.swift */; };
2BBBD08F257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.swift */; };
2BBBD090257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.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 @@ -455,6 +459,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; };
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>"; };
35828C9D217A003F00ED546E /* OfflineDirections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineDirections.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -816,6 +821,7 @@
F4F508372524D6F10044F2D0 /* RestStop.swift */,
F4F5084A2524DC280044F2D0 /* AdministrativeRegion.swift */,
F457FA79252B9E29007DAEB1 /* Incident.swift */,
2BBBD08C257FA1CD004EB3D6 /* BlockedLanes.swift */,
);
name = MapboxDirections;
path = Sources/MapboxDirections;
Expand Down Expand Up @@ -1452,6 +1458,7 @@
8D381B6B1FDB3D8A008D5A58 /* String.swift in Sources */,
F457FA7B252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084C2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD08E257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC3233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C582523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F508392524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1538,6 +1545,7 @@
8D381B6C1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7C252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084D2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD08F257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC4233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C592523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F5083A2524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1624,6 +1632,7 @@
8D381B6D1FDB3D8B008D5A58 /* String.swift in Sources */,
F457FA7D252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084E2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD090257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
438BFEC5233D854D00457294 /* DirectionsProfileIdentifier.swift in Sources */,
F4CF2C5A2523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F5083B2524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down Expand Up @@ -1677,6 +1686,7 @@
C58EA7AA1E9D7EAD008F98CE /* Congestion.swift in Sources */,
F457FA7A252B9E29007DAEB1 /* Incident.swift in Sources */,
F4F5084B2524DC280044F2D0 /* AdministrativeRegion.swift in Sources */,
2BBBD08D257FA1CD004EB3D6 /* BlockedLanes.swift in Sources */,
8D381B6A1FDB101F008D5A58 /* String.swift in Sources */,
F4CF2C572523B66300A6D0B6 /* TollCollection.swift in Sources */,
F4F508382524D6F10044F2D0 /* RestStop.swift in Sources */,
Expand Down
116 changes: 116 additions & 0 deletions Sources/MapboxDirections/BlockedLanes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

import Foundation

/// Defines a lane affected by the `Incident`
public struct BlockedLanes: OptionSet, CustomStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh cool, thanks for taking care of turning this type into an option set.

public var rawValue: Int
var stringKey: String?

public init(rawValue: Int) {
self.init(rawValue: rawValue, key: nil)
}

init(rawValue: Int, key: String?) {
self.rawValue = rawValue
self.stringKey = key
}

/// Left lane
public static let left = BlockedLanes(rawValue: 1 << 0, key: "LEFT")
/// Left center lane
///
/// Usually refers to the second lane from left on a four-lane highway
public static let leftCenter = BlockedLanes(rawValue: 1 << 1, key: "LEFT CENTER")
/// Left turn lane
public static let leftTurnLane = BlockedLanes(rawValue: 1 << 2, key: "LEFT TURN LANE")
/// Center lane
public static let center = BlockedLanes(rawValue: 1 << 3, key: "CENTER")
/// Right lane
public static let right = BlockedLanes(rawValue: 1 << 4, key: "RIGHT")
/// Right center lane
///
/// Usually refers to the second lane from right on a four-lane highway
public static let rightCenter = BlockedLanes(rawValue: 1 << 5, key: "RIGHT CENTER")
/// Right turn lane
public static let rightTurnLane = BlockedLanes(rawValue: 1 << 6, key: "RIGHT TURN LANE")
/// High occupancy vehicle lane
public static let highOccupancyVehicle = BlockedLanes(rawValue: 1 << 7, key: "HOV")
/// Side lane
public static let side = BlockedLanes(rawValue: 1 << 8, key: "SIDE")
/// Shoulder lane
public static let shoulder = BlockedLanes(rawValue: 1 << 9, key: "SHOULDER")
/// Median lane
public static let median = BlockedLanes(rawValue: 1 << 10, key: "MEDIAN")
/// 1st Lane.
public static let lane1 = BlockedLanes(rawValue: 1 << 11, key: "1")
/// 2nd Lane.
public static let lane2 = BlockedLanes(rawValue: 1 << 12, key: "2")
/// 3rd Lane.
public static let lane3 = BlockedLanes(rawValue: 1 << 13, key: "3")
/// 4th Lane.
public static let lane4 = BlockedLanes(rawValue: 1 << 14, key: "4")
/// 5th Lane.
public static let lane5 = BlockedLanes(rawValue: 1 << 15, key: "5")
/// 6th Lane.
public static let lane6 = BlockedLanes(rawValue: 1 << 16, key: "6")
/// 7th Lane.
public static let lane7 = BlockedLanes(rawValue: 1 << 17, key: "7")
/// 8th Lane.
public static let lane8 = BlockedLanes(rawValue: 1 << 18, key: "8")
/// 9th Lane.
public static let lane9 = BlockedLanes(rawValue: 1 << 19, key: "9")
/// 10th Lane.
public static let lane10 = BlockedLanes(rawValue: 1 << 20, key: "10")

static var allLanes: [BlockedLanes] {
return [.left, .leftCenter, .leftTurnLane, .center, .right, .rightCenter, .rightTurnLane, .highOccupancyVehicle, .side, .shoulder, .median, .lane1, .lane2, .lane3, .lane4, .lane5, .lane6, .lane7, .lane8, .lane9, .lane10]
}

/**
Creates a `BlockedLanes` given an array of strings.

Resulting options set will only contain known values. If string description does not match any known `Blocked Lane` identifier - it will be ignored.
*/
public init?(descriptions: [String]) {
var blockedLanes: BlockedLanes = []
Self.allLanes.forEach {
if descriptions.contains($0.stringKey!) {
blockedLanes.insert($0)
}
}
self.init(rawValue: blockedLanes.rawValue)
}

/**
String representation of `BlockedLanes` options set.

Resulting description contains only texts for known options. Custom options will be ignored if any.
*/
public var description: String {
var descriptions: [String] = []
Self.allLanes.forEach {
if contains($0) {
descriptions.append($0.stringKey!)
}
}
return descriptions.joined(separator: ",")
}
}

extension BlockedLanes: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(description.components(separatedBy: ",").filter { !$0.isEmpty })
}

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let descriptions = try container.decode([String].self)
if let roadClasses = BlockedLanes(descriptions: descriptions){
self = roadClasses
}
else{
throw DirectionsError.invalidResponse(nil)
}
}
}
113 changes: 21 additions & 92 deletions Sources/MapboxDirections/Incident.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,94 +25,28 @@ public struct Incident: Codable, Equatable {
///
/// Each incident may or may not have specific set of data, depending on it's `kind`
public enum Kind: String {
case Accident = "accident"
case Congestion = "congestion"
case Construction = "construction"
case DisabledVehicle = "disabled_vehicle"
case LaneRestriction = "lane_restriction"
case MassTransit = "mass_transit"
case Miscellaneous = "miscellaneous"
case OtherNews = "other_news"
case PlannedEvent = "planned_event"
case RoadClosure = "road_closure"
case RoadHazard = "road_hazard"
case Weather = "weather"
}

/// Defines a lane affected by the `Incident`
///
/// Each `Incident` may have arbitrary of affected lanes
public enum BlockedLane: String, Codable {
/// Left lane
case left = "LEFT"
/// Left and center lanes
case leftCenter = "LEFT CENTER"
/// Left turn lane
case leftTurnLane = "LEFT TURN LANE"
/// Center lane
case center = "CENTER"
/// Right lane
case right = "RIGHT"
/// Right and center lanes
case rightCenter = "RIGHT CENTER"
/// Right turn lane
case rightTurnLane = "RIGHT TURN LANE"
/// High occupancy vehicle lane
case highOccupancyVehicle = "HOV"
/// Side lane
case side = "SIDE"
/// Shoulder lane
case shoulder = "SHOULDER"
/// Median lane
case median = "MEDIAN"
/// 1st Lane.
///
/// Mind the driving side.
case lane1 = "1"
/// 2nd Lane.
///
/// Mind the driving side.
case lane2 = "2"
/// 3rd Lane.
///
/// Mind the driving side.
case lane3 = "3"
/// 4th Lane.
///
/// Mind the driving side.
case lane4 = "4"
/// 5th Lane.
///
/// Mind the driving side.
case lane5 = "5"
/// 6th Lane.
///
/// Mind the driving side.
case lane6 = "6"
/// 7th Lane.
///
/// Mind the driving side.
case lane7 = "7"
/// 8th Lane.
///
/// Mind the driving side.
case lane8 = "8"
/// 9th Lane.
///
/// Mind the driving side.
case lane9 = "9"
/// 10th Lane.
///
/// Mind the driving side.
case lane10 = "10"
case accident = "accident"
case congestion = "congestion"
case construction = "construction"
case disabledVehicle = "disabled_vehicle"
case laneRestriction = "lane_restriction"
case massTransit = "mass_transit"
case miscellaneous = "miscellaneous"
case otherNews = "other_news"
case plannedEvent = "planned_event"
case roadClosure = "road_closure"
case roadHazard = "road_hazard"
case weather = "weather"
1ec5 marked this conversation as resolved.
Show resolved Hide resolved
}

/// Incident identifier
public var identifier: String
/// The kind of an incident
///
/// This value is set to `nil` if `kind` value is not supported.
public var kind: Kind?
public var kind: Kind? {
return Kind(rawValue: rawKind)
}
var rawKind: String
/// Short description of an incident. May be used as an additional info.
public var description: String
Expand All @@ -132,11 +66,10 @@ public struct Incident: Codable, Equatable {
///
/// See https://www.iso.org/standard/59231.html for details
public var alertCodes: Set<Int>
/// A list of lanes indices, affected by the incident
/// A list of lanes, affected by the incident
///
/// `nil` value indicates that such lane identifier is not supported
public var lanesBlocked: Set<BlockedLane?>
var rawLanesBlocked: Set<String>
/// `nil` value indicates that lanes data is not available
public var lanesBlocked: BlockedLanes?
/// The range of segments within the overall leg, where the incident spans.
public var shapeIndexRange: Range<Int>

Expand All @@ -150,10 +83,9 @@ public struct Incident: Codable, Equatable {
subtype: String?,
subtypeDescription: String?,
alertCodes: Set<Int>,
lanesBlocked: Set<BlockedLane>,
lanesBlocked: BlockedLanes?,
shapeIndexRange: Range<Int>) {
self.identifier = identifier
self.kind = type
self.rawKind = type.rawValue
self.description = description
self.creationDate = creationDate
Expand All @@ -164,7 +96,6 @@ public struct Incident: Codable, Equatable {
self.subtypeDescription = subtypeDescription
self.alertCodes = alertCodes
self.lanesBlocked = lanesBlocked
self.rawLanesBlocked = Set(lanesBlocked.map { $0.rawValue })
self.shapeIndexRange = shapeIndexRange
}

Expand All @@ -174,7 +105,6 @@ public struct Incident: Codable, Equatable {

identifier = try container.decode(String.self, forKey: .identifier)
rawKind = try container.decode(String.self, forKey: .type)
kind = Kind(rawValue: rawKind)

description = try container.decode(String.self, forKey: .description)

Expand Down Expand Up @@ -205,8 +135,7 @@ public struct Incident: Codable, Equatable {
subtypeDescription = try container.decodeIfPresent(String.self, forKey: .subtypeDescription)
alertCodes = try container.decode(Set<Int>.self, forKey: .alertCodes)

rawLanesBlocked = try container.decode(Set<String>.self, forKey: .lanesBlocked)
lanesBlocked = rawLanesBlocked.reduce(into: Set<BlockedLane?>()) { $0.insert(BlockedLane(rawValue: $1)) }
lanesBlocked = try container.decodeIfPresent(BlockedLanes.self, forKey: .lanesBlocked)

let geometryIndexStart = try container.decode(Int.self, forKey: .geometryIndexStart)
let geometryIndexEnd = try container.decode(Int.self, forKey: .geometryIndexEnd)
Expand All @@ -227,7 +156,7 @@ public struct Incident: Codable, Equatable {
try container.encodeIfPresent(subtype, forKey: .subtype)
try container.encodeIfPresent(subtypeDescription, forKey: .subtypeDescription)
try container.encode(alertCodes, forKey: .alertCodes)
try container.encode(rawLanesBlocked, forKey: .lanesBlocked)
try container.encodeIfPresent(lanesBlocked, forKey: .lanesBlocked)
try container.encode(shapeIndexRange.lowerBound, forKey: .geometryIndexStart)
try container.encode(shapeIndexRange.upperBound, forKey: .geometryIndexEnd)
}
Expand Down
1 change: 0 additions & 1 deletion Tests/MapboxDirectionsTests/Fixtures/incidents.json
Original file line number Diff line number Diff line change
Expand Up @@ -24369,7 +24369,6 @@
"alertc_codes": [
701
],
"lanes_blocked": ["1"],
"geometry_index_start": 476,
"geometry_index_end": 566
},
Expand Down
7 changes: 4 additions & 3 deletions Tests/MapboxDirectionsTests/RouteStepTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,10 @@ class RouteStepTests: XCTestCase {
XCTAssertNoThrow(newRoute = try decoder.decode(Route.self, from: jsonData))
XCTAssertNotNil(newRoute)

XCTAssert(newRoute!.legs.first!.incidents!.first!.kind == Incident.Kind.Miscellaneous)
XCTAssert(newRoute!.legs.first!.incidents![1].lanesBlocked.first! == Incident.BlockedLane.lane1)
XCTAssertNil(newRoute!.legs.first!.incidents![2].lanesBlocked.first!)
XCTAssert(newRoute!.legs.first!.incidents!.first!.kind == Incident.Kind.miscellaneous)
XCTAssert(newRoute!.legs.first!.incidents![0].lanesBlocked!.contains(.right))
XCTAssertNil(newRoute!.legs.first!.incidents![1].lanesBlocked)
XCTAssert(newRoute!.legs.first!.incidents![2].lanesBlocked!.isEmpty)
XCTAssert(newRoute!.legs.first!.incidents![2].shapeIndexRange == 810..<900)
XCTAssert(newRoute!.legs.first!.incidents!.first! == route.legs.first!.incidents!.first!)
}
Expand Down