From 2d1161b0165c131c27e0e6b94aea7adaf847086f Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Wed, 13 Nov 2024 23:03:24 +0400 Subject: [PATCH] Support incidents for OSRM responses This parses incidents on the OSRM response route leg, and splits the incidents across the corresponding route step. Since an incident may span multiple steps, this will duplicate an incident and adjust the indices accordingly whenever that happens. Clients can de-duplicate these if necessary based on the incident identifier. --- .../ferrostar/core/FerrostarCoreTest.kt | 3 +- .../Mock/MockNavigationState.swift | 3 +- .../FerrostarSwiftUI/TestFixtureFactory.swift | 3 +- apple/Sources/UniFFI/ferrostar.swift | 796 +++++++++++++++++- .../FerrostarCoreTests.swift | 3 +- .../test200MockGETRouteResponse.1.txt | 1 + .../test200MockPOSTRouteResponse.1.txt | 1 + .../testCustomRouteProvider.1.txt | 1 + .../testValhallaCostingOptionsJSON.1.txt | 2 + .../testValhallaRouteParsing.1.txt | 2 + common/ferrostar/src/models.rs | 103 +++ .../src/navigation_controller/test_helpers.rs | 1 + .../src/routing_adapters/osrm/mod.rs | 48 +- .../src/routing_adapters/osrm/models.rs | 121 ++- ...ers__osrm__tests__parse_valhalla_osrm.snap | 23 + ...ts__parse_valhalla_osrm_with_via_ways.snap | 2 + .../src/routing_adapters/osrm/utilities.rs | 42 +- 17 files changed, 1145 insertions(+), 10 deletions(-) diff --git a/android/core/src/androidTest/java/com/stadiamaps/ferrostar/core/FerrostarCoreTest.kt b/android/core/src/androidTest/java/com/stadiamaps/ferrostar/core/FerrostarCoreTest.kt index 4130105c..8289d525 100644 --- a/android/core/src/androidTest/java/com/stadiamaps/ferrostar/core/FerrostarCoreTest.kt +++ b/android/core/src/androidTest/java/com/stadiamaps/ferrostar/core/FerrostarCoreTest.kt @@ -122,7 +122,8 @@ class FerrostarCoreTest { triggerDistanceBeforeManeuver = 42.0)), spokenInstructions = listOf(), duration = 0.0, - annotations = null))) + annotations = null, + incidents = listOf()))) @Test fun test401UnauthorizedRouteResponse() = runTest { diff --git a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift index 34802391..2c625d1f 100644 --- a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift +++ b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift @@ -68,7 +68,8 @@ public extension NavigationState { ), ], spokenInstructions: [], - annotations: nil + annotations: nil, + incidents: [] ), ], remainingWaypoints: [], diff --git a/apple/Sources/FerrostarSwiftUI/TestFixtureFactory.swift b/apple/Sources/FerrostarSwiftUI/TestFixtureFactory.swift index f41797dc..63b9229e 100644 --- a/apple/Sources/FerrostarSwiftUI/TestFixtureFactory.swift +++ b/apple/Sources/FerrostarSwiftUI/TestFixtureFactory.swift @@ -74,7 +74,8 @@ struct RouteStepFactory: TestFixtureFactory { instruction: "Walk west on \(roadNameBuilder(n))", visualInstructions: [visualInstructionBuilder(n)], spokenInstructions: [], - annotations: nil + annotations: nil, + incidents: [] ) } } diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index cd157817..5424234d 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -396,6 +396,22 @@ fileprivate class UniffiHandleMap { // Public interface members begin here. +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1676,6 +1692,67 @@ public func FfiConverterTypeBoundingBox_lower(_ value: BoundingBox) -> RustBuffe } +/** + * Details about congestion for an incident. + */ +public struct Congestion { + public var value: UInt8 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(value: UInt8) { + self.value = value + } +} + + + +extension Congestion: Equatable, Hashable { + public static func ==(lhs: Congestion, rhs: Congestion) -> Bool { + if lhs.value != rhs.value { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCongestion: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Congestion { + return + try Congestion( + value: FfiConverterUInt8.read(from: &buf) + ) + } + + public static func write(_ value: Congestion, into buf: inout [UInt8]) { + FfiConverterUInt8.write(value.value, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCongestion_lift(_ buf: RustBuffer) throws -> Congestion { + return try FfiConverterTypeCongestion.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCongestion_lower(_ value: Congestion) -> RustBuffer { + return FfiConverterTypeCongestion.lower(value) +} + + /** * The direction in which the user/device is observed to be traveling. */ @@ -1935,6 +2012,227 @@ public func FfiConverterTypeHeading_lower(_ value: Heading) -> RustBuffer { } +/** + * Details about an incident + */ +public struct Incident { + public var id: String + public var incidentType: IncidentType + public var description: String? + public var longDescription: String? + public var creationTime: String? + public var startTime: String? + public var endTime: String? + public var impact: Impact? + public var lanesBlocked: [BlockedLane] + public var numLanesBlocked: UInt8? + public var congestion: Congestion? + public var closed: Bool? + public var geometryIndexStart: UInt64 + public var geometryIndexEnd: UInt64 + public var subType: String? + public var subTypeDescription: String? + public var iso31661Alpha2: String? + public var iso31661Alpha3: String? + public var affectedRoadNames: [String] + public var southWest: GeographicCoordinate + public var northEast: GeographicCoordinate + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(id: String, incidentType: IncidentType, description: String?, longDescription: String?, creationTime: String?, startTime: String?, endTime: String?, impact: Impact?, lanesBlocked: [BlockedLane], numLanesBlocked: UInt8?, congestion: Congestion?, closed: Bool?, geometryIndexStart: UInt64, geometryIndexEnd: UInt64, subType: String?, subTypeDescription: String?, iso31661Alpha2: String?, iso31661Alpha3: String?, affectedRoadNames: [String], southWest: GeographicCoordinate, northEast: GeographicCoordinate) { + self.id = id + self.incidentType = incidentType + self.description = description + self.longDescription = longDescription + self.creationTime = creationTime + self.startTime = startTime + self.endTime = endTime + self.impact = impact + self.lanesBlocked = lanesBlocked + self.numLanesBlocked = numLanesBlocked + self.congestion = congestion + self.closed = closed + self.geometryIndexStart = geometryIndexStart + self.geometryIndexEnd = geometryIndexEnd + self.subType = subType + self.subTypeDescription = subTypeDescription + self.iso31661Alpha2 = iso31661Alpha2 + self.iso31661Alpha3 = iso31661Alpha3 + self.affectedRoadNames = affectedRoadNames + self.southWest = southWest + self.northEast = northEast + } +} + + + +extension Incident: Equatable, Hashable { + public static func ==(lhs: Incident, rhs: Incident) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.incidentType != rhs.incidentType { + return false + } + if lhs.description != rhs.description { + return false + } + if lhs.longDescription != rhs.longDescription { + return false + } + if lhs.creationTime != rhs.creationTime { + return false + } + if lhs.startTime != rhs.startTime { + return false + } + if lhs.endTime != rhs.endTime { + return false + } + if lhs.impact != rhs.impact { + return false + } + if lhs.lanesBlocked != rhs.lanesBlocked { + return false + } + if lhs.numLanesBlocked != rhs.numLanesBlocked { + return false + } + if lhs.congestion != rhs.congestion { + return false + } + if lhs.closed != rhs.closed { + return false + } + if lhs.geometryIndexStart != rhs.geometryIndexStart { + return false + } + if lhs.geometryIndexEnd != rhs.geometryIndexEnd { + return false + } + if lhs.subType != rhs.subType { + return false + } + if lhs.subTypeDescription != rhs.subTypeDescription { + return false + } + if lhs.iso31661Alpha2 != rhs.iso31661Alpha2 { + return false + } + if lhs.iso31661Alpha3 != rhs.iso31661Alpha3 { + return false + } + if lhs.affectedRoadNames != rhs.affectedRoadNames { + return false + } + if lhs.southWest != rhs.southWest { + return false + } + if lhs.northEast != rhs.northEast { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(incidentType) + hasher.combine(description) + hasher.combine(longDescription) + hasher.combine(creationTime) + hasher.combine(startTime) + hasher.combine(endTime) + hasher.combine(impact) + hasher.combine(lanesBlocked) + hasher.combine(numLanesBlocked) + hasher.combine(congestion) + hasher.combine(closed) + hasher.combine(geometryIndexStart) + hasher.combine(geometryIndexEnd) + hasher.combine(subType) + hasher.combine(subTypeDescription) + hasher.combine(iso31661Alpha2) + hasher.combine(iso31661Alpha3) + hasher.combine(affectedRoadNames) + hasher.combine(southWest) + hasher.combine(northEast) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeIncident: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Incident { + return + try Incident( + id: FfiConverterString.read(from: &buf), + incidentType: FfiConverterTypeIncidentType.read(from: &buf), + description: FfiConverterOptionString.read(from: &buf), + longDescription: FfiConverterOptionString.read(from: &buf), + creationTime: FfiConverterOptionString.read(from: &buf), + startTime: FfiConverterOptionString.read(from: &buf), + endTime: FfiConverterOptionString.read(from: &buf), + impact: FfiConverterOptionTypeImpact.read(from: &buf), + lanesBlocked: FfiConverterSequenceTypeBlockedLane.read(from: &buf), + numLanesBlocked: FfiConverterOptionUInt8.read(from: &buf), + congestion: FfiConverterOptionTypeCongestion.read(from: &buf), + closed: FfiConverterOptionBool.read(from: &buf), + geometryIndexStart: FfiConverterUInt64.read(from: &buf), + geometryIndexEnd: FfiConverterUInt64.read(from: &buf), + subType: FfiConverterOptionString.read(from: &buf), + subTypeDescription: FfiConverterOptionString.read(from: &buf), + iso31661Alpha2: FfiConverterOptionString.read(from: &buf), + iso31661Alpha3: FfiConverterOptionString.read(from: &buf), + affectedRoadNames: FfiConverterSequenceString.read(from: &buf), + southWest: FfiConverterTypeGeographicCoordinate.read(from: &buf), + northEast: FfiConverterTypeGeographicCoordinate.read(from: &buf) + ) + } + + public static func write(_ value: Incident, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypeIncidentType.write(value.incidentType, into: &buf) + FfiConverterOptionString.write(value.description, into: &buf) + FfiConverterOptionString.write(value.longDescription, into: &buf) + FfiConverterOptionString.write(value.creationTime, into: &buf) + FfiConverterOptionString.write(value.startTime, into: &buf) + FfiConverterOptionString.write(value.endTime, into: &buf) + FfiConverterOptionTypeImpact.write(value.impact, into: &buf) + FfiConverterSequenceTypeBlockedLane.write(value.lanesBlocked, into: &buf) + FfiConverterOptionUInt8.write(value.numLanesBlocked, into: &buf) + FfiConverterOptionTypeCongestion.write(value.congestion, into: &buf) + FfiConverterOptionBool.write(value.closed, into: &buf) + FfiConverterUInt64.write(value.geometryIndexStart, into: &buf) + FfiConverterUInt64.write(value.geometryIndexEnd, into: &buf) + FfiConverterOptionString.write(value.subType, into: &buf) + FfiConverterOptionString.write(value.subTypeDescription, into: &buf) + FfiConverterOptionString.write(value.iso31661Alpha2, into: &buf) + FfiConverterOptionString.write(value.iso31661Alpha3, into: &buf) + FfiConverterSequenceString.write(value.affectedRoadNames, into: &buf) + FfiConverterTypeGeographicCoordinate.write(value.southWest, into: &buf) + FfiConverterTypeGeographicCoordinate.write(value.northEast, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeIncident_lift(_ buf: RustBuffer) throws -> Incident { + return try FfiConverterTypeIncident.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeIncident_lower(_ value: Incident) -> RustBuffer { + return FfiConverterTypeIncident.lower(value) +} + + /** * The content of a visual instruction. */ @@ -2318,6 +2616,10 @@ public struct RouteStep { * A list of json encoded strings representing annotations between each coordinate along the step. */ public var annotations: [String]? + /** + * A list of incidents that occur along the step. + */ + public var incidents: [Incident] // Default memberwise initializers are never public by default, so we // declare one manually. @@ -2349,7 +2651,10 @@ public struct RouteStep { */spokenInstructions: [SpokenInstruction], /** * A list of json encoded strings representing annotations between each coordinate along the step. - */annotations: [String]?) { + */annotations: [String]?, + /** + * A list of incidents that occur along the step. + */incidents: [Incident]) { self.geometry = geometry self.distance = distance self.duration = duration @@ -2358,6 +2663,7 @@ public struct RouteStep { self.visualInstructions = visualInstructions self.spokenInstructions = spokenInstructions self.annotations = annotations + self.incidents = incidents } } @@ -2389,6 +2695,9 @@ extension RouteStep: Equatable, Hashable { if lhs.annotations != rhs.annotations { return false } + if lhs.incidents != rhs.incidents { + return false + } return true } @@ -2401,6 +2710,7 @@ extension RouteStep: Equatable, Hashable { hasher.combine(visualInstructions) hasher.combine(spokenInstructions) hasher.combine(annotations) + hasher.combine(incidents) } } @@ -2419,7 +2729,8 @@ public struct FfiConverterTypeRouteStep: FfiConverterRustBuffer { instruction: FfiConverterString.read(from: &buf), visualInstructions: FfiConverterSequenceTypeVisualInstruction.read(from: &buf), spokenInstructions: FfiConverterSequenceTypeSpokenInstruction.read(from: &buf), - annotations: FfiConverterOptionSequenceString.read(from: &buf) + annotations: FfiConverterOptionSequenceString.read(from: &buf), + incidents: FfiConverterSequenceTypeIncident.read(from: &buf) ) } @@ -2432,6 +2743,7 @@ public struct FfiConverterTypeRouteStep: FfiConverterRustBuffer { FfiConverterSequenceTypeVisualInstruction.write(value.visualInstructions, into: &buf) FfiConverterSequenceTypeSpokenInstruction.write(value.spokenInstructions, into: &buf) FfiConverterOptionSequenceString.write(value.annotations, into: &buf) + FfiConverterSequenceTypeIncident.write(value.incidents, into: &buf) } } @@ -3183,6 +3495,115 @@ public func FfiConverterTypeWaypoint_lower(_ value: Waypoint) -> RustBuffer { return FfiConverterTypeWaypoint.lower(value) } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The lane type blocked by the incident. + */ + +public enum BlockedLane { + + case left + case leftCenter + case leftTurnLane + case center + case right + case rightCenter + case rightTurnLane + case hov +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBlockedLane: FfiConverterRustBuffer { + typealias SwiftType = BlockedLane + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BlockedLane { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .left + + case 2: return .leftCenter + + case 3: return .leftTurnLane + + case 4: return .center + + case 5: return .right + + case 6: return .rightCenter + + case 7: return .rightTurnLane + + case 8: return .hov + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: BlockedLane, into buf: inout [UInt8]) { + switch value { + + + case .left: + writeInt(&buf, Int32(1)) + + + case .leftCenter: + writeInt(&buf, Int32(2)) + + + case .leftTurnLane: + writeInt(&buf, Int32(3)) + + + case .center: + writeInt(&buf, Int32(4)) + + + case .right: + writeInt(&buf, Int32(5)) + + + case .rightCenter: + writeInt(&buf, Int32(6)) + + + case .rightTurnLane: + writeInt(&buf, Int32(7)) + + + case .hov: + writeInt(&buf, Int32(8)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBlockedLane_lift(_ buf: RustBuffer) throws -> BlockedLane { + return try FfiConverterTypeBlockedLane.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBlockedLane_lower(_ value: BlockedLane) -> RustBuffer { + return FfiConverterTypeBlockedLane.lower(value) +} + + + +extension BlockedLane: Equatable, Hashable {} + + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** @@ -3257,6 +3678,231 @@ extension CourseFiltering: Equatable, Hashable {} +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The impact of the incident that has occurred. + */ + +public enum Impact { + + case unknown + case critical + case major + case minor + case low +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeImpact: FfiConverterRustBuffer { + typealias SwiftType = Impact + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Impact { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .unknown + + case 2: return .critical + + case 3: return .major + + case 4: return .minor + + case 5: return .low + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: Impact, into buf: inout [UInt8]) { + switch value { + + + case .unknown: + writeInt(&buf, Int32(1)) + + + case .critical: + writeInt(&buf, Int32(2)) + + + case .major: + writeInt(&buf, Int32(3)) + + + case .minor: + writeInt(&buf, Int32(4)) + + + case .low: + writeInt(&buf, Int32(5)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeImpact_lift(_ buf: RustBuffer) throws -> Impact { + return try FfiConverterTypeImpact.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeImpact_lower(_ value: Impact) -> RustBuffer { + return FfiConverterTypeImpact.lower(value) +} + + + +extension Impact: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The type of incident that has occurred. + */ + +public enum IncidentType { + + case accident + case congestion + case construction + case disabledVehicle + case laneRestriction + case massTransit + case miscellaneous + case otherNews + case plannedEvent + case roadClosure + case roadHazard + case weather +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeIncidentType: FfiConverterRustBuffer { + typealias SwiftType = IncidentType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IncidentType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .accident + + case 2: return .congestion + + case 3: return .construction + + case 4: return .disabledVehicle + + case 5: return .laneRestriction + + case 6: return .massTransit + + case 7: return .miscellaneous + + case 8: return .otherNews + + case 9: return .plannedEvent + + case 10: return .roadClosure + + case 11: return .roadHazard + + case 12: return .weather + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: IncidentType, into buf: inout [UInt8]) { + switch value { + + + case .accident: + writeInt(&buf, Int32(1)) + + + case .congestion: + writeInt(&buf, Int32(2)) + + + case .construction: + writeInt(&buf, Int32(3)) + + + case .disabledVehicle: + writeInt(&buf, Int32(4)) + + + case .laneRestriction: + writeInt(&buf, Int32(5)) + + + case .massTransit: + writeInt(&buf, Int32(6)) + + + case .miscellaneous: + writeInt(&buf, Int32(7)) + + + case .otherNews: + writeInt(&buf, Int32(8)) + + + case .plannedEvent: + writeInt(&buf, Int32(9)) + + + case .roadClosure: + writeInt(&buf, Int32(10)) + + + case .roadHazard: + writeInt(&buf, Int32(11)) + + + case .weather: + writeInt(&buf, Int32(12)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeIncidentType_lift(_ buf: RustBuffer) throws -> IncidentType { + return try FfiConverterTypeIncidentType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeIncidentType_lower(_ value: IncidentType) -> RustBuffer { + return FfiConverterTypeIncidentType.lower(value) +} + + + +extension IncidentType: Equatable, Hashable {} + + + public enum InstantiationError { @@ -4556,6 +5202,30 @@ extension WaypointKind: Equatable, Hashable {} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionUInt8: FfiConverterRustBuffer { + typealias SwiftType = UInt8? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterUInt8.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterUInt8.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -4628,6 +5298,30 @@ fileprivate struct FfiConverterOptionDouble: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionBool: FfiConverterRustBuffer { + typealias SwiftType = Bool? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterBool.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterBool.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -4652,6 +5346,30 @@ fileprivate struct FfiConverterOptionString: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeCongestion: FfiConverterRustBuffer { + typealias SwiftType = Congestion? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeCongestion.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeCongestion.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -4772,6 +5490,30 @@ fileprivate struct FfiConverterOptionTypeVisualInstructionContent: FfiConverterR } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeImpact: FfiConverterRustBuffer { + typealias SwiftType = Impact? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeImpact.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeImpact.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -4918,6 +5660,31 @@ fileprivate struct FfiConverterSequenceTypeGeographicCoordinate: FfiConverterRus } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeIncident: FfiConverterRustBuffer { + typealias SwiftType = [Incident] + + public static func write(_ value: [Incident], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeIncident.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Incident] { + let len: Int32 = try readInt(&buf) + var seq = [Incident]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeIncident.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -5068,6 +5835,31 @@ fileprivate struct FfiConverterSequenceTypeWaypoint: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeBlockedLane: FfiConverterRustBuffer { + typealias SwiftType = [BlockedLane] + + public static func write(_ value: [BlockedLane], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeBlockedLane.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [BlockedLane] { + let len: Int32 = try readInt(&buf) + var seq = [BlockedLane]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeBlockedLane.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif diff --git a/apple/Tests/FerrostarCoreTests/FerrostarCoreTests.swift b/apple/Tests/FerrostarCoreTests/FerrostarCoreTests.swift index c5fc4423..e21ecfb1 100644 --- a/apple/Tests/FerrostarCoreTests/FerrostarCoreTests.swift +++ b/apple/Tests/FerrostarCoreTests/FerrostarCoreTests.swift @@ -43,7 +43,8 @@ let mockRoute = Route( triggerDistanceBeforeManeuver: 42 )], spokenInstructions: [], - annotations: nil + annotations: nil, + incidents: [] )] ) diff --git a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockGETRouteResponse.1.txt b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockGETRouteResponse.1.txt index 2892b258..78538877 100644 --- a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockGETRouteResponse.1.txt +++ b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockGETRouteResponse.1.txt @@ -27,6 +27,7 @@ ▿ GeographicCoordinate - lat: 1.0 - lng: 1.0 + - incidents: 0 elements - instruction: "Sail straight" ▿ roadName: Optional - some: "foo road" diff --git a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockPOSTRouteResponse.1.txt b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockPOSTRouteResponse.1.txt index 2892b258..78538877 100644 --- a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockPOSTRouteResponse.1.txt +++ b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/test200MockPOSTRouteResponse.1.txt @@ -27,6 +27,7 @@ ▿ GeographicCoordinate - lat: 1.0 - lng: 1.0 + - incidents: 0 elements - instruction: "Sail straight" ▿ roadName: Optional - some: "foo road" diff --git a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testCustomRouteProvider.1.txt b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testCustomRouteProvider.1.txt index 2892b258..78538877 100644 --- a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testCustomRouteProvider.1.txt +++ b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testCustomRouteProvider.1.txt @@ -27,6 +27,7 @@ ▿ GeographicCoordinate - lat: 1.0 - lng: 1.0 + - incidents: 0 elements - instruction: "Sail straight" ▿ roadName: Optional - some: "foo road" diff --git a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testValhallaCostingOptionsJSON.1.txt b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testValhallaCostingOptionsJSON.1.txt index fd991b65..882b331f 100644 --- a/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testValhallaCostingOptionsJSON.1.txt +++ b/apple/Tests/FerrostarCoreTests/__Snapshots__/FerrostarCoreTests/testValhallaCostingOptionsJSON.1.txt @@ -75,6 +75,7 @@ ▿ GeographicCoordinate - lat: 60.534991 - lng: -149.548581 + - incidents: 0 elements - instruction: "Drive west on AK 1/Seward Highway." ▿ roadName: Optional - some: "Seward Highway" @@ -91,6 +92,7 @@ ▿ GeographicCoordinate - lat: 60.534991 - lng: -149.548581 + - incidents: 0 elements - instruction: "You have arrived at your destination." ▿ roadName: Optional - some: "Seward Highway" diff --git a/apple/Tests/FerrostarCoreTests/__Snapshots__/ValhallaCoreTests/testValhallaRouteParsing.1.txt b/apple/Tests/FerrostarCoreTests/__Snapshots__/ValhallaCoreTests/testValhallaRouteParsing.1.txt index fd991b65..882b331f 100644 --- a/apple/Tests/FerrostarCoreTests/__Snapshots__/ValhallaCoreTests/testValhallaRouteParsing.1.txt +++ b/apple/Tests/FerrostarCoreTests/__Snapshots__/ValhallaCoreTests/testValhallaRouteParsing.1.txt @@ -75,6 +75,7 @@ ▿ GeographicCoordinate - lat: 60.534991 - lng: -149.548581 + - incidents: 0 elements - instruction: "Drive west on AK 1/Seward Highway." ▿ roadName: Optional - some: "Seward Highway" @@ -91,6 +92,7 @@ ▿ GeographicCoordinate - lat: 60.534991 - lng: -149.548581 + - incidents: 0 elements - instruction: "You have arrived at your destination." ▿ roadName: Optional - some: "Seward Highway" diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index 176c2890..296c470b 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -322,6 +322,8 @@ pub struct RouteStep { pub spoken_instructions: Vec, /// A list of json encoded strings representing annotations between each coordinate along the step. pub annotations: Option>, + /// A list of incidents that occur along the step. + pub incidents: Vec, } impl RouteStep { @@ -462,6 +464,107 @@ pub enum ManeuverModifier { SharpLeft, } +/// The type of incident that has occurred. +#[derive(Deserialize, Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +#[cfg_attr(any(test, feature = "wasm-bindgen"), derive(Serialize))] +#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))] +#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))] +#[serde(rename_all = "snake_case")] +pub enum IncidentType { + Accident, + Congestion, + Construction, + DisabledVehicle, + LaneRestriction, + MassTransit, + Miscellaneous, + OtherNews, + PlannedEvent, + RoadClosure, + RoadHazard, + Weather, +} + +/// The impact of the incident that has occurred. +#[derive(Deserialize, Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +#[cfg_attr(any(test, feature = "wasm-bindgen"), derive(Serialize))] +#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))] +#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))] +#[serde(rename_all = "lowercase")] +pub enum Impact { + Unknown, + Critical, + Major, + Minor, + Low, +} + +/// The lane type blocked by the incident. +#[derive(Deserialize, Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +#[cfg_attr(any(test, feature = "wasm-bindgen"), derive(Serialize))] +#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))] +#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))] +#[serde(rename_all = "lowercase")] +pub enum BlockedLane { + Left, + #[serde(rename = "left center")] + LeftCenter, + #[serde(rename = "left turn lane")] + LeftTurnLane, + Center, + Right, + #[serde(rename = "right center")] + RightCenter, + #[serde(rename = "right turn lane")] + RightTurnLane, + #[serde(rename = "hov")] + HOV, +} + +/// Details about congestion for an incident. +#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] +#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))] +#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))] +pub struct Congestion { + pub value: u8, +} + +/// Details about an incident +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] +#[cfg_attr(any(feature = "wasm-bindgen", test), derive(Serialize, Deserialize))] +#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))] +#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))] +pub struct Incident { + pub id: String, + pub incident_type: IncidentType, + pub description: Option, + pub long_description: Option, + pub creation_time: Option, + pub start_time: Option, + pub end_time: Option, + pub impact: Option, + pub lanes_blocked: Vec, + pub num_lanes_blocked: Option, + pub congestion: Option, + pub closed: Option, + pub geometry_index_start: u64, + pub geometry_index_end: Option, + pub sub_type: Option, + pub sub_type_description: Option, + pub iso_3166_1_alpha2: Option, + pub iso_3166_1_alpha3: Option, + pub affected_road_names: Vec, + pub south_west: Option, + pub north_east: Option, +} + /// The content of a visual instruction. #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] diff --git a/common/ferrostar/src/navigation_controller/test_helpers.rs b/common/ferrostar/src/navigation_controller/test_helpers.rs index ca39f075..6a94673c 100644 --- a/common/ferrostar/src/navigation_controller/test_helpers.rs +++ b/common/ferrostar/src/navigation_controller/test_helpers.rs @@ -31,6 +31,7 @@ pub fn gen_dummy_route_step( visual_instructions: vec![], spoken_instructions: vec![], annotations: None, + incidents: vec![], } } diff --git a/common/ferrostar/src/routing_adapters/osrm/mod.rs b/common/ferrostar/src/routing_adapters/osrm/mod.rs index 6a49acf4..27f54bdb 100644 --- a/common/ferrostar/src/routing_adapters/osrm/mod.rs +++ b/common/ferrostar/src/routing_adapters/osrm/mod.rs @@ -5,7 +5,7 @@ pub mod utilities; use super::RouteResponseParser; use crate::models::{ - AnyAnnotationValue, GeographicCoordinate, LaneInfo, RouteStep, SpokenInstruction, + AnyAnnotationValue, GeographicCoordinate, Incident, LaneInfo, RouteStep, SpokenInstruction, VisualInstruction, VisualInstructionContent, Waypoint, WaypointKind, }; use crate::routing_adapters::utilities::get_coordinates_from_geometry; @@ -101,6 +101,13 @@ impl Route { .as_ref() .map(|leg_annotation| utilities::zip_annotations(leg_annotation.clone())); + // Convert all incidents into a vector of Incident objects. + let incident_items = leg + .incidents + .iter() + .map(|incident| utilities::convert_incident(incident)) + .collect::>(); + // Index for the annotations slice let mut start_index: usize = 0; @@ -120,12 +127,49 @@ impl Route { let annotation_slice = get_annotation_slice(annotations.clone(), start_index, end_index).ok(); + let relevant_incidents_slice = incident_items + .iter() + .filter(|incident| { + let incident_start = incident.geometry_index_start as usize; + + match incident.geometry_index_end { + Some(end) => { + let incident_end = end as usize; + incident_start >= start_index && incident_end <= end_index + } + None => { + incident_start >= start_index && incident_start <= end_index + } + } + }) + .map(|incident| { + let mut adjusted_incident = incident.clone(); + if adjusted_incident.geometry_index_start - start_index as u64 > 0 { + adjusted_incident.geometry_index_start -= start_index as u64; + } else { + adjusted_incident.geometry_index_start = 0; + } + + if let Some(end) = adjusted_incident.geometry_index_end { + let adjusted_end = end - start_index as u64; + adjusted_incident.geometry_index_end = + Some(if adjusted_end > end_index as u64 { + end_index as u64 + } else { + adjusted_end + }) + } + adjusted_incident + }) + .collect::>(); + start_index = end_index; return RouteStep::from_osrm_and_geom( step, step_geometry, annotation_slice, + relevant_incidents_slice, ); }); }) @@ -151,6 +195,7 @@ impl RouteStep { value: &OsrmRouteStep, geometry: Vec, annotations: Option>, + incidents: Vec, ) -> Result { let visual_instructions = value .banner_instructions @@ -232,6 +277,7 @@ impl RouteStep { visual_instructions, spoken_instructions, annotations: annotations_as_strings, + incidents, }) } } diff --git a/common/ferrostar/src/routing_adapters/osrm/models.rs b/common/ferrostar/src/routing_adapters/osrm/models.rs index c8b97a0d..f602da8b 100644 --- a/common/ferrostar/src/routing_adapters/osrm/models.rs +++ b/common/ferrostar/src/routing_adapters/osrm/models.rs @@ -4,7 +4,9 @@ //! by others which are now pseudo-standardized (ex: Mapbox). We omit some fields which are not //! needed for navigation. -use crate::models::{ManeuverModifier, ManeuverType}; +use crate::models::{ + BlockedLane, Congestion, Impact, IncidentType, ManeuverModifier, ManeuverType, +}; use alloc::{string::String, vec::Vec}; use serde::Deserialize; use serde_json::Value; @@ -67,6 +69,9 @@ pub struct RouteLeg { /// A Mapbox and Valhalla extension which indicates which waypoints are passed through rather than creating a new leg. #[serde(default)] pub via_waypoints: Vec, + /// Incidents along the route. + #[serde(default)] + pub incidents: Vec, } #[derive(Deserialize, Debug, Clone, PartialEq)] @@ -75,6 +80,36 @@ pub struct AnyAnnotation { pub values: HashMap>, } +#[derive(Deserialize, Debug)] +pub struct OsrmIncident { + pub id: String, + #[serde(rename = "type")] + pub incident_type: IncidentType, + pub description: Option, + pub long_description: Option, + pub creation_time: Option, + pub start_time: Option, + pub end_time: Option, + pub impact: Option, + #[serde(default)] + pub lanes_blocked: Vec, + pub num_lanes_blocked: Option, + pub congestion: Option, + pub closed: Option, + pub geometry_index_start: u64, + pub geometry_index_end: Option, + pub sub_type: Option, + pub sub_type_description: Option, + pub iso_3166_1_alpha2: Option, + pub iso_3166_1_alpha3: Option, + #[serde(default)] + pub affected_road_names: Vec, + pub south: Option, + pub west: Option, + pub north: Option, + pub east: Option, +} + #[derive(Deserialize, Debug)] pub struct RouteStep { /// The distance from the start of the current maneuver to the following step, in meters. @@ -500,4 +535,88 @@ mod tests { Some(vec!["right".to_string()]) ); } + + #[test] + fn deserialize_incidents() { + let data = r#" + { + "id": "13956787949218641", + "type": "construction", + "creation_time": "2024-11-13T16:39:17Z", + "start_time": "2023-04-03T22:00:00Z", + "end_time": "2024-11-26T04:59:00Z", + "iso_3166_1_alpha2": "US", + "iso_3166_1_alpha3": "USA", + "description": "I-84 W/B: intermittent lane closures from Exit 57 CT-15 to US-44 Connecticut Blvd", + "long_description": "Intermittent lane closures due to barrier repairs on I-84 Westbound from Exit 57 CT-15 to US-44 Connecticut Blvd.", + "impact": "major", + "sub_type": "CONSTRUCTION", + "alertc_codes": [ + 701, + 703 + ], + "traffic_codes": { + "incident_primary_code": 701 + }, + "lanes_blocked": [], + "length": 2403, + "south": 41.763362, + "west": -72.661148, + "north": 41.769363, + "east": -72.633712, + "congestion": { + "value": 101 + }, + "geometry_index_start": 2932, + "geometry_index_end": 3017, + "affected_road_names": [ + "Officer Brian A. Aselton Memorial Highway" + ], + "affected_road_names_unknown": [ + "I 84 West/US 6" + ], + "affected_road_names_en": [ + "Officer Brian A. Aselton Memorial Highway" + ] + } + "#; + + let incident: OsrmIncident = serde_json::from_str(data).expect("Failed to parse Incident"); + + // help me finish this test + assert_eq!(incident.id, "13956787949218641"); + assert_eq!(incident.incident_type, IncidentType::Construction); + assert_eq!( + incident.creation_time, + Some("2024-11-13T16:39:17Z".to_string()) + ); + assert_eq!( + incident.start_time, + Some("2023-04-03T22:00:00Z".to_string()) + ); + assert_eq!(incident.end_time, Some("2024-11-26T04:59:00Z".to_string())); + assert_eq!(incident.iso_3166_1_alpha2, Some("US".to_string())); + assert_eq!(incident.iso_3166_1_alpha3, Some("USA".to_string())); + assert_eq!( + incident.description, + Some( + "I-84 W/B: intermittent lane closures from Exit 57 CT-15 to US-44 Connecticut Blvd" + .to_string() + ) + ); + assert_eq!(incident.impact, Some(Impact::Major)); + assert_eq!(incident.sub_type, Some("CONSTRUCTION".to_string())); + assert_eq!(incident.lanes_blocked, vec![]); + assert_eq!(incident.south, Some(41.763362)); + assert_eq!(incident.west, Some(-72.661148)); + assert_eq!(incident.north, Some(41.769363)); + assert_eq!(incident.east, Some(-72.633712)); + assert_eq!(incident.congestion.unwrap().value, 101); + assert_eq!(incident.geometry_index_start, 2932); + assert_eq!(incident.geometry_index_end, Some(3017)); + assert_eq!( + incident.affected_road_names, + vec!["Officer Brian A. Aselton Memorial Highway"] + ); + } } diff --git a/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm.snap b/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm.snap index 05119e3f..57e5b7a8 100644 --- a/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm.snap +++ b/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm.snap @@ -338,6 +338,7 @@ expression: routes ssml: "In 200 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442754 lng: 24.763449 @@ -362,6 +363,7 @@ expression: routes ssml: "In 14 feet, Turn right onto Laeva." trigger_distance_before_maneuver: 4.5 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442671 lng: 24.763423 @@ -386,6 +388,7 @@ expression: routes ssml: "In 26 feet, Bear right." trigger_distance_before_maneuver: 8 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442709 lng: 24.763155 @@ -412,6 +415,7 @@ expression: routes ssml: "In 24 feet, Bear left onto the walkway." trigger_distance_before_maneuver: 7.5 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442819 lng: 24.763 @@ -440,6 +444,7 @@ expression: routes ssml: "In 62 feet, Continue." trigger_distance_before_maneuver: 19 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442918 lng: 24.762356 @@ -464,6 +469,7 @@ expression: routes ssml: "In 11 feet, Turn right onto Admiralisild/Admiral Bridge." trigger_distance_before_maneuver: 3.5 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.442936 lng: 24.762237 @@ -494,6 +500,7 @@ expression: routes ssml: "In 200 feet, Continue on the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.443526 lng: 24.761765 @@ -520,6 +527,7 @@ expression: routes ssml: "In 75 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 23 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.4439 lng: 24.761432 @@ -544,6 +552,7 @@ expression: routes ssml: "In 200 feet, Turn right onto the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.443487 lng: 24.759273 @@ -574,6 +583,7 @@ expression: routes ssml: "In 41 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 12.5 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.443712 lng: 24.759127 @@ -602,6 +612,7 @@ expression: routes ssml: "In 26 feet, Turn right onto Logi." trigger_distance_before_maneuver: 8 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.443674 lng: 24.758853 @@ -644,6 +655,7 @@ expression: routes ssml: "In 200 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.444448 lng: 24.758392 @@ -668,6 +680,7 @@ expression: routes ssml: "In 13 feet, Turn right onto the walkway." trigger_distance_before_maneuver: 4 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.444431 lng: 24.758246 @@ -698,6 +711,7 @@ expression: routes ssml: "In 200 feet, Bear left onto Kultuurikilomeeter." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.445069 lng: 24.757636 @@ -828,6 +842,7 @@ expression: routes ssml: "In 200 feet, Turn right onto the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.44946 lng: 24.739543 @@ -854,6 +869,7 @@ expression: routes ssml: "In 37 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 11.5 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.449652 lng: 24.739675 @@ -880,6 +896,7 @@ expression: routes ssml: "In 26 feet, Turn left onto the crosswalk." trigger_distance_before_maneuver: 8 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.449733 lng: 24.739454 @@ -942,6 +959,7 @@ expression: routes ssml: "In 200 feet, Turn right onto the walkway." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.450765 lng: 24.733721 @@ -966,6 +984,7 @@ expression: routes ssml: "In 3 feet, Turn left onto the walkway." trigger_distance_before_maneuver: 1 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.450787 lng: 24.733717 @@ -1016,6 +1035,7 @@ expression: routes ssml: "In 200 feet, Bear left onto Allveelaeva." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.451907 lng: 24.730259 @@ -1040,6 +1060,7 @@ expression: routes ssml: "In 45 feet, Turn right onto Peetri." trigger_distance_before_maneuver: 14 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.452026 lng: 24.729829 @@ -1066,6 +1087,7 @@ expression: routes ssml: "In 41 feet, You have arrived at your destination." trigger_distance_before_maneuver: 12.5495 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 59.452226 lng: 24.730034 @@ -1087,3 +1109,4 @@ expression: routes trigger_distance_before_maneuver: 0 spoken_instructions: [] annotations: redacted annotations json strings vec + incidents: [] diff --git a/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm_with_via_ways.snap b/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm_with_via_ways.snap index 60430485..c0b66e45 100644 --- a/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm_with_via_ways.snap +++ b/common/ferrostar/src/routing_adapters/osrm/snapshots/ferrostar__routing_adapters__osrm__tests__parse_valhalla_osrm_with_via_ways.snap @@ -452,6 +452,7 @@ expression: routes ssml: "In 200 feet, You have arrived at your destination." trigger_distance_before_maneuver: 60 annotations: redacted annotations json strings vec + incidents: [] - geometry: - lat: 28.790106 lng: -82.018021 @@ -473,3 +474,4 @@ expression: routes trigger_distance_before_maneuver: 0 spoken_instructions: [] annotations: redacted annotations json strings vec + incidents: [] diff --git a/common/ferrostar/src/routing_adapters/osrm/utilities.rs b/common/ferrostar/src/routing_adapters/osrm/utilities.rs index e7a5ced7..f8d4abf1 100644 --- a/common/ferrostar/src/routing_adapters/osrm/utilities.rs +++ b/common/ferrostar/src/routing_adapters/osrm/utilities.rs @@ -1,5 +1,5 @@ -use super::models::AnyAnnotation; -use crate::models::AnyAnnotationValue; +use super::models::{AnyAnnotation, OsrmIncident}; +use crate::models::{AnyAnnotationValue, GeographicCoordinate, Incident}; use crate::routing_adapters::error::ParsingError; use serde_json::Value; use std::collections::HashMap; @@ -47,6 +47,44 @@ pub(crate) fn zip_annotations(annotation: AnyAnnotation) -> Vec>(); } +pub(crate) fn convert_incident(incident: &OsrmIncident) -> Incident { + Incident { + id: incident.id.clone(), + incident_type: incident.incident_type, + description: incident.description.clone(), + long_description: incident.long_description.clone(), + creation_time: incident.creation_time.clone(), + start_time: incident.start_time.clone(), + end_time: incident.end_time.clone(), + impact: incident.impact, + lanes_blocked: incident.lanes_blocked.clone(), + num_lanes_blocked: incident.num_lanes_blocked, + congestion: incident.congestion.clone(), + closed: incident.closed, + geometry_index_start: incident.geometry_index_start, + geometry_index_end: incident.geometry_index_end, + sub_type: incident.sub_type.clone(), + sub_type_description: incident.sub_type_description.clone(), + iso_3166_1_alpha2: incident.iso_3166_1_alpha2.clone(), + iso_3166_1_alpha3: incident.iso_3166_1_alpha3.clone(), + affected_road_names: incident.affected_road_names.clone(), + south_west: match (incident.south, incident.west) { + (Some(south), Some(west)) => Some(GeographicCoordinate { + lat: south, + lng: west, + }), + _ => None, + }, + north_east: match (incident.north, incident.east) { + (Some(north), Some(east)) => Some(GeographicCoordinate { + lat: north, + lng: east, + }), + _ => None, + }, + } +} + #[cfg(test)] mod test {