From d6fccb7a634e42b781f013fb4f358c7f39d7fe73 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Fri, 5 Jun 2020 17:17:57 -0500 Subject: [PATCH 1/4] Add guidance view marshalling support. Co-authored-by: Avi Cieplinski --- .../VisualInstructionBanner.swift | 13 ++++++- .../VisualInstructionComponent.swift | 35 +++++++++++++++++++ .../VisualInstructionTests.swift | 19 +++++++++- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Sources/MapboxDirections/VisualInstructionBanner.swift b/Sources/MapboxDirections/VisualInstructionBanner.swift index 9eea0abdf..afd5a8556 100644 --- a/Sources/MapboxDirections/VisualInstructionBanner.swift +++ b/Sources/MapboxDirections/VisualInstructionBanner.swift @@ -14,6 +14,7 @@ open class VisualInstructionBanner: Codable { case primaryInstruction = "primary" case secondaryInstruction = "secondary" case tertiaryInstruction = "sub" + case quaternaryInstruction = "view" case drivingSide } @@ -22,11 +23,12 @@ open class VisualInstructionBanner: Codable { /** Initializes a visual instruction banner with the given instructions. */ - public init(distanceAlongStep: CLLocationDistance, primary: VisualInstruction, secondary: VisualInstruction?, tertiary: VisualInstruction?, drivingSide: DrivingSide) { + public init(distanceAlongStep: CLLocationDistance, primary: VisualInstruction, secondary: VisualInstruction?, tertiary: VisualInstruction?, quaternary: VisualInstruction?, drivingSide: DrivingSide) { self.distanceAlongStep = distanceAlongStep primaryInstruction = primary secondaryInstruction = secondary tertiaryInstruction = tertiary + quaternaryInstruction = quaternary self.drivingSide = drivingSide } @@ -36,6 +38,7 @@ open class VisualInstructionBanner: Codable { try container.encode(primaryInstruction, forKey: .primaryInstruction) try container.encodeIfPresent(secondaryInstruction, forKey: .secondaryInstruction) try container.encodeIfPresent(tertiaryInstruction, forKey: .tertiaryInstruction) + try container.encodeIfPresent(quaternaryInstruction, forKey: .quaternaryInstruction) try container.encode(drivingSide, forKey: .drivingSide) } @@ -45,6 +48,7 @@ open class VisualInstructionBanner: Codable { primaryInstruction = try container.decode(VisualInstruction.self, forKey: .primaryInstruction) secondaryInstruction = try container.decodeIfPresent(VisualInstruction.self, forKey: .secondaryInstruction) tertiaryInstruction = try container.decodeIfPresent(VisualInstruction.self, forKey: .tertiaryInstruction) + quaternaryInstruction = try container.decodeIfPresent(VisualInstruction.self, forKey: .quaternaryInstruction) if let directlyEncoded = try container.decodeIfPresent(DrivingSide.self, forKey: .drivingSide) { drivingSide = directlyEncoded } else { @@ -78,6 +82,12 @@ open class VisualInstructionBanner: Codable { */ public let tertiaryInstruction: VisualInstruction? + /** + A visual instruction that is presented to provide information about the incoming junction. + This instruction displays a zoomed image of incoming junction. + */ + public let quaternaryInstruction: VisualInstruction? + // MARK: Respecting Regional Driving Rules /** @@ -92,6 +102,7 @@ extension VisualInstructionBanner: Equatable { lhs.primaryInstruction == rhs.primaryInstruction && lhs.secondaryInstruction == rhs.secondaryInstruction && lhs.tertiaryInstruction == rhs.tertiaryInstruction && + lhs.quaternaryInstruction == rhs .quaternaryInstruction && lhs.drivingSide == rhs.drivingSide } } diff --git a/Sources/MapboxDirections/VisualInstructionComponent.swift b/Sources/MapboxDirections/VisualInstructionComponent.swift index de01b268a..bf0fc9e4e 100644 --- a/Sources/MapboxDirections/VisualInstructionComponent.swift +++ b/Sources/MapboxDirections/VisualInstructionComponent.swift @@ -34,6 +34,8 @@ public extension VisualInstruction { */ case image(image: ImageRepresentation, alternativeText: TextRepresentation) + case guidanceView(image: GuidanceViewImageRepresentation, alternativeText: TextRepresentation) + /** The compoment contains the localized word for “Exit”. @@ -149,6 +151,21 @@ public extension VisualInstruction.Component { } } +public struct GuidanceViewImageRepresentation: Equatable { + /** + Initializes an image representation bearing the image at the given URL. + */ + public init(imageURL: URL?) { + self.imageURL = imageURL + } + + /** + Returns a remote URL to the image file that represents the component. + */ + public let imageURL: URL? + +} + extension VisualInstruction.Component: Codable { private enum CodingKeys: String, CodingKey { case kind = "type" @@ -156,6 +173,7 @@ extension VisualInstruction.Component: Codable { case abbreviatedText = "abbr" case abbreviatedTextPriority = "abbr_priority" case imageBaseURL + case imageURL = "url" case directions case isActive = "active" } @@ -164,6 +182,7 @@ extension VisualInstruction.Component: Codable { case delimiter case text case image = "icon" + case guidanceView = "guidance-view" case exit case exitCode = "exit-number" case lane @@ -203,6 +222,13 @@ extension VisualInstruction.Component: Codable { self = .exitCode(text: textRepresentation) case .lane: preconditionFailure("Lane component should have been initialized before decoding text") + case .guidanceView: + var imageURL: URL? + if let imageURLString = try container.decodeIfPresent(String.self, forKey: .imageURL) { + imageURL = URL(string: imageURLString) + } + let guidanceViewImageRepresentation = GuidanceViewImageRepresentation(imageURL: imageURL) + self = .guidanceView(image: guidanceViewImageRepresentation, alternativeText: textRepresentation) } } @@ -232,6 +258,10 @@ extension VisualInstruction.Component: Codable { textRepresentation = .init(text: "", abbreviation: nil, abbreviationPriority: nil) try container.encode(indications, forKey: .directions) try container.encode(isUsable, forKey: .isActive) + case .guidanceView(let image, let alternativeText): + try container.encode(Kind.guidanceView, forKey: .kind) + textRepresentation = alternativeText + try container.encodeIfPresent(image.imageURL?.absoluteString, forKey: .imageURL) } if let textRepresentation = textRepresentation { @@ -254,6 +284,10 @@ extension VisualInstruction.Component: Equatable { let .image(rhsURL, rhsAlternativeText)): return lhsURL == rhsURL && lhsAlternativeText == rhsAlternativeText + case (let .guidanceView(lhsURL, lhsAlternativeText), + let .guidanceView(rhsURL, rhsAlternativeText)): + return lhsURL == rhsURL + && lhsAlternativeText == rhsAlternativeText case (let .lane(lhsIndications, lhsIsUsable), let .lane(rhsIndications, rhsIsUsable)): return lhsIndications == rhsIndications @@ -263,6 +297,7 @@ extension VisualInstruction.Component: Equatable { (.image, _), (.exit, _), (.exitCode, _), + (.guidanceView, _), (.lane, _): return false } diff --git a/Tests/MapboxDirectionsTests/VisualInstructionTests.swift b/Tests/MapboxDirectionsTests/VisualInstructionTests.swift index 58d1e000b..b4d9951f1 100644 --- a/Tests/MapboxDirectionsTests/VisualInstructionTests.swift +++ b/Tests/MapboxDirectionsTests/VisualInstructionTests.swift @@ -24,6 +24,18 @@ class VisualInstructionsTests: XCTestCase { "modifier": "right", ], "secondary": nil, + "view": [ + "text": "CA01610_1_E", + "components": [ + [ + "text": "CA01610_1_E", + "type": "guidance-view", + "url": "https://www.mapbox.com/navigation" + ], + ], + "type": "fork", + "modifier": "right" + ], ] let bannerData = try! JSONSerialization.data(withJSONObject: bannerJSON, options: []) var banner: VisualInstructionBanner? @@ -36,12 +48,17 @@ class VisualInstructionsTests: XCTestCase { XCTAssertEqual(banner.primaryInstruction.maneuverType, .turn) XCTAssertEqual(banner.primaryInstruction.maneuverDirection, .right) XCTAssertNil(banner.secondaryInstruction) + XCTAssertNotNil(banner.quaternaryInstruction) XCTAssertEqual(banner.drivingSide, .default) } let component = VisualInstruction.Component.text(text: .init(text: "Weinstock Strasse", abbreviation: nil, abbreviationPriority: nil)) let primaryInstruction = VisualInstruction(text: "Weinstock Strasse", maneuverType: .turn, maneuverDirection: .right, components: [component]) - banner = VisualInstructionBanner(distanceAlongStep: 393.3, primary: primaryInstruction, secondary: nil, tertiary: nil, drivingSide: .right) + + let guideViewComponent = VisualInstruction.Component.guidanceView(image: GuidanceViewImageRepresentation(imageURL: URL(string: "https://www.mapbox.com/navigation")), alternativeText: VisualInstruction.Component.TextRepresentation(text: "CA01610_1_E", abbreviation: nil, abbreviationPriority: nil)) + let quaternaryInstruction = VisualInstruction(text: "CA01610_1_E", maneuverType: .reachFork, maneuverDirection: .right, components: [guideViewComponent]) + + banner = VisualInstructionBanner(distanceAlongStep: 393.3, primary: primaryInstruction, secondary: nil, tertiary: nil, quaternary: quaternaryInstruction, drivingSide: .right) let encoder = JSONEncoder() var encodedData: Data? XCTAssertNoThrow(encodedData = try encoder.encode(banner)) From 095688d95ee30e649bf840d18e1e63d13fe7d11e Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 10 Jun 2020 13:25:44 -0500 Subject: [PATCH 2/4] Add guidance view documentation. --- Sources/MapboxDirections/VisualInstructionComponent.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/MapboxDirections/VisualInstructionComponent.swift b/Sources/MapboxDirections/VisualInstructionComponent.swift index bf0fc9e4e..954db8f1e 100644 --- a/Sources/MapboxDirections/VisualInstructionComponent.swift +++ b/Sources/MapboxDirections/VisualInstructionComponent.swift @@ -34,10 +34,13 @@ public extension VisualInstruction { */ case image(image: ImageRepresentation, alternativeText: TextRepresentation) + /** + The component is an image of a zoomed junction, with a fallback text representation. + */ case guidanceView(image: GuidanceViewImageRepresentation, alternativeText: TextRepresentation) /** - The compoment contains the localized word for “Exit”. + The component contains the localized word for “Exit”. This component may appear before or after an `.exitCode` component, depending on the language. You can hide this component if the adjacent `.exitCode` component has an obvious exit-number appearance, for example with an accompanying [motorway exit icon](https://commons.wikimedia.org/wiki/File:Sinnbild_Autobahnausfahrt.svg). */ From d2d66de82875102c9d5789fb48928ba52606c913 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 10 Jun 2020 13:25:51 -0500 Subject: [PATCH 3/4] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4afb8c5fb..7dbb62144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Removed the `Lane` class in favor of storing an array of `LaneIndication`s directly in the `Intersection.approachLanes` property. ([#382](https://github.com/mapbox/mapbox-directions-swift/pull/382)) * Removed the `ComponentRepresentable` protocol, `VisualInstructionComponent` class, and `LaneIndicationComponent` class in favor of a `VisualInstruction.Component` enumeration that contains a `VisualInstruction.Component.TextRepresentation` and/or `VisualInstruction.Component.ImageRepresentation`, depending on the type of component. ([#382](https://github.com/mapbox/mapbox-directions-swift/pull/382)) * Added the `VisualInstruction.Component.ImageRepresentation.imageURL(scale:format:)` method for fetching images with scales other than the current screen’s native scale or formats other than PNG. ([#382](https://github.com/mapbox/mapbox-directions-swift/pull/382)) +* Added the `VisualInstructionBanner.quaternaryInstruction` property and `VisualInstruction.Component.guidanceView(image:alternativeText:)` enumeration case to represent a detailed image of an upcoming junction. ([#425](https://github.com/mapbox/mapbox-directions-swift/pull/425)) ### Other changes From 54d58743031d30475781093de26cd6e927df8c58 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 10 Jun 2020 16:31:42 -0500 Subject: [PATCH 4/4] Update class doc --- Sources/MapboxDirections/VisualInstructionComponent.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/MapboxDirections/VisualInstructionComponent.swift b/Sources/MapboxDirections/VisualInstructionComponent.swift index 954db8f1e..1079c6ac7 100644 --- a/Sources/MapboxDirections/VisualInstructionComponent.swift +++ b/Sources/MapboxDirections/VisualInstructionComponent.swift @@ -154,6 +154,7 @@ public extension VisualInstruction.Component { } } +/// A guidance view image representation of a visual instruction component. public struct GuidanceViewImageRepresentation: Equatable { /** Initializes an image representation bearing the image at the given URL.