From 635694cf662e64f6b338e3d959ab3f9e5623fed0 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Thu, 12 Nov 2020 14:41:49 -0800 Subject: [PATCH 01/18] Add initial implementation of congestion consistency. --- MapboxNavigation/NavigationMapView.swift | 91 ++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 7 deletions(-) mode change 100644 => 100755 MapboxNavigation/NavigationMapView.swift diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift old mode 100644 new mode 100755 index 2b2b24997db..c92b7ebe734 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -46,6 +46,11 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { */ public var tapGestureDistanceThreshold: CGFloat = 50 + /** + A collection of road classes for which a color substitution should occur. + */ + public var trafficOverrideRoadClasses: [RoadClasses] = [] + /** The object that acts as the navigation delegate of the map view. */ @@ -1035,6 +1040,50 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { return MGLShapeCollectionFeature(shapes: [mainRoute]) } + /** + Returns an array of `RoadClasses` for specific leg. + + - parameter leg: Leg of the route. + - returns: A list of `RoadClasses` for specific `RouteLeg`. `RoadClasses` will be set to `nil` if it's not present in `Intersection`. + */ + func roadClasses(_ leg: RouteLeg) -> [RoadClasses?] { + // Iterate over `Intersection` segments for specific `RouteLeg` and leave only valid ones. + // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. + var intersectionsIndexesByStep = [Int]() + leg.intersectionsIndexesByStep.compactMap({ $0 }).forEach { + intersectionsIndexesByStep.append(contentsOf: $0.compactMap({ $0 })) + } + + // Iterate over each `Intersection` and save `RoadClasses`. + // Array of `RoadClasses` can look like this: + // [Optional(toll,motorway), ... , Optional(), Optional(toll,motorway), nil] + var roadClassesInLeg = [RoadClasses?]() + leg.steps.forEach { + $0.intersections?.forEach { + roadClassesInLeg.append($0.outletRoadClasses) + } + } + + // Iterate over each `Intersection` segment and fill it in with appropriate `RoadClasses`. + // At the end amount of `RoadClasses` should be equal to the last segment index. + var roadClasses = [RoadClasses?]() + for (index, _) in intersectionsIndexesByStep.enumerated() { + let nextIndex = index + 1 + + if intersectionsIndexesByStep.indices.contains(nextIndex), + roadClassesInLeg.indices.contains(index) { + let currentIndex = intersectionsIndexesByStep[index] + let nextIndex = intersectionsIndexesByStep[nextIndex] + + for _ in currentIndex.. [MGLPolylineFeature]? { guard let coordinates = route.shape?.coordinates else { return nil } @@ -1051,8 +1100,11 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { return index == 0 ? stepCoordinates : allCoordinates + stepCoordinates.suffix(from: 1) } - - let mergedCongestionSegments = combine(legCoordinates, with: legCongestion) + + let mergedCongestionSegments = combine(legCoordinates, + with: legCongestion, + roadClasses: roadClasses(leg), + trafficOverrideRoadClasses: trafficOverrideRoadClasses) lines = mergedCongestionSegments.map { (congestionSegment: CongestionSegment) -> MGLPolylineFeature in let polyline = MGLPolylineFeature(coordinates: congestionSegment.0, count: UInt(congestionSegment.0.count)) @@ -1084,20 +1136,45 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This method coalesces consecutive line segments that have the same congestion level. - - coordinates: The coordinates of a leg. - - congestions: The congestion levels along a leg. There should be one fewer congestion levels than coordinates. + For each item in the` CongestionSegment` collection a `CongestionLevel` substitution will take place that has a road class contained in the `trafficOverrideRoadClasses` collection. + For each of these items the `CongestionLevel` for `.unknown` traffic congestion will be replaced with the `.low` traffic congestion. + + - parameter coordinates: The coordinates of a leg. + - parameter congestions: The congestion levels along a leg. There should be one fewer congestion levels than coordinates. + - parameter roadClasses: A collection of road classes for each geometry index in `Intersection`. There should be the same amount of `roadClasses` and `congestions`. + - parameter trafficOverrideRoadClasses: A collection of road classes for which a `CongestionLevel` substitution should occur. + - returns: A list of `CongestionSegment` tuples with coordinate and congestion level. */ - func combine(_ coordinates: [CLLocationCoordinate2D], with congestions: [CongestionLevel]) -> [CongestionSegment] { + func combine(_ coordinates: [CLLocationCoordinate2D], + with congestions: [CongestionLevel], + roadClasses: [RoadClasses?]? = nil, + trafficOverrideRoadClasses: [RoadClasses]? = nil) -> [CongestionSegment] { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) + + var index = 0 for (firstSegment, congestionLevel) in zip(zip(coordinates, coordinates.suffix(from: 1)), congestions) { let coordinates = [firstSegment.0, firstSegment.1] - if segments.last?.1 == congestionLevel { + + var overriddenCongestionLevel = congestionLevel + if let roadClasses = roadClasses, + let trafficOverrideRoadClasses = trafficOverrideRoadClasses, + roadClasses.indices.contains(index), + let roadClass = roadClasses[index], + trafficOverrideRoadClasses.contains(roadClass), + congestionLevel == .unknown { + overriddenCongestionLevel = .low + } + + if segments.last?.1 == overriddenCongestionLevel { segments[segments.count - 1].0 += coordinates } else { - segments.append((coordinates, congestionLevel)) + segments.append((coordinates, overriddenCongestionLevel)) } + + index += 1 } + return segments } From 8f5b139920bc89b472613a7ce0e8579d1cc86c20 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Fri, 13 Nov 2020 12:11:55 -0800 Subject: [PATCH 02/18] Improve algorithm which calculates road classes for specific route. Remove unused `trafficAlternateLow` property. Remove unused `.gitignore` file. --- MapboxNavigation/DayStyle.swift | 1 - MapboxNavigation/NavigationMapView.swift | 34 +++++++++++++------ MapboxNavigation/RouteMapViewController.swift | 1 - scripts/.gitignore | 1 - 4 files changed, 23 insertions(+), 14 deletions(-) delete mode 100644 scripts/.gitignore diff --git a/MapboxNavigation/DayStyle.swift b/MapboxNavigation/DayStyle.swift index bd49ca1eca9..5d686557a4e 100644 --- a/MapboxNavigation/DayStyle.swift +++ b/MapboxNavigation/DayStyle.swift @@ -25,7 +25,6 @@ extension UIColor { class var trafficModerate: UIColor { get { return #colorLiteral(red: 1, green: 0.5843137255, blue: 0, alpha: 1) } } class var trafficHeavy: UIColor { get { return #colorLiteral(red: 1, green: 0.3019607843, blue: 0.3019607843, alpha: 1) } } class var trafficSevere: UIColor { get { return #colorLiteral(red: 0.5607843137, green: 0.1411764706, blue: 0.2784313725, alpha: 1) } } - class var trafficAlternateLow: UIColor { get { return #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1) } } class var defaultBuildingColor: UIColor { get { return #colorLiteral(red: 0.9833194452, green: 0.9843137255, blue: 0.9331936657, alpha: 0.8019049658) } } class var defaultBuildingHighlightColor: UIColor { get { return #colorLiteral(red: 0.337254902, green: 0.6588235294, blue: 0.9843137255, alpha: 0.949406036) } } diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index c92b7ebe734..283ffba948a 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -1047,11 +1047,13 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { - returns: A list of `RoadClasses` for specific `RouteLeg`. `RoadClasses` will be set to `nil` if it's not present in `Intersection`. */ func roadClasses(_ leg: RouteLeg) -> [RoadClasses?] { - // Iterate over `Intersection` segments for specific `RouteLeg` and leave only valid ones. + // Iterate over `RouteStep`s and pick only valid segment indices for specific `Intersection`. // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. - var intersectionsIndexesByStep = [Int]() - leg.intersectionsIndexesByStep.compactMap({ $0 }).forEach { - intersectionsIndexesByStep.append(contentsOf: $0.compactMap({ $0 })) + var segmentIndicesByIntersection = [Int]() + leg.steps.forEach { + if let segmentIndices = $0.segmentIndicesByIntersection?.compactMap({ $0 }) { + segmentIndicesByIntersection.append(contentsOf: segmentIndices) + } } // Iterate over each `Intersection` and save `RoadClasses`. @@ -1060,20 +1062,25 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { var roadClassesInLeg = [RoadClasses?]() leg.steps.forEach { $0.intersections?.forEach { - roadClassesInLeg.append($0.outletRoadClasses) + var roadClass = $0.outletRoadClasses + if let roadClasses = roadClass, roadClasses.isEmpty { + roadClass = nil + } + + roadClassesInLeg.append(roadClass) } } // Iterate over each `Intersection` segment and fill it in with appropriate `RoadClasses`. // At the end amount of `RoadClasses` should be equal to the last segment index. var roadClasses = [RoadClasses?]() - for (index, _) in intersectionsIndexesByStep.enumerated() { + for (index, _) in segmentIndicesByIntersection.enumerated() { let nextIndex = index + 1 - if intersectionsIndexesByStep.indices.contains(nextIndex), + if segmentIndicesByIntersection.indices.contains(nextIndex), roadClassesInLeg.indices.contains(index) { - let currentIndex = intersectionsIndexesByStep[index] - let nextIndex = intersectionsIndexesByStep[nextIndex] + let currentIndex = segmentIndicesByIntersection[index] + let nextIndex = segmentIndicesByIntersection[nextIndex] for _ in currentIndex.. Date: Fri, 13 Nov 2020 14:50:43 -0800 Subject: [PATCH 03/18] Add unit tests for congestion consistency functionality. --- MapboxNavigation.xcodeproj/project.pbxproj | 30 + .../route-with-missing-road-classes.json | 462 ++++++ .../route-with-mixed-road-classes.json | 464 ++++++ .../route-with-not-present-road-classes.json | 1394 +++++++++++++++++ ...e-with-road-classes-single-congestion.json | 464 ++++++ ...ame-congestion-different-road-classes.json | 457 ++++++ .../NavigationMapViewTests.swift | 223 ++- 7 files changed, 3489 insertions(+), 5 deletions(-) mode change 100644 => 100755 MapboxNavigation.xcodeproj/project.pbxproj create mode 100755 MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json create mode 100755 MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json create mode 100755 MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json create mode 100755 MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json create mode 100755 MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 40ce66690c2..e2881e90f37 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -263,7 +263,17 @@ 6441B16A1EFC64E50076499F /* WaypointConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6441B1691EFC64E50076499F /* WaypointConfirmationViewController.swift */; }; 64847A041F04629D003F3A69 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64847A031F04629D003F3A69 /* Feedback.swift */; }; 7C12F2D8225B7C320010A931 /* DCA-Arboretum-dummy-faster-route.json in Resources */ = {isa = PBXBuildFile; fileRef = 7C12F2D7225B7C310010A931 /* DCA-Arboretum-dummy-faster-route.json */; }; + 8A1BA8C4255B82FB004B499B /* route-with-missing-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */; }; + 8A485FDD255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */; }; + 8A485FDE255F2ADE00DCAC7F /* route-with-mixed-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */; }; + 8A485FEB255F2D5A00DCAC7F /* route-with-mixed-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */; }; + 8A485FF2255F2D5E00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */; }; + 8A485FFA255F333C00DCAC7F /* route-with-not-present-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */; }; + 8A486007255F339100DCAC7F /* route-with-not-present-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */; }; + 8A48600F255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */; }; + 8A48601C255F3B5D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */; }; 8AA849E924E722410008EE59 /* WaypointStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA849E824E722410008EE59 /* WaypointStyle.swift */; }; + 8AE53F29255B811800D1B668 /* route-with-missing-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */; }; 8D07C5A820B612310093D779 /* EmptyStyle.json in Resources */ = {isa = PBXBuildFile; fileRef = 8D07C5A720B612310093D779 /* EmptyStyle.json */; }; 8D1A5CD2212DDFCD0059BA4A /* DispatchTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D1A5CD1212DDFCD0059BA4A /* DispatchTimer.swift */; }; 8D24A2F62040960C0098CBF8 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24A2F52040960C0098CBF8 /* UIEdgeInsets.swift */; }; @@ -880,7 +890,12 @@ 6441B1691EFC64E50076499F /* WaypointConfirmationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WaypointConfirmationViewController.swift; path = Example/WaypointConfirmationViewController.swift; sourceTree = ""; }; 64847A031F04629D003F3A69 /* Feedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = ""; }; 7C12F2D7225B7C310010A931 /* DCA-Arboretum-dummy-faster-route.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "DCA-Arboretum-dummy-faster-route.json"; sourceTree = ""; }; + 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-road-classes-single-congestion.json"; sourceTree = ""; }; + 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-mixed-road-classes.json"; sourceTree = ""; }; + 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-not-present-road-classes.json"; sourceTree = ""; }; + 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-same-congestion-different-road-classes.json"; sourceTree = ""; }; 8AA849E824E722410008EE59 /* WaypointStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointStyle.swift; sourceTree = ""; }; + 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-missing-road-classes.json"; sourceTree = ""; }; 8B808F852487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Main.strings; sourceTree = ""; }; 8B808F862487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Navigation.strings; sourceTree = ""; }; 8B808F892487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; @@ -1431,6 +1446,11 @@ isa = PBXGroup; children = ( B48CEFAB25796F5A00696BB3 /* route-for-vanishing-route-line.json */, + 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */, + 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */, + 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */, + 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */, + 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */, 35EB9A6920A1A89500CB1225 /* turn_left.data */, 8D07C5A720B612310093D779 /* EmptyStyle.json */, 355DB5741EFA78070091BFB7 /* GGPark-to-BernalHeights.route */, @@ -2223,18 +2243,23 @@ buildActionMask = 2147483647; files = ( DA1755F92357B7A100B06C1D /* md5_crazy_strings.txt in Resources */, + 8A485FDD255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */, 351030111F54B72000E3B7E7 /* route-for-lane-testing.json in Resources */, 3527D2B91EC4619400C07FC9 /* Fixtures.xcassets in Resources */, 355DB5771EFA780E0091BFB7 /* UnionSquare-to-GGPark.route in Resources */, 3597ABD421553F6800C12785 /* sthlm-double-back.json in Resources */, 355DB5751EFA78070091BFB7 /* GGPark-to-BernalHeights.route in Resources */, + 8AE53F29255B811800D1B668 /* route-with-missing-road-classes.json in Resources */, 35EB9A6A20A1AB7C00CB1225 /* turn_left.data in Resources */, 3540514D1F73F3BB00ED572D /* route-with-straight-roundabout.json in Resources */, 8D07C5A820B612310093D779 /* EmptyStyle.json in Resources */, + 8A48600F255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */, 35C11359215BADF900CC2929 /* sthlm-double-back-replay.json in Resources */, 8D86AE8921C31C640064A304 /* waypoint-after-turn.json in Resources */, AE5F8771209A082500F58FDB /* route-with-banner-instructions.json in Resources */, + 8A485FFA255F333C00DCAC7F /* route-with-not-present-road-classes.json in Resources */, DAA96D18215A961D00BEF703 /* route-doubling-back.json in Resources */, + 8A485FDE255F2ADE00DCAC7F /* route-with-mixed-road-classes.json in Resources */, 35DC9D8F1F4321CC001ECD64 /* route-with-lanes.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2259,6 +2284,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8A1BA8C4255B82FB004B499B /* route-with-missing-road-classes.json in Resources */, 3557506D21A827E800AEF9B6 /* li.tar in Resources */, 35CDA88D2190F5210072B675 /* DCA-Arboretum.json in Resources */, 35CDA8912190F6980072B675 /* route-for-lane-testing.json in Resources */, @@ -2266,7 +2292,9 @@ 3557506F21A8293E00AEF9B6 /* tiles in Resources */, 35CDA8902190F6980072B675 /* route-doubling-back.json in Resources */, 35CDA8952190F6980072B675 /* route-with-straight-roundabout.json in Resources */, + 8A48601C255F3B5D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */, 35F9BC2F2296FCD300364A10 /* 9-legged-route.json in Resources */, + 8A486007255F339100DCAC7F /* route-with-not-present-road-classes.json in Resources */, 35CDA8982190F6980072B675 /* turn_left.data in Resources */, 35CDA8962190F6980072B675 /* sthlm-double-back-replay.json in Resources */, 35CDA8922190F6980072B675 /* route-with-banner-instructions.json in Resources */, @@ -2279,6 +2307,8 @@ 35CDA8992190F6980072B675 /* UnionSquare-to-GGPark.route in Resources */, 357DEC44221AEE150019BAEC /* multileg-route.json in Resources */, 35C8DBF6219194380053328C /* routeWithTunnels_9thStreetDC.json in Resources */, + 8A485FF2255F2D5E00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */, + 8A485FEB255F2D5A00DCAC7F /* route-with-mixed-road-classes.json in Resources */, 35C8DBF52191940C0053328C /* straight-line.json in Resources */, 7C12F2D8225B7C320010A931 /* DCA-Arboretum-dummy-faster-route.json in Resources */, 4341758223060A17004264A9 /* route-with-tertiary.json in Resources */, diff --git a/MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json b/MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json new file mode 100755 index 00000000000..b5b8fc82e4c --- /dev/null +++ b/MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json @@ -0,0 +1,462 @@ +{ + "routeIndex": "0", + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "weight": 260.313, + "weight_name": "auto", + "legs": [ + { + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "summary": "A4", + "admins": [ + { + "iso_3166_1": "PL", + "iso_3166_1_alpha3": "POL" + } + ], + "steps": [ + { + "distance": 1582.757, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.983162, + 51.045254 + ], + "bearing_before": 0.0, + "bearing_after": 135.0, + "instruction": "Drive southeast on A4.", + "type": "depart" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "announcement": "Drive southeast on A4.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eDrive southeast on A4.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 1536.091, + "announcement": "In 1 mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn 1 mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 804.672, + "announcement": "In a half mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn a half mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 111.111, + "announcement": "You have arrived at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eYou have arrived at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "primary": { + "text": "You will arrive at your destination", + "components": [ + { + "text": "You will arrive at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + }, + { + "distanceAlongGeometry": 111.111, + "primary": { + "text": "You have arrived at your destination", + "components": [ + { + "text": "You have arrived at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + } + ], + "driving_side": "right", + "weight": 260.313, + "intersections": [ + { + "location": [ + 16.983162, + 51.045254 + ], + "bearings": [ + 135 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true + ], + "out": 0, + "geometry_index": 0, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.983438, + 51.045078 + ], + "bearings": [ + 135, + 315 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 3, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.985809, + 51.043472 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 24, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + + } + }, + { + "location": [ + 16.98613, + 51.043251 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 28, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.98844, + 51.041656 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 48, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + + } + }, + { + "location": [ + 16.994438, + 51.037525 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 50, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.995361, + 51.036888 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 51, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + } + ] + }, + { + "distance": 0.0, + "duration": 0.0, + "duration_typical": 0.0, + "geometry": "ug|i`B{`ol_@??", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.99843, + 51.034763 + ], + "bearing_before": 138.0, + "bearing_after": 0.0, + "instruction": "You have arrived at your destination.", + "type": "arrive" + }, + "voiceInstructions": [ + + ], + "bannerInstructions": [ + + ], + "driving_side": "right", + "weight": 0.0, + "intersections": [ + { + "location": [ + 16.99843, + 51.034763 + ], + "bearings": [ + 318 + ], + "entry": [ + true + ], + "in": 0, + "geometry_index": 53, + "admin_index": 0 + } + ] + } + ], + "annotation": { + "distance": [ + 3.7, + 12.4, + 11.4, + 12.4, + 12.0, + 11.4, + 12.2, + 12.3, + 11.8, + 12.2, + 12.0, + 12.1, + 12.0, + 11.8, + 12.0, + 12.0, + 12.1, + 12.2, + 11.6, + 12.5, + 12.0, + 12.0, + 11.7, + 4.0, + 8.0, + 12.0, + 12.2, + 1.2, + 10.9, + 11.9, + 11.7, + 12.5, + 11.6, + 12.1, + 12.1, + 12.0, + 12.0, + 12.1, + 12.1, + 12.0, + 12.2, + 11.7, + 12.5, + 12.1, + 12.1, + 11.5, + 12.0, + 13.3, + 58.5, + 564.2, + 95.9, + 122.9, + 196.6 + ], + "congestion": [ + "severe", + "unknown", + "unknown", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe", + "severe" + ] + } + } + ], + "routeOptions": { + "baseUrl": "https://api.mapbox.com", + "user": "mapbox", + "profile": "driving-traffic", + "coordinates": [ + [ + 16.983119, + 51.045222 + ], + [ + 16.99842, + 51.034759 + ] + ], + "alternatives": true, + "language": "en", + "continue_straight": false, + "roundabout_exits": false, + "geometries": "polyline6", + "overview": "full", + "steps": true, + "annotations": "congestion,distance", + "voice_instructions": true, + "banner_instructions": true, + "voice_units": "imperial", + "access_token": "token", + "uuid": "id" + }, + "voiceLocale": "en-US" +} diff --git a/MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json b/MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json new file mode 100755 index 00000000000..7a82770d939 --- /dev/null +++ b/MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json @@ -0,0 +1,464 @@ +{ + "routeIndex": "0", + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "weight": 260.313, + "weight_name": "auto", + "legs": [ + { + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "summary": "A4", + "admins": [ + { + "iso_3166_1": "PL", + "iso_3166_1_alpha3": "POL" + } + ], + "steps": [ + { + "distance": 1582.757, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.983162, + 51.045254 + ], + "bearing_before": 0.0, + "bearing_after": 135.0, + "instruction": "Drive southeast on A4.", + "type": "depart" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "announcement": "Drive southeast on A4.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eDrive southeast on A4.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 1536.091, + "announcement": "In 1 mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn 1 mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 804.672, + "announcement": "In a half mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn a half mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 111.111, + "announcement": "You have arrived at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eYou have arrived at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "primary": { + "text": "You will arrive at your destination", + "components": [ + { + "text": "You will arrive at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + }, + { + "distanceAlongGeometry": 111.111, + "primary": { + "text": "You have arrived at your destination", + "components": [ + { + "text": "You have arrived at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + } + ], + "driving_side": "right", + "weight": 260.313, + "intersections": [ + { + "location": [ + 16.983162, + 51.045254 + ], + "bearings": [ + 135 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true + ], + "out": 0, + "geometry_index": 0, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.983438, + 51.045078 + ], + "bearings": [ + 135, + 315 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 3, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.985809, + 51.043472 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 24, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.98613, + 51.043251 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 28, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.98844, + 51.041656 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 48, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.994438, + 51.037525 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 50, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.995361, + 51.036888 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 51, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + } + ] + }, + { + "distance": 0.0, + "duration": 0.0, + "duration_typical": 0.0, + "geometry": "ug|i`B{`ol_@??", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.99843, + 51.034763 + ], + "bearing_before": 138.0, + "bearing_after": 0.0, + "instruction": "You have arrived at your destination.", + "type": "arrive" + }, + "voiceInstructions": [ + + ], + "bannerInstructions": [ + + ], + "driving_side": "right", + "weight": 0.0, + "intersections": [ + { + "location": [ + 16.99843, + 51.034763 + ], + "bearings": [ + 318 + ], + "entry": [ + true + ], + "in": 0, + "geometry_index": 53, + "admin_index": 0 + } + ] + } + ], + "annotation": { + "distance": [ + 3.7, + 12.4, + 11.4, + 12.4, + 12.0, + 11.4, + 12.2, + 12.3, + 11.8, + 12.2, + 12.0, + 12.1, + 12.0, + 11.8, + 12.0, + 12.0, + 12.1, + 12.2, + 11.6, + 12.5, + 12.0, + 12.0, + 11.7, + 4.0, + 8.0, + 12.0, + 12.2, + 1.2, + 10.9, + 11.9, + 11.7, + 12.5, + 11.6, + 12.1, + 12.1, + 12.0, + 12.0, + 12.1, + 12.1, + 12.0, + 12.2, + 11.7, + 12.5, + 12.1, + 12.1, + 11.5, + 12.0, + 13.3, + 58.5, + 564.2, + 95.9, + 122.9, + 196.6 + ], + "congestion": [ + "unknown", + "severe", + "severe", + "unknown", + "severe", + "severe", + "severe", + "severe", + "severe", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown" + ] + } + } + ], + "routeOptions": { + "baseUrl": "https://api.mapbox.com", + "user": "mapbox", + "profile": "driving-traffic", + "coordinates": [ + [ + 16.983119, + 51.045222 + ], + [ + 16.99842, + 51.034759 + ] + ], + "alternatives": true, + "language": "en", + "continue_straight": false, + "roundabout_exits": false, + "geometries": "polyline6", + "overview": "full", + "steps": true, + "annotations": "congestion,distance", + "voice_instructions": true, + "banner_instructions": true, + "voice_units": "imperial", + "access_token": "token", + "uuid": "id" + }, + "voiceLocale": "en-US" +} diff --git a/MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json b/MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json new file mode 100755 index 00000000000..276150b5ed3 --- /dev/null +++ b/MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json @@ -0,0 +1,1394 @@ +{ + "routeIndex": "0", + "distance": 1335.036, + "duration": 377.934, + "duration_typical": 377.934, + "geometry": "mtylgAd`guhFzJxBl@aH`Ca\\je@hLlKxBzKxBpB^jFz@f^`HrVfDbQvClDl@nDl@zUfDr[rFnC\\fDl@ba@pGfIlAbFz@bBgc@\\aGNyCLcFfE]fDkBpByBpBwCbByBrAiCpBuEbAiBbByB~CwCnIsFhMkLj~@a|@bLbGdE\\|JzAvIl@`WzA~Hl@nIwDvNoHfIsGjA{ApByB~HyLxC}IzK}UbQ}]{GuFwCrG", + "weight": 451.469, + "weight_name": "auto", + "legs": [ + { + "distance": 1335.036, + "duration": 377.934, + "duration_typical": 377.934, + "summary": "Lincoln Avenue, Francisco Boulevard West", + "admins": [ + { + "iso_3166_1": "US", + "iso_3166_1_alpha3": "USA" + } + ], + "steps": [ + { + "distance": 22.292, + "duration": 13.375, + "duration_typical": 13.375, + "geometry": "mtylgAd`guhFzJxB", + "name": "", + "mode": "driving", + "maneuver": { + "location": [ + -122.523666, + 37.975384 + ], + "bearing_before": 0, + "bearing_after": 194, + "instruction": "Drive south.", + "type": "depart" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 22.292, + "announcement": "Drive south. Then Turn left onto Laurel Place.", + "ssmlAnnouncement": "Drive south. Then Turn left onto Laurel Place.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 22.292, + "primary": { + "text": "Laurel Place", + "components": [ + { + "text": "Laurel Place", + "type": "text" + } + ], + "type": "turn", + "modifier": "left" + }, + "sub": { + "text": "Lincoln Avenue", + "components": [ + { + "text": "Lincoln Avenue", + "type": "text" + } + ], + "type": "turn", + "modifier": "right" + } + } + ], + "driving_side": "right", + "weight": 15.047, + "intersections": [ + { + "location": [ + -122.523666, + 37.975384 + ], + "bearings": [ + 194 + ], + "entry": [ + true + ], + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 54, + "duration": 27.103, + "duration_typical": 27.103, + "geometry": "qhylgA~cguhFl@aH`Ca\\","name":"LaurelPlace","mode":"driving","maneuver":{"location":[-122.523727,37.975193],"bearing_before":194,"bearing_after":101,"instruction":"TurnleftontoLaurelPlace.","type":"turn","modifier":"left"},"voiceInstructions":[{"distanceAlongGeometry":44,"announcement":"TurnrightontoLincolnAvenue.","ssmlAnnouncement":"Turn right onto Lincoln Avenue.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 54, + "primary": { + "text": "Lincoln Avenue", + "components": [ + { + "text": "Lincoln Avenue", + "type": "text" + } + ], + "type": "turn", + "modifier": "right" + } + } + ], + "driving_side": "right", + "weight": 34.366, + "intersections": [ + { + "location": [ + -122.523727, + 37.975193 + ], + "bearings": [ + 14, + 101 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523582, + 37.97517 + ], + "bearings": [ + 100, + 281 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 496, + "duration": 172.634, + "duration_typical": 172.634, + "geometry": "acylgAz}euhFje@hLlKxBzKxBpB^jFz@f^`HrVfDbQvClDl@nDl@zUfDr[rFnC\\fDl@ba@pGfIlAbFz@", + "name": "Lincoln Avenue", + "mode": "driving", + "maneuver": { + "location": [ + -122.523117, + 37.975105 + ], + "bearing_before": 100, + "bearing_after": 195, + "instruction": "Turn right onto Lincoln Avenue.", + "type": "turn", + "modifier": "right" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 480, + "announcement": "In a quarter mile, Turn left onto 2nd Street.", + "ssmlAnnouncement": "In a quarter mile, Turn left onto 2nd Street.<\/prosody><\/amazon:effect><\/speak>" + }, + { + "distanceAlongGeometry": 101.333, + "announcement": "Turn left onto 2nd Street. Then Turn right onto Francisco Boulevard West.", + "ssmlAnnouncement": "Turn left onto 2nd Street. Then Turn right onto Francisco Boulevard West.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 496, + "primary": { + "text": "2nd Street", + "components": [ + { + "text": "2nd Street", + "type": "text" + } + ], + "type": "turn", + "modifier": "left" + }, + "sub": { + "text": "Francisco Boulevard West", + "components": [ + { + "text": "Francisco Boulevard West", + "type": "text" + } + ], + "type": "turn", + "modifier": "right" + } + } + ], + "driving_side": "right", + "weight": 196.223, + "intersections": [ + { + "location": [ + -122.523117, + 37.975105 + ], + "bearings": [ + 195, + 280 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523392, + 37.974293 + ], + "bearings": [ + 13, + 193 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523453, + 37.974087 + ], + "bearings": [ + 13, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523499, + 37.973911 + ], + "bearings": [ + 12, + 193 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523643, + 37.973412 + ], + "bearings": [ + 13, + 190 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523727, + 37.973034 + ], + "bearings": [ + 10, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523804, + 37.972744 + ], + "bearings": [ + 12, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523827, + 37.972656 + ], + "bearings": [ + 12, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523849, + 37.972569 + ], + "bearings": [ + 12, + 190 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523933, + 37.972202 + ], + "bearings": [ + 10, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.524055, + 37.971745 + ], + "bearings": [ + 12, + 189 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.524071, + 37.971672 + ], + "bearings": [ + 9, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.524094, + 37.971588 + ], + "bearings": [ + 12, + 191 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.524231, + 37.971043 + ], + "bearings": [ + 11, + 190 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.524269, + 37.970879 + ], + "bearings": [ + 10, + 192 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 80, + "duration": 23.598, + "duration_typical": 23.598, + "geometry": "wsplgAvghuhFbBgc@\\aGNyCLcF", + "name": "2nd Street", + "mode": "driving", + "maneuver": { + "location": [ + -122.5243, + 37.970764 + ], + "bearing_before": 192, + "bearing_after": 96, + "instruction": "Turn left onto 2nd Street.", + "type": "turn", + "modifier": "left" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 71.111, + "announcement": "Turn right onto Francisco Boulevard West.", + "ssmlAnnouncement": "Turn right onto Francisco Boulevard West.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 80, + "primary": { + "text": "Francisco Boulevard West", + "components": [ + { + "text": "Francisco Boulevard West", + "type": "text" + } + ], + "type": "turn", + "modifier": "right" + } + } + ], + "driving_side": "right", + "weight": 29.888, + "intersections": [ + { + "location": [ + -122.5243, + 37.970764 + ], + "bearings": [ + 12, + 96 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.52372, + 37.970715 + ], + "bearings": [ + 98, + 276 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.52359, + 37.970699 + ], + "bearings": [ + 97, + 278 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523514, + 37.970692 + ], + "bearings": [ + 95, + 277 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 286, + "duration": 33.487, + "duration_typical": 33.487, + "geometry": "wnplgAnofuhFfE]fDkBpByBpBwCbByBrAiCpBuEbAiBbByB~CwCnIsFhMkLj~@a|@", + "name": "Francisco Boulevard West", + "mode": "driving", + "maneuver": { + "location": [ + -122.523399, + 37.970684 + ], + "bearing_before": 95, + "bearing_after": 163, + "instruction": "Turn right onto Francisco Boulevard West.", + "type": "turn", + "modifier": "right" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 272.667, + "announcement": "In 900 feet, Turn right onto Irwin Street.", + "ssmlAnnouncement": "In 900 feet, Turn right onto Irwin Street.<\/prosody><\/amazon:effect><\/speak>" + }, + { + "distanceAlongGeometry": 66.667, + "announcement": "Turn right onto Irwin Street.", + "ssmlAnnouncement": "Turn right onto Irwin Street.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 286, + "primary": { + "text": "Irwin Street", + "components": [ + { + "text": "Irwin Street", + "type": "text" + } + ], + "type": "turn", + "modifier": "right" + } + } + ], + "driving_side": "right", + "weight": 42.61, + "intersections": [ + { + "location": [ + -122.523399, + 37.970684 + ], + "bearings": [ + 163, + 275 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523331, + 37.970501 + ], + "bearings": [ + 137, + 343 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523193, + 37.970387 + ], + "bearings": [ + 132, + 317 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.523064, + 37.970295 + ], + "bearings": [ + 126, + 312 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.522903, + 37.970203 + ], + "bearings": [ + 140, + 306 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 139, + "duration": 34.933, + "duration_typical": 34.933, + "geometry": "kpllgAzubuhFbLbGdE\\|JzAvIl@`WzA~Hl@", + "name": "Irwin Street", + "mode": "driving", + "maneuver": { + "location": [ + -122.521454, + 37.968662 + ], + "bearing_before": 142, + "bearing_after": 206, + "instruction": "Turn right onto Irwin Street.", + "type": "turn", + "modifier": "right" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 129, + "announcement": "In 500 feet, Bear left onto Du Bois Street.", + "ssmlAnnouncement": "In 500 feet, Bear left onto Du Bois Street.<\/prosody><\/amazon:effect><\/speak>" + }, + { + "distanceAlongGeometry": 53.333, + "announcement": "Bear left onto Du Bois Street.", + "ssmlAnnouncement": "Bear left onto Du Bois Street.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 139, + "primary": { + "text": "Du Bois Street", + "components": [ + { + "text": "Du Bois Street", + "type": "text" + } + ], + "type": "turn", + "modifier": "slight left" + } + } + ], + "driving_side": "right", + "weight": 43.71, + "intersections": [ + { + "location": [ + -122.521454, + 37.968662 + ], + "bearings": [ + 206, + 322 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521584, + 37.968452 + ], + "bearings": [ + 26, + 187 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521599, + 37.968353 + ], + "bearings": [ + 7, + 191 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521645, + 37.968163 + ], + "bearings": [ + 11, + 186 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521667, + 37.967991 + ], + "bearings": [ + 6, + 185 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 224, + "duration": 41.801, + "duration_typical": 41.801, + "geometry": "idjlgApgcuhFnIwDvNoHfIsGjA{ApByB~HyLxC}IzK}UbQ}]", + "name": "Du Bois Street", + "mode": "driving", + "maneuver": { + "location": [ + -122.521736, + 37.967445 + ], + "bearing_before": 188, + "bearing_after": 156, + "instruction": "Bear left onto Du Bois Street.", + "type": "turn", + "modifier": "slight left" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 214, + "announcement": "In 700 feet, Turn left.", + "ssmlAnnouncement": "In 700 feet, Turn left.<\/prosody><\/amazon:effect><\/speak>" + }, + { + "distanceAlongGeometry": 45.833, + "announcement": "Turn left. Then Turn left.", + "ssmlAnnouncement": "Turn left. Then Turn left.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 224, + "primary": { + "text": "Turn left", + "components": [ + { + "text": "Turn left", + "type": "text" + } + ], + "type": "turn", + "modifier": "left" + }, + "sub": { + "text": "Turn left", + "components": [ + { + "text": "Turn left", + "type": "text" + } + ], + "type": "turn", + "modifier": "left" + } + } + ], + "driving_side": "right", + "weight": 51.696, + "intersections": [ + { + "location": [ + -122.521736, + 37.967445 + ], + "bearings": [ + 8, + 156 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521492, + 37.967026 + ], + "bearings": [ + 147, + 334 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521355, + 37.966862 + ], + "bearings": [ + 137, + 327 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521309, + 37.966824 + ], + "bearings": [ + 140, + 317 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.521248, + 37.966766 + ], + "bearings": [ + 134, + 320 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.520851, + 37.96653 + ], + "bearings": [ + 126, + 298 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + }, + { + "location": [ + -122.520485, + 37.966324 + ], + "bearings": [ + 127, + 306 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 19, + "duration": 13.42, + "duration_typical": 13.42, + "geometry": "alglgAjz_uhF{GuF", + "name": "", + "mode": "driving", + "maneuver": { + "location": [ + -122.519989, + 37.966034 + ], + "bearing_before": 127, + "bearing_after": 34, + "instruction": "Turn left.", + "type": "turn", + "modifier": "left" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 19, + "announcement": "Turn left. Then Your destination will be on the left.", + "ssmlAnnouncement": "Turn left. Then Your destination will be on the left.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 19, + "primary": { + "text": "Turn left", + "components": [ + { + "text": "Turn left", + "type": "text" + } + ], + "type": "turn", + "modifier": "left" + } + } + ], + "driving_side": "right", + "weight": 19.018, + "intersections": [ + { + "location": [ + -122.519989, + 37.966034 + ], + "bearings": [ + 34, + 307 + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 14.744, + "duration": 17.583, + "duration_typical": 17.583, + "geometry": "}tglgAtr_uhFwCrG", + "name": "", + "mode": "driving", + "maneuver": { + "location": [ + -122.519867, + 37.966175 + ], + "bearing_before": 34, + "bearing_after": 305, + "instruction": "Turn left.", + "type": "turn", + "modifier": "left" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 14.744, + "announcement": "Your destination is on the left.", + "ssmlAnnouncement": "Your destination is on the left.<\/prosody><\/amazon:effect><\/speak>" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 14.744, + "primary": { + "text": "Your destination is on the left", + "components": [ + { + "text": "Your destination is on the left", + "type": "text" + } + ], + "type": "arrive", + "modifier": "left" + } + } + ], + "driving_side": "right", + "weight": 18.91, + "intersections": [ + { + "location": [ + -122.519867, + 37.966175 + ], + "bearings": [ + 214, + 305 + ], + "entry": [ + false, + true + ], + "in": 0, + "out": 1, + "is_urban": true, + "admin_index": 0 + } + ] + }, + { + "distance": 0, + "duration": 0, + "duration_typical": 0, + "geometry": "uyglgAh{_uhF??", + "name": "", + "mode": "driving", + "maneuver": { + "location": [ + -122.520004, + 37.966251 + ], + "bearing_before": 305, + "bearing_after": 0, + "instruction": "Your destination is on the left.", + "type": "arrive", + "modifier": "left" + }, + "voiceInstructions": [ + + ], + "bannerInstructions": [ + + ], + "driving_side": "right", + "weight": 0, + "intersections": [ + { + "location": [ + -122.520004, + 37.966251 + ], + "bearings": [ + 125 + ], + "entry": [ + true + ], + "in": 0, + "admin_index": 0 + } + ] + } + ], + "annotation": { + "distance": [ + 21.9, + 13, + 41.5, + 70.9, + 22.7, + 23.5, + 6.5, + 13.4, + 57.1, + 42.7, + 33, + 10, + 10, + 41.4, + 52.1, + 8.2, + 9.6, + 61.9, + 18.6, + 13, + 51.2, + 11.5, + 6.7, + 10.1, + 11.1, + 10.5, + 8.3, + 9.2, + 7.7, + 7.6, + 11.3, + 6, + 7.7, + 11.2, + 21.5, + 31.6, + 141.8, + 26, + 11.1, + 21.6, + 19.2, + 43.1, + 17.9, + 20.3, + 31.1, + 21.9, + 5.8, + 8.3, + 26.4, + 17.6, + 39.5, + 54.2, + 19, + 14.7 + ], + "congestion": [ + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "low", + "low", + "low", + "low", + "low", + "low", + "low", + "low", + "low", + "low", + "moderate", + "moderate", + "moderate", + "moderate", + "moderate", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "low", + "low", + "low", + "low", + "low" + ] + } + } + ], + "routeOptions": { + "baseUrl": "https:\/\/api.mapbox.com", + "user": "mapbox", + "profile": "driving-traffic", + "coordinates": [ + [ + -122.5237328, + 37.9753972 + ], + [ + -122.5200609, + 37.9661962 + ] + ], + "alternatives": true, + "language": "en", + "continue_straight": false, + "roundabout_exits": false, + "geometries": "polyline6", + "overview": "full", + "steps": true, + "annotations": "congestion,distance", + "voice_instructions": true, + "banner_instructions": true, + "voice_units": "imperial", + "access_token": "token-here", + "uuid": "4EDxslIYgRqso6fVkA3NC124fG5-200qq3ohWVmcRzqNo4-otP61qw==" + }, + "voiceLocale": "en-US" +} diff --git a/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json b/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json new file mode 100755 index 00000000000..8a25b7f408f --- /dev/null +++ b/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json @@ -0,0 +1,464 @@ +{ + "routeIndex": "0", + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "weight": 260.313, + "weight_name": "auto", + "legs": [ + { + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "summary": "A4", + "admins": [ + { + "iso_3166_1": "PL", + "iso_3166_1_alpha3": "POL" + } + ], + "steps": [ + { + "distance": 1582.757, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.983162, + 51.045254 + ], + "bearing_before": 0.0, + "bearing_after": 135.0, + "instruction": "Drive southeast on A4.", + "type": "depart" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "announcement": "Drive southeast on A4.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eDrive southeast on A4.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 1536.091, + "announcement": "In 1 mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn 1 mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 804.672, + "announcement": "In a half mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn a half mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 111.111, + "announcement": "You have arrived at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eYou have arrived at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "primary": { + "text": "You will arrive at your destination", + "components": [ + { + "text": "You will arrive at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + }, + { + "distanceAlongGeometry": 111.111, + "primary": { + "text": "You have arrived at your destination", + "components": [ + { + "text": "You have arrived at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + } + ], + "driving_side": "right", + "weight": 260.313, + "intersections": [ + { + "location": [ + 16.983162, + 51.045254 + ], + "bearings": [ + 135 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true + ], + "out": 0, + "geometry_index": 0, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.983438, + 51.045078 + ], + "bearings": [ + 135, + 315 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 3, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.985809, + 51.043472 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 24, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.98613, + 51.043251 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 28, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.98844, + 51.041656 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 48, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.994438, + 51.037525 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 50, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.995361, + 51.036888 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "toll", + "tunnel" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 51, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + } + ] + }, + { + "distance": 0.0, + "duration": 0.0, + "duration_typical": 0.0, + "geometry": "ug|i`B{`ol_@??", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.99843, + 51.034763 + ], + "bearing_before": 138.0, + "bearing_after": 0.0, + "instruction": "You have arrived at your destination.", + "type": "arrive" + }, + "voiceInstructions": [ + + ], + "bannerInstructions": [ + + ], + "driving_side": "right", + "weight": 0.0, + "intersections": [ + { + "location": [ + 16.99843, + 51.034763 + ], + "bearings": [ + 318 + ], + "entry": [ + true + ], + "in": 0, + "geometry_index": 53, + "admin_index": 0 + } + ] + } + ], + "annotation": { + "distance": [ + 3.7, + 12.4, + 11.4, + 12.4, + 12.0, + 11.4, + 12.2, + 12.3, + 11.8, + 12.2, + 12.0, + 12.1, + 12.0, + 11.8, + 12.0, + 12.0, + 12.1, + 12.2, + 11.6, + 12.5, + 12.0, + 12.0, + 11.7, + 4.0, + 8.0, + 12.0, + 12.2, + 1.2, + 10.9, + 11.9, + 11.7, + 12.5, + 11.6, + 12.1, + 12.1, + 12.0, + 12.0, + 12.1, + 12.1, + 12.0, + 12.2, + 11.7, + 12.5, + 12.1, + 12.1, + 11.5, + 12.0, + 13.3, + 58.5, + 564.2, + 95.9, + 122.9, + 196.6 + ], + "congestion": [ + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown" + ] + } + } + ], + "routeOptions": { + "baseUrl": "https://api.mapbox.com", + "user": "mapbox", + "profile": "driving-traffic", + "coordinates": [ + [ + 16.983119, + 51.045222 + ], + [ + 16.99842, + 51.034759 + ] + ], + "alternatives": true, + "language": "en", + "continue_straight": false, + "roundabout_exits": false, + "geometries": "polyline6", + "overview": "full", + "steps": true, + "annotations": "congestion,distance", + "voice_instructions": true, + "banner_instructions": true, + "voice_units": "imperial", + "access_token": "token", + "uuid": "id" + }, + "voiceLocale": "en-US" +} diff --git a/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json b/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json new file mode 100755 index 00000000000..b312d9fdc18 --- /dev/null +++ b/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json @@ -0,0 +1,457 @@ +{ + "routeIndex": "0", + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "weight": 260.313, + "weight_name": "auto", + "legs": [ + { + "distance": 1582.758, + "duration": 271.613, + "duration_typical": 271.613, + "summary": "A4", + "admins": [ + { + "iso_3166_1": "PL", + "iso_3166_1_alpha3": "POL" + } + ], + "steps": [ + { + "distance": 1582.757, + "duration": 271.613, + "duration_typical": 271.613, + "geometry": "iwpj`Bqfqk_@l@mA~CwFnCcF~CwFxCoFnCeF~CkF~CsFxCgF~CmF~CcF~CgF~CeFvCgF~CeF`DcF~CgF~CmFvC_FfDkF~CeF~CcFxCeFr@kAjBwC~CeF~CkFLUpCqE~CaFvCcFfDkFxCaF~CgF~CiF~CcF~CeF~CgF~CgF~CeF~CkFxCcFfDmF~CgF~CgFvC}E~CcFnDcGhWeb@zhFwrIxf@ux@`r@{hAvpA}tB", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.983162, + 51.045254 + ], + "bearing_before": 0.0, + "bearing_after": 135.0, + "instruction": "Drive southeast on A4.", + "type": "depart" + }, + "voiceInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "announcement": "Drive southeast on A4.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eDrive southeast on A4.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 1536.091, + "announcement": "In 1 mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn 1 mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 804.672, + "announcement": "In a half mile, You will arrive at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eIn a half mile, You will arrive at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + }, + { + "distanceAlongGeometry": 111.111, + "announcement": "You have arrived at your destination.", + "ssmlAnnouncement": "\u003cspeak\u003e\u003camazon:effect name\u003d\"drc\"\u003e\u003cprosody rate\u003d\"1.08\"\u003eYou have arrived at your destination.\u003c/prosody\u003e\u003c/amazon:effect\u003e\u003c/speak\u003e" + } + ], + "bannerInstructions": [ + { + "distanceAlongGeometry": 1582.757, + "primary": { + "text": "You will arrive at your destination", + "components": [ + { + "text": "You will arrive at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + }, + { + "distanceAlongGeometry": 111.111, + "primary": { + "text": "You have arrived at your destination", + "components": [ + { + "text": "You have arrived at your destination", + "type": "text" + } + ], + "type": "arrive", + "modifier": "straight" + } + } + ], + "driving_side": "right", + "weight": 260.313, + "intersections": [ + { + "location": [ + 16.983162, + 51.045254 + ], + "bearings": [ + 135 + ], + "classes": [ + "tunnel" + ], + "entry": [ + true + ], + "out": 0, + "geometry_index": 0, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "tunnel" + } + }, + { + "location": [ + 16.983438, + 51.045078 + ], + "bearings": [ + 135, + 315 + ], + "classes": [ + "ferry" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 3, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "ferry" + } + }, + { + "location": [ + 16.985809, + 51.043472 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 24, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.98613, + 51.043251 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 28, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.98844, + 51.041656 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 48, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.994438, + 51.037525 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 50, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + }, + { + "location": [ + 16.995361, + 51.036888 + ], + "bearings": [ + 138, + 318 + ], + "classes": [ + "motorway" + ], + "entry": [ + true, + false + ], + "in": 1, + "out": 0, + "geometry_index": 51, + "is_urban": false, + "admin_index": 0, + "mapbox_streets_v8": { + "class": "motorway" + } + } + ] + }, + { + "distance": 0.0, + "duration": 0.0, + "duration_typical": 0.0, + "geometry": "ug|i`B{`ol_@??", + "name": "", + "ref": "A4", + "mode": "driving", + "maneuver": { + "location": [ + 16.99843, + 51.034763 + ], + "bearing_before": 138.0, + "bearing_after": 0.0, + "instruction": "You have arrived at your destination.", + "type": "arrive" + }, + "voiceInstructions": [ + + ], + "bannerInstructions": [ + + ], + "driving_side": "right", + "weight": 0.0, + "intersections": [ + { + "location": [ + 16.99843, + 51.034763 + ], + "bearings": [ + 318 + ], + "entry": [ + true + ], + "in": 0, + "geometry_index": 53, + "admin_index": 0 + } + ] + } + ], + "annotation": { + "distance": [ + 3.7, + 12.4, + 11.4, + 12.4, + 12.0, + 11.4, + 12.2, + 12.3, + 11.8, + 12.2, + 12.0, + 12.1, + 12.0, + 11.8, + 12.0, + 12.0, + 12.1, + 12.2, + 11.6, + 12.5, + 12.0, + 12.0, + 11.7, + 4.0, + 8.0, + 12.0, + 12.2, + 1.2, + 10.9, + 11.9, + 11.7, + 12.5, + 11.6, + 12.1, + 12.1, + 12.0, + 12.0, + 12.1, + 12.1, + 12.0, + 12.2, + 11.7, + 12.5, + 12.1, + 12.1, + 11.5, + 12.0, + 13.3, + 58.5, + 564.2, + 95.9, + 122.9, + 196.6 + ], + "congestion": [ + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown" + ] + } + } + ], + "routeOptions": { + "baseUrl": "https://api.mapbox.com", + "user": "mapbox", + "profile": "driving-traffic", + "coordinates": [ + [ + 16.983119, + 51.045222 + ], + [ + 16.99842, + 51.034759 + ] + ], + "alternatives": true, + "language": "en", + "continue_straight": false, + "roundabout_exits": false, + "geometries": "polyline6", + "overview": "full", + "steps": true, + "annotations": "congestion,distance", + "voice_instructions": true, + "banner_instructions": true, + "voice_units": "imperial", + "access_token": "token", + "uuid": "id" + }, + "voiceLocale": "en-US" +} diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index e58caf40c0d..a5717ae49e1 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -18,8 +18,6 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { return route }() - - override func setUp() { super.setUp() @@ -50,7 +48,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { CLLocationCoordinate2D(latitude: 3, longitude: 3), CLLocationCoordinate2D(latitude: 4, longitude: 4), CLLocationCoordinate2D(latitude: 5, longitude: 5), - ] + ] func testNavigationMapViewCombineWithSimilarCongestions() { let congestionSegments = mapView!.combine(coordinates, with: [ @@ -59,7 +57,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { .low, .low, .low - ]) + ]) XCTAssertEqual(congestionSegments.count, 1) XCTAssertEqual(congestionSegments[0].0.count, 10) @@ -104,7 +102,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { mapView!.removeAnnotations(mapView!.annotations ?? []) XCTAssertNil(mapView!.annotations) } - + func setUpVanishingRouteLine() -> Route { let routeData = Fixture.JSONFromFileNamed(name: "route-for-vanishing-route-line") let routeOptions = NavigationRouteOptions(coordinates: [ @@ -160,6 +158,221 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(distanceArray?[32].distanceRemaining, 0) } + // MARK: - Route congestion consistency tests + + func loadRoute(from jsonFile: String) -> Route { + let defaultRouteOptions = NavigationRouteOptions(coordinates: [ + CLLocationCoordinate2DMake(16.983119, 51.045222), + CLLocationCoordinate2DMake(16.99842, 51.034759) + ]) + + let routeData = Fixture.JSONFromFileNamed(name: jsonFile) + let decoder = JSONDecoder() + decoder.userInfo[.options] = defaultRouteOptions + + var route: Route? = nil + XCTAssertNoThrow(route = try decoder.decode(Route.self, from: routeData)) + + guard let validRoute = route else { + preconditionFailure("Route is invalid.") + } + + return validRoute + } + + func congestionLevel(_ feature: MGLPolylineFeature) -> CongestionLevel? { + guard let congestionLevel = feature.attributes["congestion"] as? String else { return nil } + + return CongestionLevel(rawValue: congestionLevel) + } + + func testOverriddenRouteClassTunnelSingleCongestionLevel() { + let route = loadRoute(from: "route-with-road-classes-single-congestion") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + XCTAssertEqual(congestions?.count, 1) + + // Since `NavigationMapView.addCongestion(to:legIndex:)` merges congestion levels which are similar + // it is expected that only one congestion level is shown for this route. + var expectedCongestionLevel: CongestionLevel = .unknown + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevel) + } + + navigationMapView.trafficOverrideRoadClasses = [.tunnel] + expectedCongestionLevel = .low + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevel) + } + } + + func testOverriddenRouteClassMotorwayMixedCongestionLevels() { + let route = loadRoute(from: "route-with-mixed-road-classes") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + XCTAssertEqual(congestions?.count, 5) + + var expectedCongestionLevels: [CongestionLevel] = [ + .unknown, + .severe, + .unknown, + .severe, + .unknown + ] + + // Since `NavigationMapView.addCongestion(to:legIndex:)` merges congestion levels which are similar + // in such case it is expected that mixed congestion levels remain unmodified. + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + navigationMapView.trafficOverrideRoadClasses = [.motorway] + expectedCongestionLevels = [ + .low, + .severe, + .low, + .severe, + .low + ] + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + } + + func testOverriddenRouteClassMissing() { + let route = loadRoute(from: "route-with-missing-road-classes") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + navigationMapView.trafficOverrideRoadClasses = [.motorway] + + var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + XCTAssertEqual(congestions?.count, 3) + + var expectedCongestionLevels: [CongestionLevel] = [ + .severe, + .low, + .severe + ] + + // In case if `trafficOverrideRoadClasses` was provided with `.motorway` `RoadClass` it is expected + // that any `.unknown` congestion level for such `RoadClass` will be overwritten to `.low` congestion level. + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + navigationMapView.trafficOverrideRoadClasses = [] + expectedCongestionLevels[1] = .unknown + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + // In case if `trafficOverrideRoadClasses` is empty `.unknown` congestion level will not be + // overwritten. + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + // Make sure that at certain indexes `RoadClasses` is not present and assigned to `nil`. + route.legs.forEach { + let roadClasses = navigationMapView.roadClasses($0) + + for index in 24...27 { + XCTAssertEqual(roadClasses[index], nil) + } + } + } + + func testRouteRoadClassesCountEqualToCongestionLevelsCount() { + let route = loadRoute(from: "route-with-missing-road-classes") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + navigationMapView.trafficOverrideRoadClasses = [.motorway] + + // Make sure that number of `RoadClasses` is equal to number of congestion levels. + route.legs.forEach { + let roadClasses = navigationMapView.roadClasses($0) + let segmentCongestionLevels = $0.segmentCongestionLevels + + XCTAssertEqual(roadClasses.count, segmentCongestionLevels?.count) + } + } + + func testRouteRoadClassesNotPresent() { + let route = loadRoute(from: "route-with-not-present-road-classes") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + let expectedCongestionLevels: [CongestionLevel] = [ + .unknown, + .low, + .moderate, + .unknown, + .low + ] + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + navigationMapView.trafficOverrideRoadClasses = [.motorway, .tunnel, .ferry] + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + // Since `RouteClasses` are not present in this route congestion levels should remain unchanged after + // modifying `trafficOverrideRoadClasses`, `roadClasses` should be empty as well. + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + route.legs.forEach { + let roadClasses = navigationMapView.roadClasses($0) + + XCTAssertEqual(roadClasses.count, 0) + } + } + + func testRouteRoadClassesDifferentAndSameCongestion() { + let route = loadRoute(from: "route-with-same-congestion-different-road-classes") + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + var expectedCongestionLevels: [CongestionLevel] = [ + .unknown + ] + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + // It is expected that congestion will be overridden only for `RoadClasses` added in `trafficOverrideRoadClasses`. + // Other congestions should remain unchanged. + expectedCongestionLevels = [ + .low, + .unknown + ] + navigationMapView.trafficOverrideRoadClasses = [.tunnel] + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + navigationMapView.trafficOverrideRoadClasses = [.tunnel, .ferry] + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + + // Since there is only one type of congestion and three `RoadClasses` after overriding all of them + // all congestion levels should we changed from `.unknown` to `.low`. + expectedCongestionLevels = [ + .low + ] + navigationMapView.trafficOverrideRoadClasses = [.tunnel, .ferry, .motorway] + congestions = navigationMapView.addCongestion(to: route, legIndex: 0) + + congestions?.enumerated().forEach { + XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) + } + } } class PersistentAnnotation: MGLPointAnnotation { } From 58fe5180f485b78ba41c01df635e63aa7ff02973 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Fri, 13 Nov 2020 15:28:26 -0800 Subject: [PATCH 04/18] Improve documentation. --- Example/ViewController.swift | 10 +++++++++- MapboxNavigation/NavigationMapView.swift | 7 +++++-- 2 files changed, 14 insertions(+), 3 deletions(-) mode change 100644 => 100755 Example/ViewController.swift diff --git a/Example/ViewController.swift b/Example/ViewController.swift old mode 100644 new mode 100755 index 3201c2281ec..5bd12ce463f --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -271,11 +271,13 @@ class ViewController: UIViewController { // Example of building highlighting in 3D. navigationViewController.waypointStyle = .extrudedBuilding - navigationViewController.detailedFeedbackEnabled = true // Show second level of detail for feedback items. navigationViewController.detailedFeedbackEnabled = true + // Override traffic congestion level from `.unknown` to `.low` for `RoadClasses` passed in `trafficOverrideRoadClasses`. + navigationViewController.mapView?.trafficOverrideRoadClasses = [.motorway] + presentAndRemoveMapview(navigationViewController, completion: beginCarPlayNavigation) } @@ -408,6 +410,12 @@ class ViewController: UIViewController { mapView.showsUserLocation = true mapView.userTrackingMode = .follow mapView.showsUserHeadingIndicator = true + + // Use map style which allows to show road traffic. + mapView.styleURL = MGLStyle.navigationDayStyleURL + + // Override traffic congestion level from `.unknown` to `.low` for `RoadClasses` passed in `trafficOverrideRoadClasses`. + mapView.trafficOverrideRoadClasses = [.motorway] } func uninstall(_ mapView: NavigationMapView) { diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 283ffba948a..be68ad3733e 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -47,7 +47,10 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { public var tapGestureDistanceThreshold: CGFloat = 50 /** - A collection of road classes for which a color substitution should occur. + A collection of road classes for which a congestion level substitution should occur. + + For any road class included in the `trafficOverrideRoadClasses`, all route segments with an `.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` + will be replaced with the `.low` congestion level. */ public var trafficOverrideRoadClasses: [RoadClasses] = [] @@ -1143,7 +1146,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This method coalesces consecutive line segments that have the same congestion level. - For each item in the` CongestionSegment` collection a `CongestionLevel` substitution will take place that has a road class contained in the `trafficOverrideRoadClasses` collection. + For each item in the`CongestionSegment` collection a `CongestionLevel` substitution will take place that has a road class contained in the `trafficOverrideRoadClasses` collection. For each of these items the `CongestionLevel` for `.unknown` traffic congestion will be replaced with the `.low` traffic congestion. - parameter coordinates: The coordinates of a leg. From 48952f9294dff8e37c702ad1cc292c7e87b9dd51 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Fri, 20 Nov 2020 11:51:47 -0800 Subject: [PATCH 05/18] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d4a6c33ab9..cc46407755c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ * The user can now report feedback about an incorrect speed limit in the speed limit view. ([#2725](https://github.com/mapbox/mapbox-navigation-ios/pull/2725)) * Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694) +* Added a new `NavigationMapView.trafficOverrideRoadClasses` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) ## v1.1.0 From 67986cf15b2bc37a522c3c514440cedf15a93100 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Fri, 20 Nov 2020 14:05:24 -0800 Subject: [PATCH 06/18] Use higher order functions for road classes creation. --- MapboxNavigation/NavigationMapView.swift | 47 +++++------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index be68ad3733e..0a78959cec7 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -1050,46 +1050,19 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { - returns: A list of `RoadClasses` for specific `RouteLeg`. `RoadClasses` will be set to `nil` if it's not present in `Intersection`. */ func roadClasses(_ leg: RouteLeg) -> [RoadClasses?] { - // Iterate over `RouteStep`s and pick only valid segment indices for specific `Intersection`. + // Pick only valid segment indices for specific `Intersection` in `RouteStep`. // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. - var segmentIndicesByIntersection = [Int]() - leg.steps.forEach { - if let segmentIndices = $0.segmentIndicesByIntersection?.compactMap({ $0 }) { - segmentIndicesByIntersection.append(contentsOf: segmentIndices) - } - } + let segmentIndices = leg.steps.compactMap({ $0.segmentIndicesByIntersection?.compactMap({ $0 }) }).reduce([], +) - // Iterate over each `Intersection` and save `RoadClasses`. - // Array of `RoadClasses` can look like this: - // [Optional(toll,motorway), ... , Optional(), Optional(toll,motorway), nil] - var roadClassesInLeg = [RoadClasses?]() - leg.steps.forEach { - $0.intersections?.forEach { - var roadClass = $0.outletRoadClasses - if let roadClasses = roadClass, roadClasses.isEmpty { - roadClass = nil - } - - roadClassesInLeg.append(roadClass) - } - } - - // Iterate over each `Intersection` segment and fill it in with appropriate `RoadClasses`. + // Pick `RoadClasses` in specific `Intersection` of `RouteStep`. It's possible that `RoadClasses` can be empty instead of `nil`, if so, + // replace it with `nil`, instead of removing. It is expected that number of `segmentIndices` will be equal to number of `roadClassesInLeg`. + // Array of `RoadClasses` can look like this: [Optional(toll,motorway), ... , Optional(toll,motorway), nil] + let roadClassesInLeg = leg.steps.compactMap({ $0.intersections?.map({ !($0.outletRoadClasses?.isEmpty ?? false) ? $0.outletRoadClasses : nil }) }).reduce([], +) + + // Map each `RoadClasses` to the amount of two adjacent `segmentIndices`. // At the end amount of `RoadClasses` should be equal to the last segment index. - var roadClasses = [RoadClasses?]() - for (index, _) in segmentIndicesByIntersection.enumerated() { - let nextIndex = index + 1 - - if segmentIndicesByIntersection.indices.contains(nextIndex), - roadClassesInLeg.indices.contains(index) { - let currentIndex = segmentIndicesByIntersection[index] - let nextIndex = segmentIndicesByIntersection[nextIndex] - - for _ in currentIndex.. Date: Mon, 23 Nov 2020 15:21:37 -0800 Subject: [PATCH 07/18] Use RoadClasses option set instead of array. --- MapboxNavigation/NavigationMapView.swift | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 0a78959cec7..26e387e1d02 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -49,10 +49,10 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** A collection of road classes for which a congestion level substitution should occur. - For any road class included in the `trafficOverrideRoadClasses`, all route segments with an `.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` - will be replaced with the `.low` congestion level. + For any road class included in the `trafficOverrideRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` + will be replaced with the `CongestionLevel.low` congestion level. */ - public var trafficOverrideRoadClasses: [RoadClasses] = [] + public var trafficOverrideRoadClasses: RoadClasses? = nil /** The object that acts as the navigation delegate of the map view. @@ -1125,13 +1125,13 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { - parameter coordinates: The coordinates of a leg. - parameter congestions: The congestion levels along a leg. There should be one fewer congestion levels than coordinates. - parameter roadClasses: A collection of road classes for each geometry index in `Intersection`. There should be the same amount of `roadClasses` and `congestions`. - - parameter trafficOverrideRoadClasses: A collection of road classes for which a `CongestionLevel` substitution should occur. + - parameter trafficOverrideRoadClasses: Road classes for which a `CongestionLevel` substitution should occur. - returns: A list of `CongestionSegment` tuples with coordinate and congestion level. */ func combine(_ coordinates: [CLLocationCoordinate2D], with congestions: [CongestionLevel], roadClasses: [RoadClasses?]? = nil, - trafficOverrideRoadClasses: [RoadClasses]? = nil) -> [CongestionSegment] { + trafficOverrideRoadClasses: RoadClasses? = nil) -> [CongestionSegment] { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) @@ -1146,11 +1146,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let roadClass = roadClasses[index], congestionLevel == .unknown { - for element in trafficOverrideRoadClasses { - if roadClass.contains(element) { - overriddenCongestionLevel = .low - break - } + if !trafficOverrideRoadClasses.intersection(roadClass).isEmpty { + overriddenCongestionLevel = .low } } From 61adbcab3c946047a021f55ac31996c4cf7068cf Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Tue, 24 Nov 2020 08:24:35 -0800 Subject: [PATCH 08/18] Update to most recent version of Mapbox Directions. --- Cartfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index 2f9f333338c..055e0f1e1f4 100644 --- a/Cartfile +++ b/Cartfile @@ -2,8 +2,7 @@ binary "https://www.mapbox.com/ios-sdk/MapboxAccounts.json" ~> 2.3.0 binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.json" ~> 26.3 binary "https://api.mapbox.com/downloads/v2/carthage/mobile-maps/mapbox-ios-sdk-dynamic.json" ~> 6.0 binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon-ios.json" == 9.1.0 -github "mapbox/mapbox-directions-swift" ~> 1.2.0-alpha.3 -github "mapbox/turf-swift" ~> 1.0 +github "mapbox/mapbox-directions-swift" ~> 1.2.0-alpha.3github "mapbox/turf-swift" ~> 1.0 github "mapbox/mapbox-events-ios" ~> 0.10.2 github "ceeK/Solar" ~> 2.1.0 github "mapbox/mapbox-speech-swift" ~> 1.0 From de67a7d724c3e45ffa43d09cfb919a8229835e11 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 11:38:51 -0800 Subject: [PATCH 09/18] Update to Mapbox Directions v1.2.0-alpha.3. --- Cartfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 055e0f1e1f4..2f9f333338c 100644 --- a/Cartfile +++ b/Cartfile @@ -2,7 +2,8 @@ binary "https://www.mapbox.com/ios-sdk/MapboxAccounts.json" ~> 2.3.0 binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.json" ~> 26.3 binary "https://api.mapbox.com/downloads/v2/carthage/mobile-maps/mapbox-ios-sdk-dynamic.json" ~> 6.0 binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon-ios.json" == 9.1.0 -github "mapbox/mapbox-directions-swift" ~> 1.2.0-alpha.3github "mapbox/turf-swift" ~> 1.0 +github "mapbox/mapbox-directions-swift" ~> 1.2.0-alpha.3 +github "mapbox/turf-swift" ~> 1.0 github "mapbox/mapbox-events-ios" ~> 0.10.2 github "ceeK/Solar" ~> 2.1.0 github "mapbox/mapbox-speech-swift" ~> 1.0 From d0beb48f7a80c81439bd2279abd82b3cec38d796 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 14:07:55 -0800 Subject: [PATCH 10/18] Replace `RoadClasses` with newly added `MapboxStreetsRoadClass`. --- Example/ViewController.swift | 10 ++-- MapboxNavigation/NavigationMapView.swift | 54 +++++++++---------- ...e-with-road-classes-single-congestion.json | 14 ++--- ...ame-congestion-different-road-classes.json | 2 +- .../NavigationMapViewTests.swift | 26 ++++----- 5 files changed, 54 insertions(+), 52 deletions(-) diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 5bd12ce463f..62a0be9fd72 100755 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -275,8 +275,9 @@ class ViewController: UIViewController { // Show second level of detail for feedback items. navigationViewController.detailedFeedbackEnabled = true - // Override traffic congestion level from `.unknown` to `.low` for `RoadClasses` passed in `trafficOverrideRoadClasses`. - navigationViewController.mapView?.trafficOverrideRoadClasses = [.motorway] + // Override traffic congestion level from `.unknown` to `.low` + // for `MapboxStreetsRoadClass` passed in `trafficOverrideStreetsRoadClasses`. + navigationViewController.mapView?.trafficOverrideStreetsRoadClasses = [.motorway] presentAndRemoveMapview(navigationViewController, completion: beginCarPlayNavigation) } @@ -414,8 +415,9 @@ class ViewController: UIViewController { // Use map style which allows to show road traffic. mapView.styleURL = MGLStyle.navigationDayStyleURL - // Override traffic congestion level from `.unknown` to `.low` for `RoadClasses` passed in `trafficOverrideRoadClasses`. - mapView.trafficOverrideRoadClasses = [.motorway] + // Override traffic congestion level from `.unknown` to `.low` + // for `MapboxStreetsRoadClass` passed in `trafficOverrideStreetsRoadClasses`. + mapView.trafficOverrideStreetsRoadClasses = [.motorway] } func uninstall(_ mapView: NavigationMapView) { diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 26e387e1d02..802f570adb4 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -47,12 +47,12 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { public var tapGestureDistanceThreshold: CGFloat = 50 /** - A collection of road classes for which a congestion level substitution should occur. + A collection of street road classes for which a congestion level substitution should occur. - For any road class included in the `trafficOverrideRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` + For any road class included in the `trafficOverrideStreetsRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. */ - public var trafficOverrideRoadClasses: RoadClasses? = nil + public var trafficOverrideStreetsRoadClasses: [MapboxStreetsRoadClass]? = nil /** The object that acts as the navigation delegate of the map view. @@ -1044,27 +1044,27 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } /** - Returns an array of `RoadClasses` for specific leg. + Returns an array of `MapboxStreetsRoadClass` objects for specific leg. - parameter leg: Leg of the route. - - returns: A list of `RoadClasses` for specific `RouteLeg`. `RoadClasses` will be set to `nil` if it's not present in `Intersection`. + - returns: An array of `MapboxStreetsRoadClass` for specific `RouteLeg`. `MapboxStreetsRoadClass` will be set to `nil` if it's not present in `Intersection`. */ - func roadClasses(_ leg: RouteLeg) -> [RoadClasses?] { + func streetsRoadClasses(_ leg: RouteLeg) -> [MapboxStreetsRoadClass?] { // Pick only valid segment indices for specific `Intersection` in `RouteStep`. // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. let segmentIndices = leg.steps.compactMap({ $0.segmentIndicesByIntersection?.compactMap({ $0 }) }).reduce([], +) - // Pick `RoadClasses` in specific `Intersection` of `RouteStep`. It's possible that `RoadClasses` can be empty instead of `nil`, if so, - // replace it with `nil`, instead of removing. It is expected that number of `segmentIndices` will be equal to number of `roadClassesInLeg`. - // Array of `RoadClasses` can look like this: [Optional(toll,motorway), ... , Optional(toll,motorway), nil] - let roadClassesInLeg = leg.steps.compactMap({ $0.intersections?.map({ !($0.outletRoadClasses?.isEmpty ?? false) ? $0.outletRoadClasses : nil }) }).reduce([], +) + // Pick `MapboxStreetsRoadClass` in specific `Intersection` of `RouteStep`. + // It is expected that number of `segmentIndices` will be equal to number of `roadClassesInLeg`. + // Array of `MapboxStreetsRoadClass` can look like this: [Optional(motorway), ... , Optional(motorway), nil] + let streetsRoadClassesInLeg = leg.steps.compactMap({ $0.intersections?.map({ $0.outletMapboxStreetsRoadClass }) }).reduce([], +) - // Map each `RoadClasses` to the amount of two adjacent `segmentIndices`. - // At the end amount of `RoadClasses` should be equal to the last segment index. - let roadClasses = segmentIndices.enumerated().map({ segmentIndices.indices.contains($0.offset + 1) && roadClassesInLeg.indices.contains($0.offset) ? - Array(repeating: roadClassesInLeg[$0.offset], count: segmentIndices[$0.offset + 1] - segmentIndices[$0.offset]) : [] }).reduce([], +) + // Map each `MapboxStreetsRoadClass` to the amount of two adjacent `segmentIndices`. + // At the end amount of `MapboxStreetsRoadClass` should be equal to the last segment index. + let streetsRoadClasses = segmentIndices.enumerated().map({ segmentIndices.indices.contains($0.offset + 1) && streetsRoadClassesInLeg.indices.contains($0.offset) ? + Array(repeating: streetsRoadClassesInLeg[$0.offset], count: segmentIndices[$0.offset + 1] - segmentIndices[$0.offset]) : [] }).reduce([], +) - return roadClasses + return streetsRoadClasses } func addCongestion(to route: Route, legIndex: Int?) -> [MGLPolylineFeature]? { @@ -1086,8 +1086,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let mergedCongestionSegments = combine(legCoordinates, with: legCongestion, - roadClasses: roadClasses(leg), - trafficOverrideRoadClasses: trafficOverrideRoadClasses) + streetsRoadClasses: streetsRoadClasses(leg), + trafficOverrideStreetsRoadClasses: trafficOverrideStreetsRoadClasses) lines = mergedCongestionSegments.map { (congestionSegment: CongestionSegment) -> MGLPolylineFeature in let polyline = MGLPolylineFeature(coordinates: congestionSegment.0, count: UInt(congestionSegment.0.count)) @@ -1119,19 +1119,19 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This method coalesces consecutive line segments that have the same congestion level. - For each item in the`CongestionSegment` collection a `CongestionLevel` substitution will take place that has a road class contained in the `trafficOverrideRoadClasses` collection. + For each item in the`CongestionSegment` collection a `CongestionLevel` substitution will take place that has a streets road class contained in the `trafficOverrideStreetsRoadClasses` collection. For each of these items the `CongestionLevel` for `.unknown` traffic congestion will be replaced with the `.low` traffic congestion. - parameter coordinates: The coordinates of a leg. - parameter congestions: The congestion levels along a leg. There should be one fewer congestion levels than coordinates. - - parameter roadClasses: A collection of road classes for each geometry index in `Intersection`. There should be the same amount of `roadClasses` and `congestions`. - - parameter trafficOverrideRoadClasses: Road classes for which a `CongestionLevel` substitution should occur. + - parameter streetsRoadClasses: A collection of streets road classes for each geometry index in `Intersection`. There should be the same amount of `streetsRoadClasses` and `congestions`. + - parameter trafficOverrideStreetsRoadClasses: Streets road classes for which a `CongestionLevel` substitution should occur. - returns: A list of `CongestionSegment` tuples with coordinate and congestion level. */ func combine(_ coordinates: [CLLocationCoordinate2D], with congestions: [CongestionLevel], - roadClasses: [RoadClasses?]? = nil, - trafficOverrideRoadClasses: RoadClasses? = nil) -> [CongestionSegment] { + streetsRoadClasses: [MapboxStreetsRoadClass?]? = nil, + trafficOverrideStreetsRoadClasses: [MapboxStreetsRoadClass]? = nil) -> [CongestionSegment] { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) @@ -1140,13 +1140,13 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let coordinates = [firstSegment.0, firstSegment.1] var overriddenCongestionLevel = congestionLevel - if let roadClasses = roadClasses, - let trafficOverrideRoadClasses = trafficOverrideRoadClasses, - roadClasses.indices.contains(index), - let roadClass = roadClasses[index], + if let streetsRoadClasses = streetsRoadClasses, + let trafficOverrideStreetsRoadClasses = trafficOverrideStreetsRoadClasses, + streetsRoadClasses.indices.contains(index), + let streetsRoadClass = streetsRoadClasses[index], congestionLevel == .unknown { - if !trafficOverrideRoadClasses.intersection(roadClass).isEmpty { + if trafficOverrideStreetsRoadClasses.contains(streetsRoadClass) { overriddenCongestionLevel = .low } } diff --git a/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json b/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json index 8a25b7f408f..75f4dfb4e52 100755 --- a/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json +++ b/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json @@ -112,7 +112,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -138,7 +138,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -164,7 +164,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -190,7 +190,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -216,7 +216,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -242,7 +242,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } }, { @@ -268,7 +268,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "golf" } } ] diff --git a/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json b/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json index b312d9fdc18..8bfa2e402fe 100755 --- a/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json +++ b/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json @@ -111,7 +111,7 @@ "is_urban": false, "admin_index": 0, "mapbox_streets_v8": { - "class": "tunnel" + "class": "street" } }, { diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index a5717ae49e1..096d9a239d2 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -199,7 +199,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevel) } - navigationMapView.trafficOverrideRoadClasses = [.tunnel] + navigationMapView.trafficOverrideStreetsRoadClasses = [.golf] expectedCongestionLevel = .low congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -228,7 +228,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideRoadClasses = [.motorway] + navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] expectedCongestionLevels = [ .low, .severe, @@ -246,7 +246,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { func testOverriddenRouteClassMissing() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) - navigationMapView.trafficOverrideRoadClasses = [.motorway] + navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) XCTAssertEqual(congestions?.count, 3) @@ -263,7 +263,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideRoadClasses = [] + navigationMapView.trafficOverrideStreetsRoadClasses = [] expectedCongestionLevels[1] = .unknown congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -275,7 +275,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { // Make sure that at certain indexes `RoadClasses` is not present and assigned to `nil`. route.legs.forEach { - let roadClasses = navigationMapView.roadClasses($0) + let roadClasses = navigationMapView.streetsRoadClasses($0) for index in 24...27 { XCTAssertEqual(roadClasses[index], nil) @@ -286,11 +286,11 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { func testRouteRoadClassesCountEqualToCongestionLevelsCount() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) - navigationMapView.trafficOverrideRoadClasses = [.motorway] + navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] // Make sure that number of `RoadClasses` is equal to number of congestion levels. route.legs.forEach { - let roadClasses = navigationMapView.roadClasses($0) + let roadClasses = navigationMapView.streetsRoadClasses($0) let segmentCongestionLevels = $0.segmentCongestionLevels XCTAssertEqual(roadClasses.count, segmentCongestionLevels?.count) @@ -313,7 +313,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideRoadClasses = [.motorway, .tunnel, .ferry] + navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway, .secondary, .ferry] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) // Since `RouteClasses` are not present in this route congestion levels should remain unchanged after @@ -323,8 +323,8 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } route.legs.forEach { - let roadClasses = navigationMapView.roadClasses($0) - + let roadClasses = navigationMapView.streetsRoadClasses($0) + XCTAssertEqual(roadClasses.count, 0) } } @@ -347,14 +347,14 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { .low, .unknown ] - navigationMapView.trafficOverrideRoadClasses = [.tunnel] + navigationMapView.trafficOverrideStreetsRoadClasses = [.street] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideRoadClasses = [.tunnel, .ferry] + navigationMapView.trafficOverrideStreetsRoadClasses = [.street, .ferry] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { @@ -366,7 +366,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { expectedCongestionLevels = [ .low ] - navigationMapView.trafficOverrideRoadClasses = [.tunnel, .ferry, .motorway] + navigationMapView.trafficOverrideStreetsRoadClasses = [.street, .ferry, .motorway] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { From 2707c2ce31431b8a2ec695f28d4eaf68de681a62 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 14:21:29 -0800 Subject: [PATCH 11/18] Further renaming after moving to `MapboxDirections.MapboxStreetsRoadClass`. --- CHANGELOG.md | 2 +- MapboxNavigation/NavigationMapView.swift | 2 +- .../NavigationMapViewTests.swift | 40 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc46407755c..24ddb2af56c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ * The user can now report feedback about an incorrect speed limit in the speed limit view. ([#2725](https://github.com/mapbox/mapbox-navigation-ios/pull/2725)) * Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694) -* Added a new `NavigationMapView.trafficOverrideRoadClasses` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.RoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) +* Added a new `NavigationMapView.trafficOverrideStreetsRoadClasses` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) ## v1.1.0 diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 802f570adb4..fc55b7c9cde 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -1055,7 +1055,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let segmentIndices = leg.steps.compactMap({ $0.segmentIndicesByIntersection?.compactMap({ $0 }) }).reduce([], +) // Pick `MapboxStreetsRoadClass` in specific `Intersection` of `RouteStep`. - // It is expected that number of `segmentIndices` will be equal to number of `roadClassesInLeg`. + // It is expected that number of `segmentIndices` will be equal to number of `streetsRoadClassesInLeg`. // Array of `MapboxStreetsRoadClass` can look like this: [Optional(motorway), ... , Optional(motorway), nil] let streetsRoadClassesInLeg = leg.steps.compactMap({ $0.intersections?.map({ $0.outletMapboxStreetsRoadClass }) }).reduce([], +) diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index 096d9a239d2..c1860b89567 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -186,7 +186,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { return CongestionLevel(rawValue: congestionLevel) } - func testOverriddenRouteClassTunnelSingleCongestionLevel() { + func testOverriddenStreetsRouteClassTunnelSingleCongestionLevel() { let route = loadRoute(from: "route-with-road-classes-single-congestion") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -208,7 +208,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } } - func testOverriddenRouteClassMotorwayMixedCongestionLevels() { + func testOverriddenStreetsRouteClassMotorwayMixedCongestionLevels() { let route = loadRoute(from: "route-with-mixed-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -243,7 +243,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } } - func testOverriddenRouteClassMissing() { + func testOverriddenStreetsRouteClassMissing() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] @@ -257,8 +257,8 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { .severe ] - // In case if `trafficOverrideRoadClasses` was provided with `.motorway` `RoadClass` it is expected - // that any `.unknown` congestion level for such `RoadClass` will be overwritten to `.low` congestion level. + // In case if `trafficOverrideStreetsRoadClasses` was provided with `.motorway` `MapboxStreetsRoadClass` it is expected + // that any `.unknown` congestion level for such `MapboxStreetsRoadClass` will be overwritten to `.low` congestion level. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } @@ -267,37 +267,37 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { expectedCongestionLevels[1] = .unknown congestions = navigationMapView.addCongestion(to: route, legIndex: 0) - // In case if `trafficOverrideRoadClasses` is empty `.unknown` congestion level will not be + // In case if `trafficOverrideStreetsRoadClasses` is empty `.unknown` congestion level will not be // overwritten. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - // Make sure that at certain indexes `RoadClasses` is not present and assigned to `nil`. + // Make sure that at certain indexes `MapboxStreetsRoadClass` is not present and assigned to `nil`. route.legs.forEach { - let roadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) for index in 24...27 { - XCTAssertEqual(roadClasses[index], nil) + XCTAssertEqual(streetsRoadClasses[index], nil) } } } - func testRouteRoadClassesCountEqualToCongestionLevelsCount() { + func testRouteStreetsRoadClassesCountEqualToCongestionLevelsCount() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] - // Make sure that number of `RoadClasses` is equal to number of congestion levels. + // Make sure that number of `MapboxStreetsRoadClass` is equal to number of congestion levels. route.legs.forEach { - let roadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) let segmentCongestionLevels = $0.segmentCongestionLevels - XCTAssertEqual(roadClasses.count, segmentCongestionLevels?.count) + XCTAssertEqual(streetsRoadClasses.count, segmentCongestionLevels?.count) } } - func testRouteRoadClassesNotPresent() { + func testRouteStreetsRoadClassesNotPresent() { let route = loadRoute(from: "route-with-not-present-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -316,20 +316,20 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway, .secondary, .ferry] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) - // Since `RouteClasses` are not present in this route congestion levels should remain unchanged after - // modifying `trafficOverrideRoadClasses`, `roadClasses` should be empty as well. + // Since `SreetsRoadClass`es are not present in this route congestion levels should remain unchanged after + // modifying `trafficOverrideStreetsRoadClasses`, `streetsRoadClasses` should be empty as well. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } route.legs.forEach { - let roadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) - XCTAssertEqual(roadClasses.count, 0) + XCTAssertEqual(streetsRoadClasses.count, 0) } } - func testRouteRoadClassesDifferentAndSameCongestion() { + func testRouteStreetsRoadClassesDifferentAndSameCongestion() { let route = loadRoute(from: "route-with-same-congestion-different-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -341,7 +341,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - // It is expected that congestion will be overridden only for `RoadClasses` added in `trafficOverrideRoadClasses`. + // It is expected that congestion will be overridden only for `RoadClasses` added in `trafficOverrideStreetsRoadClasses`. // Other congestions should remain unchanged. expectedCongestionLevels = [ .low, From cbe462822d416223deac353cf08bba810e63da61 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 14:29:17 -0800 Subject: [PATCH 12/18] Minor improvement in changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24ddb2af56c..fa40e04892b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ ### Other changes * The user can now report feedback about an incorrect speed limit in the speed limit view. ([#2725](https://github.com/mapbox/mapbox-navigation-ios/pull/2725)) -* Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694) +* Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694)) * Added a new `NavigationMapView.trafficOverrideStreetsRoadClasses` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) ## v1.1.0 From af8671fd416cb47e129e88c29e57b3c10d2ca193 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 14:34:47 -0800 Subject: [PATCH 13/18] Move test fixtures to TestHelper. --- MapboxNavigation.xcodeproj/project.pbxproj | 50 ++++++++----------- .../route-with-missing-road-classes.json | 0 .../route-with-mixed-road-classes.json | 0 .../route-with-not-present-road-classes.json | 0 ...e-with-road-classes-single-congestion.json | 0 ...ame-congestion-different-road-classes.json | 0 6 files changed, 20 insertions(+), 30 deletions(-) rename {MapboxNavigationTests => TestHelper}/Fixtures/route-with-missing-road-classes.json (100%) rename {MapboxNavigationTests => TestHelper}/Fixtures/route-with-mixed-road-classes.json (100%) rename {MapboxNavigationTests => TestHelper}/Fixtures/route-with-not-present-road-classes.json (100%) rename {MapboxNavigationTests => TestHelper}/Fixtures/route-with-road-classes-single-congestion.json (100%) rename {MapboxNavigationTests => TestHelper}/Fixtures/route-with-same-congestion-different-road-classes.json (100%) diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index e2881e90f37..4b95f1b7bad 100755 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -263,17 +263,12 @@ 6441B16A1EFC64E50076499F /* WaypointConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6441B1691EFC64E50076499F /* WaypointConfirmationViewController.swift */; }; 64847A041F04629D003F3A69 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64847A031F04629D003F3A69 /* Feedback.swift */; }; 7C12F2D8225B7C320010A931 /* DCA-Arboretum-dummy-faster-route.json in Resources */ = {isa = PBXBuildFile; fileRef = 7C12F2D7225B7C310010A931 /* DCA-Arboretum-dummy-faster-route.json */; }; - 8A1BA8C4255B82FB004B499B /* route-with-missing-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */; }; - 8A485FDD255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */; }; - 8A485FDE255F2ADE00DCAC7F /* route-with-mixed-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */; }; - 8A485FEB255F2D5A00DCAC7F /* route-with-mixed-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */; }; - 8A485FF2255F2D5E00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */; }; - 8A485FFA255F333C00DCAC7F /* route-with-not-present-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */; }; - 8A486007255F339100DCAC7F /* route-with-not-present-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */; }; - 8A48600F255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */; }; - 8A48601C255F3B5D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */; }; 8AA849E924E722410008EE59 /* WaypointStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA849E824E722410008EE59 /* WaypointStyle.swift */; }; - 8AE53F29255B811800D1B668 /* route-with-missing-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */; }; + 8AC6191025881F8D00430AA8 /* route-with-mixed-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AC6190B25881F8D00430AA8 /* route-with-mixed-road-classes.json */; }; + 8AC6191125881F8D00430AA8 /* route-with-missing-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AC6190C25881F8D00430AA8 /* route-with-missing-road-classes.json */; }; + 8AC6191225881F8D00430AA8 /* route-with-not-present-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AC6190D25881F8D00430AA8 /* route-with-not-present-road-classes.json */; }; + 8AC6191325881F8D00430AA8 /* route-with-road-classes-single-congestion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AC6190E25881F8D00430AA8 /* route-with-road-classes-single-congestion.json */; }; + 8AC6191425881F8D00430AA8 /* route-with-same-congestion-different-road-classes.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AC6190F25881F8D00430AA8 /* route-with-same-congestion-different-road-classes.json */; }; 8D07C5A820B612310093D779 /* EmptyStyle.json in Resources */ = {isa = PBXBuildFile; fileRef = 8D07C5A720B612310093D779 /* EmptyStyle.json */; }; 8D1A5CD2212DDFCD0059BA4A /* DispatchTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D1A5CD1212DDFCD0059BA4A /* DispatchTimer.swift */; }; 8D24A2F62040960C0098CBF8 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24A2F52040960C0098CBF8 /* UIEdgeInsets.swift */; }; @@ -890,12 +885,12 @@ 6441B1691EFC64E50076499F /* WaypointConfirmationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WaypointConfirmationViewController.swift; path = Example/WaypointConfirmationViewController.swift; sourceTree = ""; }; 64847A031F04629D003F3A69 /* Feedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = ""; }; 7C12F2D7225B7C310010A931 /* DCA-Arboretum-dummy-faster-route.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "DCA-Arboretum-dummy-faster-route.json"; sourceTree = ""; }; - 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-road-classes-single-congestion.json"; sourceTree = ""; }; - 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-mixed-road-classes.json"; sourceTree = ""; }; - 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-not-present-road-classes.json"; sourceTree = ""; }; - 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-same-congestion-different-road-classes.json"; sourceTree = ""; }; 8AA849E824E722410008EE59 /* WaypointStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointStyle.swift; sourceTree = ""; }; - 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-missing-road-classes.json"; sourceTree = ""; }; + 8AC6190B25881F8D00430AA8 /* route-with-mixed-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-mixed-road-classes.json"; sourceTree = ""; }; + 8AC6190C25881F8D00430AA8 /* route-with-missing-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-missing-road-classes.json"; sourceTree = ""; }; + 8AC6190D25881F8D00430AA8 /* route-with-not-present-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-not-present-road-classes.json"; sourceTree = ""; }; + 8AC6190E25881F8D00430AA8 /* route-with-road-classes-single-congestion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-road-classes-single-congestion.json"; sourceTree = ""; }; + 8AC6190F25881F8D00430AA8 /* route-with-same-congestion-different-road-classes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-same-congestion-different-road-classes.json"; sourceTree = ""; }; 8B808F852487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Main.strings; sourceTree = ""; }; 8B808F862487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Navigation.strings; sourceTree = ""; }; 8B808F892487CFEC00EEE453 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; @@ -1254,6 +1249,11 @@ 165F91502204D50B0036CB9E /* Fixtures */ = { isa = PBXGroup; children = ( + 8AC6190C25881F8D00430AA8 /* route-with-missing-road-classes.json */, + 8AC6190B25881F8D00430AA8 /* route-with-mixed-road-classes.json */, + 8AC6190D25881F8D00430AA8 /* route-with-not-present-road-classes.json */, + 8AC6190E25881F8D00430AA8 /* route-with-road-classes-single-congestion.json */, + 8AC6190F25881F8D00430AA8 /* route-with-same-congestion-different-road-classes.json */, 439FFC222304BC23004C20AA /* route-with-tertiary.json */, 35CDA8862190F50C0072B675 /* DCA-Arboretum.json */, 35C8DC0E2191DE940053328C /* DCA-Arboretum.trace.json */, @@ -1446,11 +1446,6 @@ isa = PBXGroup; children = ( B48CEFAB25796F5A00696BB3 /* route-for-vanishing-route-line.json */, - 8A48600E255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json */, - 8A485FF9255F333C00DCAC7F /* route-with-not-present-road-classes.json */, - 8A485FDC255F2ADE00DCAC7F /* route-with-mixed-road-classes.json */, - 8A485FDB255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json */, - 8AE53F28255B811700D1B668 /* route-with-missing-road-classes.json */, 35EB9A6920A1A89500CB1225 /* turn_left.data */, 8D07C5A720B612310093D779 /* EmptyStyle.json */, 355DB5741EFA78070091BFB7 /* GGPark-to-BernalHeights.route */, @@ -2243,23 +2238,18 @@ buildActionMask = 2147483647; files = ( DA1755F92357B7A100B06C1D /* md5_crazy_strings.txt in Resources */, - 8A485FDD255F2ADE00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */, 351030111F54B72000E3B7E7 /* route-for-lane-testing.json in Resources */, 3527D2B91EC4619400C07FC9 /* Fixtures.xcassets in Resources */, 355DB5771EFA780E0091BFB7 /* UnionSquare-to-GGPark.route in Resources */, 3597ABD421553F6800C12785 /* sthlm-double-back.json in Resources */, 355DB5751EFA78070091BFB7 /* GGPark-to-BernalHeights.route in Resources */, - 8AE53F29255B811800D1B668 /* route-with-missing-road-classes.json in Resources */, 35EB9A6A20A1AB7C00CB1225 /* turn_left.data in Resources */, 3540514D1F73F3BB00ED572D /* route-with-straight-roundabout.json in Resources */, 8D07C5A820B612310093D779 /* EmptyStyle.json in Resources */, - 8A48600F255F396D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */, 35C11359215BADF900CC2929 /* sthlm-double-back-replay.json in Resources */, 8D86AE8921C31C640064A304 /* waypoint-after-turn.json in Resources */, AE5F8771209A082500F58FDB /* route-with-banner-instructions.json in Resources */, - 8A485FFA255F333C00DCAC7F /* route-with-not-present-road-classes.json in Resources */, DAA96D18215A961D00BEF703 /* route-doubling-back.json in Resources */, - 8A485FDE255F2ADE00DCAC7F /* route-with-mixed-road-classes.json in Resources */, 35DC9D8F1F4321CC001ECD64 /* route-with-lanes.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2284,17 +2274,16 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8A1BA8C4255B82FB004B499B /* route-with-missing-road-classes.json in Resources */, 3557506D21A827E800AEF9B6 /* li.tar in Resources */, + 8AC6191425881F8D00430AA8 /* route-with-same-congestion-different-road-classes.json in Resources */, 35CDA88D2190F5210072B675 /* DCA-Arboretum.json in Resources */, 35CDA8912190F6980072B675 /* route-for-lane-testing.json in Resources */, B48CEFC125796FD300696BB3 /* route-for-vanishing-route-line.json in Resources */, 3557506F21A8293E00AEF9B6 /* tiles in Resources */, 35CDA8902190F6980072B675 /* route-doubling-back.json in Resources */, 35CDA8952190F6980072B675 /* route-with-straight-roundabout.json in Resources */, - 8A48601C255F3B5D00DCAC7F /* route-with-same-congestion-different-road-classes.json in Resources */, 35F9BC2F2296FCD300364A10 /* 9-legged-route.json in Resources */, - 8A486007255F339100DCAC7F /* route-with-not-present-road-classes.json in Resources */, + 8AC6191225881F8D00430AA8 /* route-with-not-present-road-classes.json in Resources */, 35CDA8982190F6980072B675 /* turn_left.data in Resources */, 35CDA8962190F6980072B675 /* sthlm-double-back-replay.json in Resources */, 35CDA8922190F6980072B675 /* route-with-banner-instructions.json in Resources */, @@ -2302,16 +2291,17 @@ 355832AD2192F7E300141922 /* PipeFittersUnion-FourSeasonsBoston.trace.json in Resources */, 35CDA8942190F6980072B675 /* route-with-lanes.json in Resources */, 8D86AE8C21C31F210064A304 /* waypoint-after-turn.json in Resources */, + 8AC6191125881F8D00430AA8 /* route-with-missing-road-classes.json in Resources */, 35CDA8972190F6980072B675 /* sthlm-double-back.json in Resources */, 35CDA88F2190F6980072B675 /* EmptyStyle.json in Resources */, 35CDA8992190F6980072B675 /* UnionSquare-to-GGPark.route in Resources */, 357DEC44221AEE150019BAEC /* multileg-route.json in Resources */, 35C8DBF6219194380053328C /* routeWithTunnels_9thStreetDC.json in Resources */, - 8A485FF2255F2D5E00DCAC7F /* route-with-road-classes-single-congestion.json in Resources */, - 8A485FEB255F2D5A00DCAC7F /* route-with-mixed-road-classes.json in Resources */, + 8AC6191025881F8D00430AA8 /* route-with-mixed-road-classes.json in Resources */, 35C8DBF52191940C0053328C /* straight-line.json in Resources */, 7C12F2D8225B7C320010A931 /* DCA-Arboretum-dummy-faster-route.json in Resources */, 4341758223060A17004264A9 /* route-with-tertiary.json in Resources */, + 8AC6191325881F8D00430AA8 /* route-with-road-classes-single-congestion.json in Resources */, 35C8DC0F2191DE940053328C /* DCA-Arboretum.trace.json in Resources */, 35CDA8932190F6980072B675 /* route-with-instructions.json in Resources */, 35C8DBF8219198320053328C /* route.json in Resources */, diff --git a/MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json b/TestHelper/Fixtures/route-with-missing-road-classes.json similarity index 100% rename from MapboxNavigationTests/Fixtures/route-with-missing-road-classes.json rename to TestHelper/Fixtures/route-with-missing-road-classes.json diff --git a/MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json b/TestHelper/Fixtures/route-with-mixed-road-classes.json similarity index 100% rename from MapboxNavigationTests/Fixtures/route-with-mixed-road-classes.json rename to TestHelper/Fixtures/route-with-mixed-road-classes.json diff --git a/MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json b/TestHelper/Fixtures/route-with-not-present-road-classes.json similarity index 100% rename from MapboxNavigationTests/Fixtures/route-with-not-present-road-classes.json rename to TestHelper/Fixtures/route-with-not-present-road-classes.json diff --git a/MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json b/TestHelper/Fixtures/route-with-road-classes-single-congestion.json similarity index 100% rename from MapboxNavigationTests/Fixtures/route-with-road-classes-single-congestion.json rename to TestHelper/Fixtures/route-with-road-classes-single-congestion.json diff --git a/MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json b/TestHelper/Fixtures/route-with-same-congestion-different-road-classes.json similarity index 100% rename from MapboxNavigationTests/Fixtures/route-with-same-congestion-different-road-classes.json rename to TestHelper/Fixtures/route-with-same-congestion-different-road-classes.json From 2883ecb1470818eb9662a5270f0a4a945deaa5fc Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 15:03:30 -0800 Subject: [PATCH 14/18] Use set of `MapboxStreetsRoadClass` instead of array. --- MapboxNavigation/NavigationMapView.swift | 6 +++--- MapboxNavigationTests/NavigationMapViewTests.swift | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index fc55b7c9cde..78dcaf3144e 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -49,10 +49,10 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** A collection of street road classes for which a congestion level substitution should occur. - For any road class included in the `trafficOverrideStreetsRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` + For any street road class included in the `trafficOverrideStreetsRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. */ - public var trafficOverrideStreetsRoadClasses: [MapboxStreetsRoadClass]? = nil + public var trafficOverrideStreetsRoadClasses: Set? = nil /** The object that acts as the navigation delegate of the map view. @@ -1131,7 +1131,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { func combine(_ coordinates: [CLLocationCoordinate2D], with congestions: [CongestionLevel], streetsRoadClasses: [MapboxStreetsRoadClass?]? = nil, - trafficOverrideStreetsRoadClasses: [MapboxStreetsRoadClass]? = nil) -> [CongestionSegment] { + trafficOverrideStreetsRoadClasses: Set? = nil) -> [CongestionSegment] { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index c1860b89567..6918316080a 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -373,6 +373,13 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } } + + func testTrafficOverrideStreetsRoadClassesRemovesDuplicates() { + let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) + navigationMapView.trafficOverrideStreetsRoadClasses = [.aerialway, .construction, .construction, .golf] + + XCTAssertEqual(navigationMapView.trafficOverrideStreetsRoadClasses?.count, 3) + } } class PersistentAnnotation: MGLPointAnnotation { } From 4aa04a567f722f2f868e19cc61a19e5b06830d34 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Mon, 14 Dec 2020 15:14:55 -0800 Subject: [PATCH 15/18] Simplify condition which overrides congestion level. --- MapboxNavigation/NavigationMapView.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 78dcaf3144e..1464aadb981 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -1144,11 +1144,9 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let trafficOverrideStreetsRoadClasses = trafficOverrideStreetsRoadClasses, streetsRoadClasses.indices.contains(index), let streetsRoadClass = streetsRoadClasses[index], - congestionLevel == .unknown { - - if trafficOverrideStreetsRoadClasses.contains(streetsRoadClass) { - overriddenCongestionLevel = .low - } + congestionLevel == .unknown, + trafficOverrideStreetsRoadClasses.contains(streetsRoadClass) { + overriddenCongestionLevel = .low } if segments.last?.1 == overriddenCongestionLevel { From cb585ffed02046a699b8b1f64933312e8e46d28e Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Wed, 16 Dec 2020 12:14:30 -0800 Subject: [PATCH 16/18] Rename `trafficOverrideStreetsRoadClasses` to `roadClassesWithOverriddenCongestionLevels`. --- CHANGELOG.md | 2 +- Example/ViewController.swift | 11 ------- MapboxNavigation/NavigationMapView.swift | 16 +++++----- .../NavigationMapViewTests.swift | 32 +++++++++---------- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa40e04892b..f982ad3cbef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ * The user can now report feedback about an incorrect speed limit in the speed limit view. ([#2725](https://github.com/mapbox/mapbox-navigation-ios/pull/2725)) * Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694)) -* Added a new `NavigationMapView.trafficOverrideStreetsRoadClasses` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) +* Added a new `NavigationMapView.roadClassesWithOverriddenCongestionLevels` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `Intersection.outletMapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) ## v1.1.0 diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 62a0be9fd72..6aa6ed0f894 100755 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -275,10 +275,6 @@ class ViewController: UIViewController { // Show second level of detail for feedback items. navigationViewController.detailedFeedbackEnabled = true - // Override traffic congestion level from `.unknown` to `.low` - // for `MapboxStreetsRoadClass` passed in `trafficOverrideStreetsRoadClasses`. - navigationViewController.mapView?.trafficOverrideStreetsRoadClasses = [.motorway] - presentAndRemoveMapview(navigationViewController, completion: beginCarPlayNavigation) } @@ -411,13 +407,6 @@ class ViewController: UIViewController { mapView.showsUserLocation = true mapView.userTrackingMode = .follow mapView.showsUserHeadingIndicator = true - - // Use map style which allows to show road traffic. - mapView.styleURL = MGLStyle.navigationDayStyleURL - - // Override traffic congestion level from `.unknown` to `.low` - // for `MapboxStreetsRoadClass` passed in `trafficOverrideStreetsRoadClasses`. - mapView.trafficOverrideStreetsRoadClasses = [.motorway] } func uninstall(_ mapView: NavigationMapView) { diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 1464aadb981..79ae4131b77 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -49,10 +49,10 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** A collection of street road classes for which a congestion level substitution should occur. - For any street road class included in the `trafficOverrideStreetsRoadClasses`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` + For any street road class included in the `roadClassesWithOverriddenCongestionLevels`, all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `MapboxDirections.MapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. */ - public var trafficOverrideStreetsRoadClasses: Set? = nil + public var roadClassesWithOverriddenCongestionLevels: Set? = nil /** The object that acts as the navigation delegate of the map view. @@ -1087,7 +1087,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let mergedCongestionSegments = combine(legCoordinates, with: legCongestion, streetsRoadClasses: streetsRoadClasses(leg), - trafficOverrideStreetsRoadClasses: trafficOverrideStreetsRoadClasses) + roadClassesWithOverriddenCongestionLevels: roadClassesWithOverriddenCongestionLevels) lines = mergedCongestionSegments.map { (congestionSegment: CongestionSegment) -> MGLPolylineFeature in let polyline = MGLPolylineFeature(coordinates: congestionSegment.0, count: UInt(congestionSegment.0.count)) @@ -1119,19 +1119,19 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This method coalesces consecutive line segments that have the same congestion level. - For each item in the`CongestionSegment` collection a `CongestionLevel` substitution will take place that has a streets road class contained in the `trafficOverrideStreetsRoadClasses` collection. + For each item in the`CongestionSegment` collection a `CongestionLevel` substitution will take place that has a streets road class contained in the `roadClassesWithOverriddenCongestionLevels` collection. For each of these items the `CongestionLevel` for `.unknown` traffic congestion will be replaced with the `.low` traffic congestion. - parameter coordinates: The coordinates of a leg. - parameter congestions: The congestion levels along a leg. There should be one fewer congestion levels than coordinates. - parameter streetsRoadClasses: A collection of streets road classes for each geometry index in `Intersection`. There should be the same amount of `streetsRoadClasses` and `congestions`. - - parameter trafficOverrideStreetsRoadClasses: Streets road classes for which a `CongestionLevel` substitution should occur. + - parameter roadClassesWithOverriddenCongestionLevels: Streets road classes for which a `CongestionLevel` substitution should occur. - returns: A list of `CongestionSegment` tuples with coordinate and congestion level. */ func combine(_ coordinates: [CLLocationCoordinate2D], with congestions: [CongestionLevel], streetsRoadClasses: [MapboxStreetsRoadClass?]? = nil, - trafficOverrideStreetsRoadClasses: Set? = nil) -> [CongestionSegment] { + roadClassesWithOverriddenCongestionLevels: Set? = nil) -> [CongestionSegment] { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) @@ -1141,11 +1141,11 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { var overriddenCongestionLevel = congestionLevel if let streetsRoadClasses = streetsRoadClasses, - let trafficOverrideStreetsRoadClasses = trafficOverrideStreetsRoadClasses, + let roadClassesWithOverriddenCongestionLevels = roadClassesWithOverriddenCongestionLevels, streetsRoadClasses.indices.contains(index), let streetsRoadClass = streetsRoadClasses[index], congestionLevel == .unknown, - trafficOverrideStreetsRoadClasses.contains(streetsRoadClass) { + roadClassesWithOverriddenCongestionLevels.contains(streetsRoadClass) { overriddenCongestionLevel = .low } diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index 6918316080a..b17e4101cf1 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -199,7 +199,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevel) } - navigationMapView.trafficOverrideStreetsRoadClasses = [.golf] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.golf] expectedCongestionLevel = .low congestions = navigationMapView.addCongestion(to: route, legIndex: 0) @@ -228,7 +228,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.motorway] expectedCongestionLevels = [ .low, .severe, @@ -246,7 +246,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { func testOverriddenStreetsRouteClassMissing() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) - navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.motorway] var congestions = navigationMapView.addCongestion(to: route, legIndex: 0) XCTAssertEqual(congestions?.count, 3) @@ -257,17 +257,17 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { .severe ] - // In case if `trafficOverrideStreetsRoadClasses` was provided with `.motorway` `MapboxStreetsRoadClass` it is expected + // In case if `roadClassesWithOverriddenCongestionLevels` was provided with `.motorway` `MapboxStreetsRoadClass` it is expected // that any `.unknown` congestion level for such `MapboxStreetsRoadClass` will be overwritten to `.low` congestion level. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideStreetsRoadClasses = [] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [] expectedCongestionLevels[1] = .unknown congestions = navigationMapView.addCongestion(to: route, legIndex: 0) - // In case if `trafficOverrideStreetsRoadClasses` is empty `.unknown` congestion level will not be + // In case if `roadClassesWithOverriddenCongestionLevels` is empty `.unknown` congestion level will not be // overwritten. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) @@ -286,7 +286,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { func testRouteStreetsRoadClassesCountEqualToCongestionLevelsCount() { let route = loadRoute(from: "route-with-missing-road-classes") let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) - navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.motorway] // Make sure that number of `MapboxStreetsRoadClass` is equal to number of congestion levels. route.legs.forEach { @@ -313,11 +313,11 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideStreetsRoadClasses = [.motorway, .secondary, .ferry] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.motorway, .secondary, .ferry] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) // Since `SreetsRoadClass`es are not present in this route congestion levels should remain unchanged after - // modifying `trafficOverrideStreetsRoadClasses`, `streetsRoadClasses` should be empty as well. + // modifying `roadClassesWithOverriddenCongestionLevels`, `streetsRoadClasses` should be empty as well. congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } @@ -341,20 +341,20 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - // It is expected that congestion will be overridden only for `RoadClasses` added in `trafficOverrideStreetsRoadClasses`. + // It is expected that congestion will be overridden only for `RoadClasses` added in `roadClassesWithOverriddenCongestionLevels`. // Other congestions should remain unchanged. expectedCongestionLevels = [ .low, .unknown ] - navigationMapView.trafficOverrideStreetsRoadClasses = [.street] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.street] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { XCTAssertEqual(congestionLevel($0.element), expectedCongestionLevels[$0.offset]) } - navigationMapView.trafficOverrideStreetsRoadClasses = [.street, .ferry] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.street, .ferry] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { @@ -366,7 +366,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { expectedCongestionLevels = [ .low ] - navigationMapView.trafficOverrideStreetsRoadClasses = [.street, .ferry, .motorway] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.street, .ferry, .motorway] congestions = navigationMapView.addCongestion(to: route, legIndex: 0) congestions?.enumerated().forEach { @@ -374,11 +374,11 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } } - func testTrafficOverrideStreetsRoadClassesRemovesDuplicates() { + func testRoadClassesWithOverriddenCongestionLevelsRemovesDuplicates() { let navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus), styleURL: Fixture.blankStyle) - navigationMapView.trafficOverrideStreetsRoadClasses = [.aerialway, .construction, .construction, .golf] + navigationMapView.roadClassesWithOverriddenCongestionLevels = [.aerialway, .construction, .construction, .golf] - XCTAssertEqual(navigationMapView.trafficOverrideStreetsRoadClasses?.count, 3) + XCTAssertEqual(navigationMapView.roadClassesWithOverriddenCongestionLevels?.count, 3) } } From ce2df5b2bbbea22e317b85c39db0447108b16cde Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Wed, 16 Dec 2020 12:31:18 -0800 Subject: [PATCH 17/18] Move `streetsRoadClasses` to be part of `RouteLeg`. --- MapboxCoreNavigation/RouteLeg.swift | 21 +++++++++++++++ MapboxNavigation/NavigationMapView.swift | 26 +------------------ .../NavigationMapViewTests.swift | 6 ++--- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/MapboxCoreNavigation/RouteLeg.swift b/MapboxCoreNavigation/RouteLeg.swift index 2f3dcca0bd0..c0d3955c4bb 100644 --- a/MapboxCoreNavigation/RouteLeg.swift +++ b/MapboxCoreNavigation/RouteLeg.swift @@ -7,4 +7,25 @@ extension RouteLeg { result.coordinates += (step.shape?.coordinates ?? []).dropFirst() } } + + /** + Returns an array of `MapboxStreetsRoadClass` objects for specific leg. `MapboxStreetsRoadClass` will be set to `nil` if it's not present in `Intersection`. + */ + public var streetsRoadClasses: [MapboxStreetsRoadClass?] { + // Pick only valid segment indices for specific `Intersection` in `RouteStep`. + // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. + let segmentIndices = steps.compactMap({ $0.segmentIndicesByIntersection?.compactMap({ $0 }) }).reduce([], +) + + // Pick `MapboxStreetsRoadClass` in specific `Intersection` of `RouteStep`. + // It is expected that number of `segmentIndices` will be equal to number of `streetsRoadClassesInLeg`. + // Array of `MapboxStreetsRoadClass` can look like this: [Optional(motorway), ... , Optional(motorway), nil] + let streetsRoadClassesInLeg = steps.compactMap({ $0.intersections?.map({ $0.outletMapboxStreetsRoadClass }) }).reduce([], +) + + // Map each `MapboxStreetsRoadClass` to the amount of two adjacent `segmentIndices`. + // At the end amount of `MapboxStreetsRoadClass` should be equal to the last segment index. + let streetsRoadClasses = segmentIndices.enumerated().map({ segmentIndices.indices.contains($0.offset + 1) && streetsRoadClassesInLeg.indices.contains($0.offset) ? + Array(repeating: streetsRoadClassesInLeg[$0.offset], count: segmentIndices[$0.offset + 1] - segmentIndices[$0.offset]) : [] }).reduce([], +) + + return streetsRoadClasses + } } diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 79ae4131b77..ee8c4bbdfe9 100755 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -1043,30 +1043,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { return MGLShapeCollectionFeature(shapes: [mainRoute]) } - /** - Returns an array of `MapboxStreetsRoadClass` objects for specific leg. - - - parameter leg: Leg of the route. - - returns: An array of `MapboxStreetsRoadClass` for specific `RouteLeg`. `MapboxStreetsRoadClass` will be set to `nil` if it's not present in `Intersection`. - */ - func streetsRoadClasses(_ leg: RouteLeg) -> [MapboxStreetsRoadClass?] { - // Pick only valid segment indices for specific `Intersection` in `RouteStep`. - // Array of segment indexes can look like this: [0, 3, 24, 28, 48, 50, 51, 53]. - let segmentIndices = leg.steps.compactMap({ $0.segmentIndicesByIntersection?.compactMap({ $0 }) }).reduce([], +) - - // Pick `MapboxStreetsRoadClass` in specific `Intersection` of `RouteStep`. - // It is expected that number of `segmentIndices` will be equal to number of `streetsRoadClassesInLeg`. - // Array of `MapboxStreetsRoadClass` can look like this: [Optional(motorway), ... , Optional(motorway), nil] - let streetsRoadClassesInLeg = leg.steps.compactMap({ $0.intersections?.map({ $0.outletMapboxStreetsRoadClass }) }).reduce([], +) - - // Map each `MapboxStreetsRoadClass` to the amount of two adjacent `segmentIndices`. - // At the end amount of `MapboxStreetsRoadClass` should be equal to the last segment index. - let streetsRoadClasses = segmentIndices.enumerated().map({ segmentIndices.indices.contains($0.offset + 1) && streetsRoadClassesInLeg.indices.contains($0.offset) ? - Array(repeating: streetsRoadClassesInLeg[$0.offset], count: segmentIndices[$0.offset + 1] - segmentIndices[$0.offset]) : [] }).reduce([], +) - - return streetsRoadClasses - } - func addCongestion(to route: Route, legIndex: Int?) -> [MGLPolylineFeature]? { guard let coordinates = route.shape?.coordinates else { return nil } @@ -1086,7 +1062,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let mergedCongestionSegments = combine(legCoordinates, with: legCongestion, - streetsRoadClasses: streetsRoadClasses(leg), + streetsRoadClasses: leg.streetsRoadClasses, roadClassesWithOverriddenCongestionLevels: roadClassesWithOverriddenCongestionLevels) lines = mergedCongestionSegments.map { (congestionSegment: CongestionSegment) -> MGLPolylineFeature in diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index b17e4101cf1..e5b4c539a5a 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -275,7 +275,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { // Make sure that at certain indexes `MapboxStreetsRoadClass` is not present and assigned to `nil`. route.legs.forEach { - let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = $0.streetsRoadClasses for index in 24...27 { XCTAssertEqual(streetsRoadClasses[index], nil) @@ -290,7 +290,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { // Make sure that number of `MapboxStreetsRoadClass` is equal to number of congestion levels. route.legs.forEach { - let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = $0.streetsRoadClasses let segmentCongestionLevels = $0.segmentCongestionLevels XCTAssertEqual(streetsRoadClasses.count, segmentCongestionLevels?.count) @@ -323,7 +323,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } route.legs.forEach { - let streetsRoadClasses = navigationMapView.streetsRoadClasses($0) + let streetsRoadClasses = $0.streetsRoadClasses XCTAssertEqual(streetsRoadClasses.count, 0) } From bf436db34f320a275e1e4e522e689cd3c0dfced7 Mon Sep 17 00:00:00 2001 From: Maxim Makhun Date: Thu, 17 Dec 2020 10:32:33 -0800 Subject: [PATCH 18/18] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f982ad3cbef..084b4bdf562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * The user can now report feedback about an incorrect speed limit in the speed limit view. ([#2725](https://github.com/mapbox/mapbox-navigation-ios/pull/2725)) * Added the `RouteProgress.upcomingRouteAlerts` property to track upcoming points along the route experiencing conditions that may require the user’s attention. The `UpcomingRouteAlertInfo.alert` property contains one of the following types with more details about the alert: `Incident`, `TunnelInfo`, `BorderCrossingInfo`, `TollCollection`, and `RestStop`. ([#2694](https://github.com/mapbox/mapbox-navigation-ios/pull/2694)) * Added a new `NavigationMapView.roadClassesWithOverriddenCongestionLevels` property. For any road class in it all route segments with an `CongestionLevel.unknown` traffic congestion level and a matching `Intersection.outletMapboxStreetsRoadClass` will be replaced with the `CongestionLevel.low` congestion level. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) +* Added a new `RouteLeg.streetsRoadClasses` property, which allows to get a collection of `MapboxStreetsRoadClass` objects for specific `RouteLeg`. ([#2741](https://github.com/mapbox/mapbox-navigation-ios/pull/2741)) ## v1.1.0