From df9607cd4adad575b258e8ac63185da6c2b61f1f Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sat, 31 Aug 2024 16:31:59 -0700 Subject: [PATCH 01/16] Added nearest coordinate index for polyline --- .../core/mock/MockNavigationState.kt | 1 + .../Sources/FerrostarCore/FerrostarCore.swift | 1 + .../Mock/MockNavigationState.swift | 2 + .../DynamicallyOrientingNavigationView.swift | 4 +- .../Views/LandscapeNavigationView.swift | 4 +- .../Views/NavigationMapView.swift | 4 +- .../LandscapeNavigationOverlayView.swift | 4 +- .../PortraitNavigationOverlayView.swift | 4 +- .../Views/PortraitNavigationView.swift | 4 +- apple/Sources/UniFFI/ferrostar.swift | 87 ++++++++++------- common/ferrostar/src/algorithms.rs | 94 ++++++++++++++++++- common/ferrostar/src/models.rs | 17 ++-- .../src/navigation_controller/mod.rs | 26 ++++- .../src/navigation_controller/models.rs | 3 + 14 files changed, 198 insertions(+), 57 deletions(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt index 3ca9e220..7ec7c9da 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt @@ -33,6 +33,7 @@ fun NavigationState.Companion.pedestrianExample(): NavigationState { return NavigationState( tripState = TripState.Navigating( + currentGeometryIndex = 0L, snappedUserLocation = UserLocation.pedestrianExample(), remainingSteps = listOf(), remainingWaypoints = listOf(), diff --git a/apple/Sources/FerrostarCore/FerrostarCore.swift b/apple/Sources/FerrostarCore/FerrostarCore.swift index 9b38ffc4..7c3c0340 100644 --- a/apple/Sources/FerrostarCore/FerrostarCore.swift +++ b/apple/Sources/FerrostarCore/FerrostarCore.swift @@ -274,6 +274,7 @@ public protocol FerrostarCoreDelegate: AnyObject { switch newState { case let .navigating( + currentGeometryIndex: _, snappedUserLocation: _, remainingSteps: _, remainingWaypoints: remainingWaypoints, diff --git a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift index f46955f7..a0eaca22 100644 --- a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift +++ b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift @@ -5,6 +5,7 @@ import Foundation public extension NavigationState { static let pedestrianExample = NavigationState( tripState: .navigating( + currentGeometryIndex: 0, snappedUserLocation: UserLocation( latitude: samplePedestrianWaypoints.first!.lat, longitude: samplePedestrianWaypoints.first!.lng, @@ -36,6 +37,7 @@ public extension NavigationState { return NavigationState( tripState: .navigating( + currentGeometryIndex: 0, snappedUserLocation: UserLocation( coordinates: samplePedestrianWaypoints.first!, horizontalAccuracy: 10, diff --git a/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift b/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift index 0b892eb2..75ddf6ba 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift @@ -134,7 +134,7 @@ public struct DynamicallyOrientingNavigationView: View, CustomizableNavigatingIn formatter.locale = Locale(identifier: "en-US") formatter.units = .imperial - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } @@ -153,7 +153,7 @@ public struct DynamicallyOrientingNavigationView: View, CustomizableNavigatingIn formatter.locale = Locale(identifier: "en-US") formatter.units = .metric - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } diff --git a/apple/Sources/FerrostarMapLibreUI/Views/LandscapeNavigationView.swift b/apple/Sources/FerrostarMapLibreUI/Views/LandscapeNavigationView.swift index 514085f4..f929c1df 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/LandscapeNavigationView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/LandscapeNavigationView.swift @@ -103,7 +103,7 @@ public struct LandscapeNavigationView: View { formatter.locale = Locale(identifier: "en-US") formatter.units = .imperial - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } @@ -124,7 +124,7 @@ public struct LandscapeNavigationView: View { formatter.locale = Locale(identifier: "en-US") formatter.units = .metric - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } diff --git a/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift b/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift index 16f7f8ca..56165e77 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift @@ -83,7 +83,7 @@ public struct NavigationMapView: View { } private func updateCameraIfNeeded() { - if case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = navigationState?.tripState, + if case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = navigationState?.tripState, // There is no reason to push an update if the coordinate and heading are the same. // That's all that gets displayed, so it's all that MapLibre should care about. locationManager.lastLocation.coordinate != userLocation.coordinates @@ -98,7 +98,7 @@ public struct NavigationMapView: View { // TODO: Make map URL configurable but gitignored let state = NavigationState.modifiedPedestrianExample(droppingNWaypoints: 4) - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } diff --git a/apple/Sources/FerrostarMapLibreUI/Views/Overylays/LandscapeNavigationOverlayView.swift b/apple/Sources/FerrostarMapLibreUI/Views/Overylays/LandscapeNavigationOverlayView.swift index 7fe65204..e85e41fc 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/Overylays/LandscapeNavigationOverlayView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/Overylays/LandscapeNavigationOverlayView.swift @@ -47,7 +47,7 @@ struct LandscapeNavigationOverlayView: View, CustomizableNavigatingInnerGridView var body: some View { HStack { VStack { - if case let .navigating(_, _, _, progress: progress, _, visualInstruction: visualInstruction, + if case let .navigating(_, _, _, _, progress: progress, _, visualInstruction: visualInstruction, _) = navigationState?.tripState, let visualInstruction { @@ -61,7 +61,7 @@ struct LandscapeNavigationOverlayView: View, CustomizableNavigatingInnerGridView Spacer() - if case let .navigating(_, _, _, progress: progress, _, _, _) = navigationState?.tripState { + if case let .navigating(_, _, _, _, progress: progress, _, _, _) = navigationState?.tripState { ArrivalView( progress: progress, onTapExit: onTapExit diff --git a/apple/Sources/FerrostarMapLibreUI/Views/Overylays/PortraitNavigationOverlayView.swift b/apple/Sources/FerrostarMapLibreUI/Views/Overylays/PortraitNavigationOverlayView.swift index 8814af19..cfecd6f3 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/Overylays/PortraitNavigationOverlayView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/Overylays/PortraitNavigationOverlayView.swift @@ -46,7 +46,7 @@ struct PortraitNavigationOverlayView: View, CustomizableNavigatingInnerGridView var body: some View { VStack { - if case let .navigating(_, _, _, progress: progress, _, visualInstruction: visualInstruction, + if case let .navigating(_, _, _, _, progress: progress, _, visualInstruction: visualInstruction, _) = navigationState?.tripState, let visualInstruction { @@ -81,7 +81,7 @@ struct PortraitNavigationOverlayView: View, CustomizableNavigatingInnerGridView } .padding(.horizontal, 16) - if case let .navigating(_, _, _, progress: progress, _, _, _) = navigationState?.tripState { + if case let .navigating(_, _, _, _, progress: progress, _, _, _) = navigationState?.tripState { ArrivalView( progress: progress, onTapExit: onTapExit diff --git a/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift b/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift index a256f076..1b6a29af 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift @@ -104,7 +104,7 @@ public struct PortraitNavigationView: View, CustomizableNavigatingInnerGridView formatter.locale = Locale(identifier: "en-US") formatter.units = .imperial - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } @@ -124,7 +124,7 @@ public struct PortraitNavigationView: View, CustomizableNavigatingInnerGridView formatter.locale = Locale(identifier: "en-US") formatter.units = .metric - guard case let .navigating(snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { + guard case let .navigating(_, snappedUserLocation: userLocation, _, _, _, _, _, _) = state.tripState else { return EmptyView() } diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index eaef5dd1..7015544e 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -404,6 +404,19 @@ private struct FfiConverterUInt32: FfiConverterPrimitive { } } +private struct FfiConverterInt64: FfiConverterPrimitive { + typealias FfiType = Int64 + typealias SwiftType = Int64 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int64 { + try lift(readInt(&buf)) + } + + public static func write(_ value: Int64, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + private struct FfiConverterDouble: FfiConverterPrimitive { typealias FfiType = Double typealias SwiftType = Double @@ -3305,38 +3318,45 @@ public enum TripState { /** * The navigation controller is actively navigating a trip. */ - case navigating(snappedUserLocation: UserLocation, - /** - * The ordered list of steps that remain in the trip. - * - * The step at the front of the list is always the current step. - * We currently assume that you cannot move backward to a previous step. - */ remainingSteps: [RouteStep], - /** - * Remaining waypoints to visit on the route. - * - * The waypoint at the front of the list is always the *next* waypoint "goal." - * Unlike the current step, there is no value in tracking the "current" waypoint, - * as the main use of waypoints is recalculation when the user deviates from the route. - * (In most use cases, a route will have only two waypoints, but more complex use cases - * may have multiple intervening points that are visited along the route.) - * This list is updated as the user advances through the route. - */ remainingWaypoints: [Waypoint], - /** - * The trip progress includes information that is useful for showing the - * user's progress along the full navigation trip, the route and its components. - */ progress: TripProgress, - /** - * The route deviation status: is the user following the route or not? - */ deviation: RouteDeviation, - /** - * The visual instruction that should be displayed in the user interface. - */ visualInstruction: VisualInstruction?, - /** - * The most recent spoken instruction that should be synthesized using TTS. - * - * Note it is the responsibility of the platform layer to ensure that utterances are not synthesized multiple times. This property simply reports the current spoken instruction. - */ spokenInstruction: SpokenInstruction?) + case navigating( + /** + * The closest coordinate index on the line string to the snapped location. + */ currentGeometryIndex: Int64, + /** + * A location on the line string that + */ snappedUserLocation: UserLocation, + /** + * The ordered list of steps that remain in the trip. + * + * The step at the front of the list is always the current step. + * We currently assume that you cannot move backward to a previous step. + */ remainingSteps: [RouteStep], + /** + * Remaining waypoints to visit on the route. + * + * The waypoint at the front of the list is always the *next* waypoint "goal." + * Unlike the current step, there is no value in tracking the "current" waypoint, + * as the main use of waypoints is recalculation when the user deviates from the route. + * (In most use cases, a route will have only two waypoints, but more complex use cases + * may have multiple intervening points that are visited along the route.) + * This list is updated as the user advances through the route. + */ remainingWaypoints: [Waypoint], + /** + * The trip progress includes information that is useful for showing the + * user's progress along the full navigation trip, the route and its components. + */ progress: TripProgress, + /** + * The route deviation status: is the user following the route or not? + */ deviation: RouteDeviation, + /** + * The visual instruction that should be displayed in the user interface. + */ visualInstruction: VisualInstruction?, + /** + * The most recent spoken instruction that should be synthesized using TTS. + * + * Note it is the responsibility of the platform layer to ensure that utterances are not synthesized multiple times. This property simply reports the current spoken instruction. + */ spokenInstruction: SpokenInstruction? + ) /** * The navigation controller has reached the end of the trip. */ @@ -3352,6 +3372,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { case 1: return .idle case 2: return try .navigating( + currentGeometryIndex: FfiConverterInt64.read(from: &buf), snappedUserLocation: FfiConverterTypeUserLocation.read(from: &buf), remainingSteps: FfiConverterSequenceTypeRouteStep.read(from: &buf), remainingWaypoints: FfiConverterSequenceTypeWaypoint.read(from: &buf), @@ -3373,6 +3394,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { writeInt(&buf, Int32(1)) case let .navigating( + currentGeometryIndex, snappedUserLocation, remainingSteps, remainingWaypoints, @@ -3382,6 +3404,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { spokenInstruction ): writeInt(&buf, Int32(2)) + FfiConverterInt64.write(currentGeometryIndex, into: &buf) FfiConverterTypeUserLocation.write(snappedUserLocation, into: &buf) FfiConverterSequenceTypeRouteStep.write(remainingSteps, into: &buf) FfiConverterSequenceTypeWaypoint.write(remainingWaypoints, into: &buf) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index e1040892..14f14a16 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -5,10 +5,8 @@ use crate::{ navigation_controller::models::TripProgress, }; use geo::{ - Closest, ClosestPoint, EuclideanDistance, HaversineDistance, HaversineLength, LineLocatePoint, - LineString, Point, + Closest, ClosestPoint, Coord, EuclideanDistance, HaversineDistance, HaversineLength, LineLocatePoint, LineString, Point }; - use crate::navigation_controller::models::{ StepAdvanceMode, StepAdvanceStatus, StepAdvanceStatus::{Advanced, EndOfRoute}, @@ -27,6 +25,32 @@ use std::time::SystemTime; #[cfg(all(test, feature = "web-time"))] use web_time::SystemTime; +/// Get the index of the closest point in the line. +pub fn index_of_closest_origin_point(location: UserLocation, line: &LineString, skip_to_index: i64) -> i64 { + if skip_to_index < 0 { + return 0; + } + + let max_index = line.coords().count() as i64 - 1; + if skip_to_index >= max_index { + return max_index; + } + + let point = Point::from(location.coordinates); + let skip_index = skip_to_index as usize; + + line.lines() + .enumerate() + .skip(skip_index) + .min_by(|(_, line1), (_, line2)| { + let dist1 = line1.euclidean_distance(&point); + let dist2 = line2.euclidean_distance(&point); + dist1.partial_cmp(&dist2).unwrap() + }) + .map(|(index, _)| index as i64) + .unwrap() +} + /// Snaps a user location to the closest point on a route line. /// /// If the location cannot be snapped (should only be possible with an invalid coordinate or geometry), @@ -385,6 +409,17 @@ pub fn calculate_trip_progress( } } +/// Convert a vector of geographic coordinates to a GeoRust LineString. +pub(crate) fn get_linestring(geometry: &Vec) -> LineString { + geometry + .iter() + .map(|coord| Coord { + x: coord.lng, + y: coord.lat, + }) + .collect() +} + #[cfg(test)] proptest! { #[test] @@ -566,6 +601,59 @@ proptest! { } } +#[cfg(test)] +mod geom_index_tests { + + use super::*; + + fn gen_line_string() -> LineString { + LineString::new(vec![ + coord!(x: 0.0, y: 0.0), + coord!(x: 1.0, y: 1.0), + coord!(x: 2.0, y: 2.0), + coord!(x: 3.0, y: 3.0), + coord!(x: 4.0, y: 4.0) + ]) + } + + fn make_user_location(lng: f64, lat: f64) -> UserLocation { + UserLocation { + coordinates: GeographicCoordinate { lng, lat }, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + speed: None, + } + } + + #[test] + fn test_geometry_index_initial() { + let location = make_user_location(1.1, 1.1); + let line = gen_line_string(); + + let index = index_of_closest_origin_point(location, &line, 0); + assert_eq!(index, 1); + } + + #[test] + fn test_geometry_index_secondary() { + let location = make_user_location(1.1, 1.1); + let line = gen_line_string(); + + let index = index_of_closest_origin_point(location, &line, 1); + assert_eq!(index, 1); + } + + #[test] + fn test_geometry_index_behind_skip() { + let location = make_user_location(1.1, 1.1); + let line = gen_line_string(); + + let index = index_of_closest_origin_point(location, &line, 2); + assert_eq!(index, 2); + } +} + // TODO: Other unit tests // - Under and over distance accuracy thresholds // - Equator and extreme latitude diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index 55efb708..618c2057 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -25,6 +25,8 @@ use serde::Serialize; use uuid::Uuid; +use crate::algorithms::get_linestring; + #[derive(Debug)] #[cfg_attr(feature = "std", derive(thiserror::Error))] #[cfg_attr(feature = "uniffi", derive(uniffi::Error))] @@ -250,6 +252,12 @@ pub struct Route { pub steps: Vec, } +impl Route { + pub(crate) fn get_linestring(&self) -> LineString { + return get_linestring(&self.geometry) + } +} + /// Helper function for getting the route as an encoded polyline. /// /// Mostly used for debugging. @@ -290,15 +298,8 @@ pub struct RouteStep { } impl RouteStep { - // TODO: Memoize or something later pub(crate) fn get_linestring(&self) -> LineString { - self.geometry - .iter() - .map(|coord| Coord { - x: coord.lng, - y: coord.lat, - }) - .collect() + return get_linestring(&self.geometry) } /// Gets the active visual instruction at a specific point along the step. diff --git a/common/ferrostar/src/navigation_controller/mod.rs b/common/ferrostar/src/navigation_controller/mod.rs index f876b231..b0df85a4 100644 --- a/common/ferrostar/src/navigation_controller/mod.rs +++ b/common/ferrostar/src/navigation_controller/mod.rs @@ -7,8 +7,7 @@ pub(crate) mod test_helpers; use crate::{ algorithms::{ - advance_step, calculate_trip_progress, should_advance_to_next_step, - snap_user_location_to_line, + advance_step, calculate_trip_progress, index_of_closest_origin_point, should_advance_to_next_step, snap_user_location_to_line }, models::{Route, UserLocation}, }; @@ -47,6 +46,9 @@ impl NavigationController { return TripState::Complete; }; + // TODO: We could move this to the Route struct or NavigationController directly to only calculate it once. + let route_line = self.route.get_linestring(); + let current_geometry_index = index_of_closest_origin_point(location, &route_line, 0); let current_step_linestring = current_route_step.get_linestring(); let snapped_user_location = snap_user_location_to_line(location, ¤t_step_linestring); let progress = calculate_trip_progress( @@ -67,6 +69,7 @@ impl NavigationController { .cloned(); TripState::Navigating { + current_geometry_index, snapped_user_location, remaining_steps: remaining_steps.clone(), // Skip the first waypoint, as it is the current one @@ -87,6 +90,7 @@ impl NavigationController { match state { TripState::Idle => TripState::Idle, TripState::Navigating { + current_geometry_index, snapped_user_location, ref remaining_steps, ref remaining_waypoints, @@ -100,6 +104,12 @@ impl NavigationController { step: current_step, linestring, } => { + let current_geometry_index = index_of_closest_origin_point( + *snapped_user_location, + &self.route.get_linestring(), + *current_geometry_index, + ); + // Apply the updates let mut remaining_steps = remaining_steps.clone(); remaining_steps.remove(0); @@ -139,6 +149,7 @@ impl NavigationController { .cloned(); TripState::Navigating { + current_geometry_index, snapped_user_location: *snapped_user_location, remaining_steps, remaining_waypoints, @@ -169,6 +180,7 @@ impl NavigationController { match state { TripState::Idle => TripState::Idle, TripState::Navigating { + current_geometry_index, ref remaining_steps, ref remaining_waypoints, deviation, @@ -188,12 +200,20 @@ impl NavigationController { let current_step_linestring = current_step.get_linestring(); let snapped_user_location = snap_user_location_to_line(location, ¤t_step_linestring); + + let current_geometry_index = index_of_closest_origin_point( + snapped_user_location, + &self.route.get_linestring(), + *current_geometry_index, + ); + let progress = calculate_trip_progress( &snapped_user_location.into(), ¤t_step_linestring, remaining_steps, ); let intermediate_state = TripState::Navigating { + current_geometry_index, snapped_user_location, remaining_steps: remaining_steps.clone(), remaining_waypoints: remaining_waypoints.clone(), @@ -217,6 +237,7 @@ impl NavigationController { } { TripState::Idle => TripState::Idle, TripState::Navigating { + current_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, @@ -244,6 +265,7 @@ impl NavigationController { .cloned(); TripState::Navigating { + current_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, diff --git a/common/ferrostar/src/navigation_controller/models.rs b/common/ferrostar/src/navigation_controller/models.rs index ca427293..eb3eafd3 100644 --- a/common/ferrostar/src/navigation_controller/models.rs +++ b/common/ferrostar/src/navigation_controller/models.rs @@ -34,6 +34,9 @@ pub enum TripState { #[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))] /// The navigation controller is actively navigating a trip. Navigating { + /// The closest coordinate index on the line string to the snapped location. + current_geometry_index: i64, + /// A location on the line string that snapped_user_location: UserLocation, /// The ordered list of steps that remain in the trip. /// From 74624fbbb769bedf7c6298cb1a7dec465db5f341 Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sat, 31 Aug 2024 16:38:24 -0700 Subject: [PATCH 02/16] Added nearest coordinate index for polyline --- Package.swift | 2 +- common/ferrostar/src/algorithms.rs | 23 +++++++++++-------- common/ferrostar/src/models.rs | 4 ++-- .../src/navigation_controller/mod.rs | 5 ++-- .../src/navigation_controller/models.rs | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index a2343cdb..8275110f 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ if useLocalFramework { path: "./common/target/ios/libferrostar-rs.xcframework" ) } else { - let releaseTag = "0.9.1" + let releaseTag = "0.9.2" let releaseChecksum = "d06ba13bd12262b91ecec20a80c96649a8507a43b2d041086ca04c6bcee2ba2f" binaryTarget = .binaryTarget( name: "FerrostarCoreRS", diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index 14f14a16..a95d9553 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -1,15 +1,16 @@ //! Common spatial algorithms which are useful for navigation. +use crate::navigation_controller::models::{ + StepAdvanceMode, StepAdvanceStatus, + StepAdvanceStatus::{Advanced, EndOfRoute}, +}; use crate::{ models::{GeographicCoordinate, RouteStep, UserLocation}, navigation_controller::models::TripProgress, }; use geo::{ - Closest, ClosestPoint, Coord, EuclideanDistance, HaversineDistance, HaversineLength, LineLocatePoint, LineString, Point -}; -use crate::navigation_controller::models::{ - StepAdvanceMode, StepAdvanceStatus, - StepAdvanceStatus::{Advanced, EndOfRoute}, + Closest, ClosestPoint, Coord, EuclideanDistance, HaversineDistance, HaversineLength, + LineLocatePoint, LineString, Point, }; #[cfg(test)] @@ -25,12 +26,16 @@ use std::time::SystemTime; #[cfg(all(test, feature = "web-time"))] use web_time::SystemTime; -/// Get the index of the closest point in the line. -pub fn index_of_closest_origin_point(location: UserLocation, line: &LineString, skip_to_index: i64) -> i64 { +/// Get the index of the closest point in the line. +pub fn index_of_closest_origin_point( + location: UserLocation, + line: &LineString, + skip_to_index: i64, +) -> i64 { if skip_to_index < 0 { return 0; } - + let max_index = line.coords().count() as i64 - 1; if skip_to_index >= max_index { return max_index; @@ -612,7 +617,7 @@ mod geom_index_tests { coord!(x: 1.0, y: 1.0), coord!(x: 2.0, y: 2.0), coord!(x: 3.0, y: 3.0), - coord!(x: 4.0, y: 4.0) + coord!(x: 4.0, y: 4.0), ]) } diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index 618c2057..ba753b36 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -254,7 +254,7 @@ pub struct Route { impl Route { pub(crate) fn get_linestring(&self) -> LineString { - return get_linestring(&self.geometry) + return get_linestring(&self.geometry); } } @@ -299,7 +299,7 @@ pub struct RouteStep { impl RouteStep { pub(crate) fn get_linestring(&self) -> LineString { - return get_linestring(&self.geometry) + return get_linestring(&self.geometry); } /// Gets the active visual instruction at a specific point along the step. diff --git a/common/ferrostar/src/navigation_controller/mod.rs b/common/ferrostar/src/navigation_controller/mod.rs index b0df85a4..f9e926b9 100644 --- a/common/ferrostar/src/navigation_controller/mod.rs +++ b/common/ferrostar/src/navigation_controller/mod.rs @@ -7,7 +7,8 @@ pub(crate) mod test_helpers; use crate::{ algorithms::{ - advance_step, calculate_trip_progress, index_of_closest_origin_point, should_advance_to_next_step, snap_user_location_to_line + advance_step, calculate_trip_progress, index_of_closest_origin_point, + should_advance_to_next_step, snap_user_location_to_line, }, models::{Route, UserLocation}, }; @@ -46,7 +47,7 @@ impl NavigationController { return TripState::Complete; }; - // TODO: We could move this to the Route struct or NavigationController directly to only calculate it once. + // TODO: We could move this to the Route struct or NavigationController directly to only calculate it once. let route_line = self.route.get_linestring(); let current_geometry_index = index_of_closest_origin_point(location, &route_line, 0); let current_step_linestring = current_route_step.get_linestring(); diff --git a/common/ferrostar/src/navigation_controller/models.rs b/common/ferrostar/src/navigation_controller/models.rs index eb3eafd3..ba11e8bf 100644 --- a/common/ferrostar/src/navigation_controller/models.rs +++ b/common/ferrostar/src/navigation_controller/models.rs @@ -36,7 +36,7 @@ pub enum TripState { Navigating { /// The closest coordinate index on the line string to the snapped location. current_geometry_index: i64, - /// A location on the line string that + /// A location on the line string that snapped_user_location: UserLocation, /// The ordered list of steps that remain in the trip. /// From 19ac0b4d6e13c964b2d6013c358ab97578846765 Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sat, 31 Aug 2024 16:49:47 -0700 Subject: [PATCH 03/16] Added nearest coordinate index for polyline --- Package.swift | 2 +- android/build.gradle | 2 +- common/Cargo.lock | 2 +- common/ferrostar/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 8275110f..6b71659b 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ if useLocalFramework { path: "./common/target/ios/libferrostar-rs.xcframework" ) } else { - let releaseTag = "0.9.2" + let releaseTag = "0.10.0" let releaseChecksum = "d06ba13bd12262b91ecec20a80c96649a8507a43b2d041086ca04c6bcee2ba2f" binaryTarget = .binaryTarget( name: "FerrostarCoreRS", diff --git a/android/build.gradle b/android/build.gradle index f0e01127..8b847e67 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -12,5 +12,5 @@ plugins { allprojects { group = "com.stadiamaps.ferrostar" - version = "0.9.2" + version = "0.10.0" } diff --git a/common/Cargo.lock b/common/Cargo.lock index 1c304bf5..a94a777e 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -328,7 +328,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "ferrostar" -version = "0.9.2" +version = "0.10.0" dependencies = [ "assert-json-diff", "geo", diff --git a/common/ferrostar/Cargo.toml b/common/ferrostar/Cargo.toml index ec7db0b5..ebda79eb 100644 --- a/common/ferrostar/Cargo.toml +++ b/common/ferrostar/Cargo.toml @@ -2,7 +2,7 @@ lints.workspace = true [package] name = "ferrostar" -version = "0.9.2" +version = "0.10.0" readme = "README.md" description = "The core of modern turn-by-turn navigation." keywords = ["navigation", "routing", "valhalla", "osrm"] From 19d3831217140c0ce1d3f7107b2660f77f11e245 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 1 Sep 2024 10:44:48 +0900 Subject: [PATCH 04/16] Change current_geometry_index to an unsigned type --- apple/Sources/UniFFI/ferrostar.swift | 16 ++++++++-------- common/ferrostar/src/algorithms.rs | 16 ++++++---------- .../src/navigation_controller/models.rs | 2 +- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index 7015544e..3f0f4a79 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -404,15 +404,15 @@ private struct FfiConverterUInt32: FfiConverterPrimitive { } } -private struct FfiConverterInt64: FfiConverterPrimitive { - typealias FfiType = Int64 - typealias SwiftType = Int64 +private struct FfiConverterUInt64: FfiConverterPrimitive { + typealias FfiType = UInt64 + typealias SwiftType = UInt64 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int64 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 { try lift(readInt(&buf)) } - public static func write(_ value: Int64, into buf: inout [UInt8]) { + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } } @@ -3321,7 +3321,7 @@ public enum TripState { case navigating( /** * The closest coordinate index on the line string to the snapped location. - */ currentGeometryIndex: Int64, + */ currentGeometryIndex: UInt64, /** * A location on the line string that */ snappedUserLocation: UserLocation, @@ -3372,7 +3372,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { case 1: return .idle case 2: return try .navigating( - currentGeometryIndex: FfiConverterInt64.read(from: &buf), + currentGeometryIndex: FfiConverterUInt64.read(from: &buf), snappedUserLocation: FfiConverterTypeUserLocation.read(from: &buf), remainingSteps: FfiConverterSequenceTypeRouteStep.read(from: &buf), remainingWaypoints: FfiConverterSequenceTypeWaypoint.read(from: &buf), @@ -3404,7 +3404,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { spokenInstruction ): writeInt(&buf, Int32(2)) - FfiConverterInt64.write(currentGeometryIndex, into: &buf) + FfiConverterUInt64.write(currentGeometryIndex, into: &buf) FfiConverterTypeUserLocation.write(snappedUserLocation, into: &buf) FfiConverterSequenceTypeRouteStep.write(remainingSteps, into: &buf) FfiConverterSequenceTypeWaypoint.write(remainingWaypoints, into: &buf) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index a95d9553..71ebd846 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -30,15 +30,11 @@ use web_time::SystemTime; pub fn index_of_closest_origin_point( location: UserLocation, line: &LineString, - skip_to_index: i64, -) -> i64 { - if skip_to_index < 0 { - return 0; - } - - let max_index = line.coords().count() as i64 - 1; - if skip_to_index >= max_index { - return max_index; + skip_to_index: u64, +) -> u64 { + let max_index = line.coords().count() - 1; + if skip_to_index >= max_index as u64 { + return max_index as u64; } let point = Point::from(location.coordinates); @@ -52,7 +48,7 @@ pub fn index_of_closest_origin_point( let dist2 = line2.euclidean_distance(&point); dist1.partial_cmp(&dist2).unwrap() }) - .map(|(index, _)| index as i64) + .map(|(index, _)| index as u64) .unwrap() } diff --git a/common/ferrostar/src/navigation_controller/models.rs b/common/ferrostar/src/navigation_controller/models.rs index ba11e8bf..40feb56f 100644 --- a/common/ferrostar/src/navigation_controller/models.rs +++ b/common/ferrostar/src/navigation_controller/models.rs @@ -35,7 +35,7 @@ pub enum TripState { /// The navigation controller is actively navigating a trip. Navigating { /// The closest coordinate index on the line string to the snapped location. - current_geometry_index: i64, + current_geometry_index: u64, /// A location on the line string that snapped_user_location: UserLocation, /// The ordered list of steps that remain in the trip. From dcc050558b805f3ab325a8879e8ac35e3ead3b86 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 1 Sep 2024 10:46:05 +0900 Subject: [PATCH 05/16] Minor style cleanup --- common/ferrostar/src/algorithms.rs | 4 ++-- common/ferrostar/src/models.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index 71ebd846..fbc616d6 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -410,8 +410,8 @@ pub fn calculate_trip_progress( } } -/// Convert a vector of geographic coordinates to a GeoRust LineString. -pub(crate) fn get_linestring(geometry: &Vec) -> LineString { +/// Convert a vector of geographic coordinates to a [`LineString`]. +pub(crate) fn get_linestring(geometry: &[GeographicCoordinate]) -> LineString { geometry .iter() .map(|coord| Coord { diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index ba753b36..23b76110 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -254,7 +254,7 @@ pub struct Route { impl Route { pub(crate) fn get_linestring(&self) -> LineString { - return get_linestring(&self.geometry); + get_linestring(&self.geometry) } } @@ -299,7 +299,7 @@ pub struct RouteStep { impl RouteStep { pub(crate) fn get_linestring(&self) -> LineString { - return get_linestring(&self.geometry); + get_linestring(&self.geometry) } /// Gets the active visual instruction at a specific point along the step. From dd0cf859c869900e1c89dce6143ea101a548ccd5 Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sat, 31 Aug 2024 20:22:28 -0700 Subject: [PATCH 06/16] Added nearest coordinate index for polyline --- common/ferrostar/src/algorithms.rs | 28 ++++++++----------- .../src/navigation_controller/mod.rs | 4 +-- .../src/navigation_controller/models.rs | 2 +- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index a95d9553..551e6d8f 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -27,20 +27,15 @@ use std::time::SystemTime; use web_time::SystemTime; /// Get the index of the closest point in the line. +/// +/// * `location` - The user's location. +/// * `line` - The route line. +/// * `skip_to_index` - The index to skip forward to. This allows us to evaluate the route from the last index forward. pub fn index_of_closest_origin_point( location: UserLocation, line: &LineString, - skip_to_index: i64, -) -> i64 { - if skip_to_index < 0 { - return 0; - } - - let max_index = line.coords().count() as i64 - 1; - if skip_to_index >= max_index { - return max_index; - } - + skip_to_index: u64, +) -> Option { let point = Point::from(location.coordinates); let skip_index = skip_to_index as usize; @@ -50,10 +45,9 @@ pub fn index_of_closest_origin_point( .min_by(|(_, line1), (_, line2)| { let dist1 = line1.euclidean_distance(&point); let dist2 = line2.euclidean_distance(&point); - dist1.partial_cmp(&dist2).unwrap() + dist1.total_cmp(&dist2) }) - .map(|(index, _)| index as i64) - .unwrap() + .map(|(index, _)| index as u64) } /// Snaps a user location to the closest point on a route line. @@ -637,7 +631,7 @@ mod geom_index_tests { let line = gen_line_string(); let index = index_of_closest_origin_point(location, &line, 0); - assert_eq!(index, 1); + assert_eq!(index, Some(1)); } #[test] @@ -646,7 +640,7 @@ mod geom_index_tests { let line = gen_line_string(); let index = index_of_closest_origin_point(location, &line, 1); - assert_eq!(index, 1); + assert_eq!(index, Some(1)); } #[test] @@ -655,7 +649,7 @@ mod geom_index_tests { let line = gen_line_string(); let index = index_of_closest_origin_point(location, &line, 2); - assert_eq!(index, 2); + assert_eq!(index, Some(2)); } } diff --git a/common/ferrostar/src/navigation_controller/mod.rs b/common/ferrostar/src/navigation_controller/mod.rs index f9e926b9..3a7fa351 100644 --- a/common/ferrostar/src/navigation_controller/mod.rs +++ b/common/ferrostar/src/navigation_controller/mod.rs @@ -108,7 +108,7 @@ impl NavigationController { let current_geometry_index = index_of_closest_origin_point( *snapped_user_location, &self.route.get_linestring(), - *current_geometry_index, + current_geometry_index.unwrap_or(0), ); // Apply the updates @@ -205,7 +205,7 @@ impl NavigationController { let current_geometry_index = index_of_closest_origin_point( snapped_user_location, &self.route.get_linestring(), - *current_geometry_index, + current_geometry_index.unwrap_or(0), ); let progress = calculate_trip_progress( diff --git a/common/ferrostar/src/navigation_controller/models.rs b/common/ferrostar/src/navigation_controller/models.rs index ba11e8bf..5d3fc2c8 100644 --- a/common/ferrostar/src/navigation_controller/models.rs +++ b/common/ferrostar/src/navigation_controller/models.rs @@ -35,7 +35,7 @@ pub enum TripState { /// The navigation controller is actively navigating a trip. Navigating { /// The closest coordinate index on the line string to the snapped location. - current_geometry_index: i64, + current_geometry_index: Option, /// A location on the line string that snapped_user_location: UserLocation, /// The ordered list of steps that remain in the trip. From 75be83f3183d458431d10936e1638e32487a6d2c Mon Sep 17 00:00:00 2001 From: Archdoog Date: Sun, 1 Sep 2024 03:32:08 +0000 Subject: [PATCH 07/16] Apply automatic changes --- apple/Sources/UniFFI/ferrostar.swift | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index 3f0f4a79..357a24a4 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -3321,7 +3321,7 @@ public enum TripState { case navigating( /** * The closest coordinate index on the line string to the snapped location. - */ currentGeometryIndex: UInt64, + */ currentGeometryIndex: UInt64?, /** * A location on the line string that */ snappedUserLocation: UserLocation, @@ -3372,7 +3372,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { case 1: return .idle case 2: return try .navigating( - currentGeometryIndex: FfiConverterUInt64.read(from: &buf), + currentGeometryIndex: FfiConverterOptionUInt64.read(from: &buf), snappedUserLocation: FfiConverterTypeUserLocation.read(from: &buf), remainingSteps: FfiConverterSequenceTypeRouteStep.read(from: &buf), remainingWaypoints: FfiConverterSequenceTypeWaypoint.read(from: &buf), @@ -3404,7 +3404,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { spokenInstruction ): writeInt(&buf, Int32(2)) - FfiConverterUInt64.write(currentGeometryIndex, into: &buf) + FfiConverterOptionUInt64.write(currentGeometryIndex, into: &buf) FfiConverterTypeUserLocation.write(snappedUserLocation, into: &buf) FfiConverterSequenceTypeRouteStep.write(remainingSteps, into: &buf) FfiConverterSequenceTypeWaypoint.write(remainingWaypoints, into: &buf) @@ -3504,6 +3504,27 @@ private struct FfiConverterOptionUInt16: FfiConverterRustBuffer { } } +private struct FfiConverterOptionUInt64: FfiConverterRustBuffer { + typealias SwiftType = UInt64? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterUInt64.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 FfiConverterUInt64.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + private struct FfiConverterOptionDouble: FfiConverterRustBuffer { typealias SwiftType = Double? From 2e1b9e915fb4ca2aecbda3d1aa394c92b23048cf Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sun, 1 Sep 2024 08:31:38 -0700 Subject: [PATCH 08/16] Added nearest coordinate index for polyline --- .../com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt index 7ec7c9da..74d9b368 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt @@ -33,7 +33,7 @@ fun NavigationState.Companion.pedestrianExample(): NavigationState { return NavigationState( tripState = TripState.Navigating( - currentGeometryIndex = 0L, + currentGeometryIndex = 0u, snappedUserLocation = UserLocation.pedestrianExample(), remainingSteps = listOf(), remainingWaypoints = listOf(), From be3eff1ebb27962b4669f7716d60accd21ea6fe9 Mon Sep 17 00:00:00 2001 From: Jacob Fielding Date: Sun, 1 Sep 2024 13:59:33 -0700 Subject: [PATCH 09/16] Ran cargo fmt --- common/ferrostar/src/algorithms.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index 8fc62953..de024c10 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -27,7 +27,7 @@ use std::time::SystemTime; use web_time::SystemTime; /// Get the index of the closest point in the line. -/// +/// /// * `location` - The user's location. /// * `line` - The route line. /// * `skip_to_index` - The index to skip forward to. This allows us to evaluate the route from the last index forward. From 83cd86c845890e151d5b30396fcc1f3982e80f8f Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 12:39:43 +0900 Subject: [PATCH 10/16] Revise step geometry index logic based on discussion --- common/ferrostar/src/algorithms.rs | 77 +++++++++++-------- .../src/navigation_controller/mod.rs | 34 ++++---- .../src/navigation_controller/models.rs | 6 +- 3 files changed, 63 insertions(+), 54 deletions(-) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index de024c10..9f9188db 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -26,22 +26,25 @@ use std::time::SystemTime; #[cfg(all(test, feature = "web-time"))] use web_time::SystemTime; -/// Get the index of the closest point in the line. +/// Get the index of the closest *segment* to the user's location within a [`LineString`]. /// -/// * `location` - The user's location. -/// * `line` - The route line. -/// * `skip_to_index` - The index to skip forward to. This allows us to evaluate the route from the last index forward. -pub fn index_of_closest_origin_point( - location: UserLocation, - line: &LineString, - skip_to_index: u64, -) -> Option { +/// A [`LineString`] is a set of points (ex: representing the geometry of a maneuver), +/// and this function identifies which segment a point is closest to, +/// so that you can correctly match attributes along a maneuver. +/// +/// In the case of a location being exactly on the boundary +/// (unlikely in the real world, but quite possible in simulations), +/// the *first* segment of equal distance to the location will be matched. +/// +/// The maximum value returned is *one less than* the last coordinate index into `line`. +/// Returns [`None`] if `line` contains fewer than two coordinates. +pub fn index_of_closest_segment_origin(location: UserLocation, line: &LineString) -> Option { let point = Point::from(location.coordinates); - let skip_index = skip_to_index as usize; line.lines() + // Iterate through all segments of the line .enumerate() - .skip(skip_index) + // Find the line segment closest to the user's location .min_by(|(_, line1), (_, line2)| { let dist1 = line1.euclidean_distance(&point); let dist2 = line2.euclidean_distance(&point); @@ -605,15 +608,13 @@ mod geom_index_tests { use super::*; - fn gen_line_string() -> LineString { - LineString::new(vec![ - coord!(x: 0.0, y: 0.0), - coord!(x: 1.0, y: 1.0), - coord!(x: 2.0, y: 2.0), - coord!(x: 3.0, y: 3.0), - coord!(x: 4.0, y: 4.0), - ]) - } + static COORDS: [Coord; 5] = [ + coord!(x: 0.0, y: 0.0), + coord!(x: 1.0, y: 1.0), + coord!(x: 2.0, y: 2.0), + coord!(x: 3.0, y: 3.0), + coord!(x: 4.0, y: 4.0), + ]; fn make_user_location(lng: f64, lat: f64) -> UserLocation { UserLocation { @@ -626,30 +627,40 @@ mod geom_index_tests { } #[test] - fn test_geometry_index_initial() { - let location = make_user_location(1.1, 1.1); - let line = gen_line_string(); + fn test_geometry_index_at_point() { + let line = LineString::new(COORDS.to_vec()); - let index = index_of_closest_origin_point(location, &line, 0); + // Exactly at a point (NB: does not advance until we move *past* the transition point + // and are closer to the next line segment!) + let index = index_of_closest_segment_origin(make_user_location(2.0, 2.0), &line); assert_eq!(index, Some(1)); } #[test] - fn test_geometry_index_secondary() { - let location = make_user_location(1.1, 1.1); - let line = gen_line_string(); + fn test_geometry_index_near_point() { + let line = LineString::new(COORDS.to_vec()); - let index = index_of_closest_origin_point(location, &line, 1); + // Very close to an origin point + let index = index_of_closest_segment_origin(make_user_location(1.1, 1.1), &line); + assert_eq!(index, Some(1)); + + // Very close to the next point, but not yet "passing" to the next segment! + let index = index_of_closest_segment_origin(make_user_location(1.99, 1.99), &line); assert_eq!(index, Some(1)); } #[test] - fn test_geometry_index_behind_skip() { - let location = make_user_location(1.1, 1.1); - let line = gen_line_string(); + fn test_geometry_index_far_from_point() { + let line = LineString::new(COORDS.to_vec()); + + // "Before" the start + let index = index_of_closest_segment_origin(make_user_location(-1.1, -1.1), &line); + assert_eq!(index, Some(0)); - let index = index_of_closest_origin_point(location, &line, 2); - assert_eq!(index, Some(2)); + // "Past" the end (NB: the last index in the list of coords is 4, + // but we can never advance past n-1) + let index = index_of_closest_segment_origin(make_user_location(10.0, 10.0), &line); + assert_eq!(index, Some(3)); } } diff --git a/common/ferrostar/src/navigation_controller/mod.rs b/common/ferrostar/src/navigation_controller/mod.rs index 3a7fa351..dc9803de 100644 --- a/common/ferrostar/src/navigation_controller/mod.rs +++ b/common/ferrostar/src/navigation_controller/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod test_helpers; use crate::{ algorithms::{ - advance_step, calculate_trip_progress, index_of_closest_origin_point, + advance_step, calculate_trip_progress, index_of_closest_segment_origin, should_advance_to_next_step, snap_user_location_to_line, }, models::{Route, UserLocation}, @@ -48,10 +48,10 @@ impl NavigationController { }; // TODO: We could move this to the Route struct or NavigationController directly to only calculate it once. - let route_line = self.route.get_linestring(); - let current_geometry_index = index_of_closest_origin_point(location, &route_line, 0); let current_step_linestring = current_route_step.get_linestring(); let snapped_user_location = snap_user_location_to_line(location, ¤t_step_linestring); + let current_step_geometry_index = + index_of_closest_segment_origin(snapped_user_location, ¤t_step_linestring); let progress = calculate_trip_progress( &snapped_user_location.into(), ¤t_step_linestring, @@ -70,7 +70,7 @@ impl NavigationController { .cloned(); TripState::Navigating { - current_geometry_index, + current_step_geometry_index, snapped_user_location, remaining_steps: remaining_steps.clone(), // Skip the first waypoint, as it is the current one @@ -91,7 +91,6 @@ impl NavigationController { match state { TripState::Idle => TripState::Idle, TripState::Navigating { - current_geometry_index, snapped_user_location, ref remaining_steps, ref remaining_waypoints, @@ -105,12 +104,6 @@ impl NavigationController { step: current_step, linestring, } => { - let current_geometry_index = index_of_closest_origin_point( - *snapped_user_location, - &self.route.get_linestring(), - current_geometry_index.unwrap_or(0), - ); - // Apply the updates let mut remaining_steps = remaining_steps.clone(); remaining_steps.remove(0); @@ -142,6 +135,11 @@ impl NavigationController { &remaining_steps, ); + let current_step_geometry_index = index_of_closest_segment_origin( + *snapped_user_location, + &self.route.get_linestring(), + ); + let visual_instruction = current_step .get_active_visual_instruction(progress.distance_to_next_maneuver) .cloned(); @@ -150,7 +148,7 @@ impl NavigationController { .cloned(); TripState::Navigating { - current_geometry_index, + current_step_geometry_index, snapped_user_location: *snapped_user_location, remaining_steps, remaining_waypoints, @@ -181,7 +179,6 @@ impl NavigationController { match state { TripState::Idle => TripState::Idle, TripState::Navigating { - current_geometry_index, ref remaining_steps, ref remaining_waypoints, deviation, @@ -202,10 +199,9 @@ impl NavigationController { let snapped_user_location = snap_user_location_to_line(location, ¤t_step_linestring); - let current_geometry_index = index_of_closest_origin_point( + let current_step_geometry_index = index_of_closest_segment_origin( snapped_user_location, - &self.route.get_linestring(), - current_geometry_index.unwrap_or(0), + ¤t_step_linestring, ); let progress = calculate_trip_progress( @@ -214,7 +210,7 @@ impl NavigationController { remaining_steps, ); let intermediate_state = TripState::Navigating { - current_geometry_index, + current_step_geometry_index, snapped_user_location, remaining_steps: remaining_steps.clone(), remaining_waypoints: remaining_waypoints.clone(), @@ -238,7 +234,7 @@ impl NavigationController { } { TripState::Idle => TripState::Idle, TripState::Navigating { - current_geometry_index, + current_step_geometry_index: current_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, @@ -266,7 +262,7 @@ impl NavigationController { .cloned(); TripState::Navigating { - current_geometry_index, + current_step_geometry_index: current_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, diff --git a/common/ferrostar/src/navigation_controller/models.rs b/common/ferrostar/src/navigation_controller/models.rs index 5d3fc2c8..6679acbe 100644 --- a/common/ferrostar/src/navigation_controller/models.rs +++ b/common/ferrostar/src/navigation_controller/models.rs @@ -34,8 +34,10 @@ pub enum TripState { #[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))] /// The navigation controller is actively navigating a trip. Navigating { - /// The closest coordinate index on the line string to the snapped location. - current_geometry_index: Option, + /// The index of the closest coordinate to the user's snapped location. + /// + /// This index is relative to the *current* [`RouteStep`]'s geometry. + current_step_geometry_index: Option, /// A location on the line string that snapped_user_location: UserLocation, /// The ordered list of steps that remain in the trip. From a4e3bd45fb5edbe0d23aa761a2b69c39d9d2caab Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 12:40:13 +0900 Subject: [PATCH 11/16] Clean up some unrelated linter warnings --- common/ferrostar/src/models.rs | 2 +- common/ferrostar/src/routing_adapters/error.rs | 5 +++++ common/ferrostar/src/routing_adapters/osrm/mod.rs | 12 ++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index 23b76110..bb1504a4 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -175,7 +175,7 @@ pub struct Speed { pub accuracy: Option, } -#[cfg(any(test, feature = "wasm-bindgen"))] +#[cfg(feature = "wasm-bindgen")] mod system_time_format { use serde::{self, Deserialize, Deserializer, Serializer}; diff --git a/common/ferrostar/src/routing_adapters/error.rs b/common/ferrostar/src/routing_adapters/error.rs index 5266dd6a..7439ee2d 100644 --- a/common/ferrostar/src/routing_adapters/error.rs +++ b/common/ferrostar/src/routing_adapters/error.rs @@ -57,6 +57,11 @@ pub enum ParsingError { // TODO: Unable to find route and other common errors #[cfg_attr(feature = "std", error("Failed to parse route response: {error}."))] ParseError { error: String }, + #[cfg_attr( + feature = "std", + error("Routing adapter returned an unexpected status code: {code}.") + )] + InvalidStatusCode { code: String }, #[cfg_attr( feature = "std", error("An unknown error parsing a response was raised in foreign code.") diff --git a/common/ferrostar/src/routing_adapters/osrm/mod.rs b/common/ferrostar/src/routing_adapters/osrm/mod.rs index 20ab84e0..040348b8 100644 --- a/common/ferrostar/src/routing_adapters/osrm/mod.rs +++ b/common/ferrostar/src/routing_adapters/osrm/mod.rs @@ -38,10 +38,14 @@ impl RouteResponseParser for OsrmResponseParser { fn parse_response(&self, response: Vec) -> Result, ParsingError> { let res: RouteResponse = serde_json::from_slice(&response)?; - res.routes - .iter() - .map(|route| Route::from_osrm(route, &res.waypoints, self.polyline_precision)) - .collect::, _>>() + if res.code == "Ok" { + res.routes + .iter() + .map(|route| Route::from_osrm(route, &res.waypoints, self.polyline_precision)) + .collect::, _>>() + } else { + Err(ParsingError::InvalidStatusCode { code: res.code }) + } } } From 9625f2cc6c0816ac60f657edf7ee3a6891d3b9b0 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 14:42:36 +0900 Subject: [PATCH 12/16] Improve robustness of tests --- common/ferrostar/src/algorithms.rs | 77 +++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/common/ferrostar/src/algorithms.rs b/common/ferrostar/src/algorithms.rs index 9f9188db..ae35f1bb 100644 --- a/common/ferrostar/src/algorithms.rs +++ b/common/ferrostar/src/algorithms.rs @@ -17,12 +17,11 @@ use geo::{ use { crate::navigation_controller::test_helpers::gen_dummy_route_step, geo::{coord, point}, - proptest::prelude::*, + proptest::{collection::vec, prelude::*}, }; #[cfg(all(test, feature = "std", not(feature = "web-time")))] use std::time::SystemTime; - #[cfg(all(test, feature = "web-time"))] use web_time::SystemTime; @@ -46,6 +45,7 @@ pub fn index_of_closest_segment_origin(location: UserLocation, line: &LineString .enumerate() // Find the line segment closest to the user's location .min_by(|(_, line1), (_, line2)| { + // Note: lines don't implement haversine distances let dist1 = line1.euclidean_distance(&point); let dist2 = line2.euclidean_distance(&point); dist1.total_cmp(&dist2) @@ -422,6 +422,26 @@ pub(crate) fn get_linestring(geometry: &[GeographicCoordinate]) -> LineString { .collect() } +#[cfg(test)] +/// Creates a user location at the given coordinates, +/// with all other values set to defaults or (in the case of the timestamp), the current time. +fn make_user_location(lng: f64, lat: f64) -> UserLocation { + UserLocation { + coordinates: GeographicCoordinate { lng, lat }, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + speed: None, + } +} + +#[cfg(test)] +prop_compose! { + fn arb_coord()(x in -180f64..180f64, y in -90f64..90f64) -> Coord { + coord! {x: x, y: y} + } +} + #[cfg(test)] proptest! { #[test] @@ -601,6 +621,49 @@ proptest! { prop_assert_eq!(progress.distance_remaining, 0f64); prop_assert_eq!(progress.duration_remaining, 0f64); } + + #[test] + fn test_geometry_index_empty_linestring( + x: f64, y: f64, + ) { + let index = index_of_closest_segment_origin(make_user_location(x, y), &LineString::new(vec![])); + prop_assert_eq!(index, None); + } + + #[test] + fn test_geometry_index_single_coord_invalid_linestring( + x: f64, y: f64, + ) { + let index = index_of_closest_segment_origin(make_user_location(x, y), &LineString::new(vec![coord! { x: x, y: y }])); + prop_assert_eq!(index, None); + } + + #[test] + fn test_geometry_index_is_some_for_reasonable_linestrings( + x in -180f64..180f64, y in -90f64..90f64, + coords in vec(arb_coord(), 2..500) + ) { + let index = index_of_closest_segment_origin(make_user_location(x, y), &LineString::new(coords)); + + // There are at least two points, so we have a valid segment + prop_assert_ne!(index, None); + } + + #[test] + fn test_geometry_index_at_terminal_coord( + coords in vec(arb_coord(), 2..500) + ) { + let last_coord = coords.last().unwrap(); + let coord_len = coords.len(); + let user_location = make_user_location(last_coord.x, last_coord.y); + let index = index_of_closest_segment_origin(user_location, &LineString::new(coords)); + + // There are at least two points, so we have a valid segment + prop_assert_ne!(index, None); + let index = index.unwrap(); + // We should never be able to go past the origin of the final pair + prop_assert!(index < (coord_len - 1) as u64); + } } #[cfg(test)] @@ -616,16 +679,6 @@ mod geom_index_tests { coord!(x: 4.0, y: 4.0), ]; - fn make_user_location(lng: f64, lat: f64) -> UserLocation { - UserLocation { - coordinates: GeographicCoordinate { lng, lat }, - horizontal_accuracy: 0.0, - course_over_ground: None, - timestamp: SystemTime::now(), - speed: None, - } - } - #[test] fn test_geometry_index_at_point() { let line = LineString::new(COORDS.to_vec()); From a1ea8afc720f199af193632ab5a7b2edd8df26fb Mon Sep 17 00:00:00 2001 From: ianthetechie Date: Tue, 3 Sep 2024 05:49:50 +0000 Subject: [PATCH 13/16] Apply automatic changes --- apple/Sources/UniFFI/ferrostar.swift | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index 357a24a4..690fb525 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -2900,6 +2900,7 @@ extension ModelError: Foundation.LocalizedError { public enum ParsingError { case ParseError(error: String) + case InvalidStatusCode(code: String) case UnknownError } @@ -2912,9 +2913,10 @@ public struct FfiConverterTypeParsingError: FfiConverterRustBuffer { case 1: return try .ParseError( error: FfiConverterString.read(from: &buf) ) - - case 2: return .UnknownError - + case 2: return try .InvalidStatusCode( + code: FfiConverterString.read(from: &buf) + ) + case 3: return .UnknownError default: throw UniffiInternalError.unexpectedEnumCase } } @@ -2925,8 +2927,12 @@ public struct FfiConverterTypeParsingError: FfiConverterRustBuffer { writeInt(&buf, Int32(1)) FfiConverterString.write(error, into: &buf) - case .UnknownError: + case let .InvalidStatusCode(code): writeInt(&buf, Int32(2)) + FfiConverterString.write(code, into: &buf) + + case .UnknownError: + writeInt(&buf, Int32(3)) } } } @@ -3320,8 +3326,10 @@ public enum TripState { */ case navigating( /** - * The closest coordinate index on the line string to the snapped location. - */ currentGeometryIndex: UInt64?, + * The index of the closest coordinate to the user's snapped location. + * + * This index is relative to the *current* [`RouteStep`]'s geometry. + */ currentStepGeometryIndex: UInt64?, /** * A location on the line string that */ snappedUserLocation: UserLocation, @@ -3372,7 +3380,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { case 1: return .idle case 2: return try .navigating( - currentGeometryIndex: FfiConverterOptionUInt64.read(from: &buf), + currentStepGeometryIndex: FfiConverterOptionUInt64.read(from: &buf), snappedUserLocation: FfiConverterTypeUserLocation.read(from: &buf), remainingSteps: FfiConverterSequenceTypeRouteStep.read(from: &buf), remainingWaypoints: FfiConverterSequenceTypeWaypoint.read(from: &buf), @@ -3394,7 +3402,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { writeInt(&buf, Int32(1)) case let .navigating( - currentGeometryIndex, + currentStepGeometryIndex, snappedUserLocation, remainingSteps, remainingWaypoints, @@ -3404,7 +3412,7 @@ public struct FfiConverterTypeTripState: FfiConverterRustBuffer { spokenInstruction ): writeInt(&buf, Int32(2)) - FfiConverterOptionUInt64.write(currentGeometryIndex, into: &buf) + FfiConverterOptionUInt64.write(currentStepGeometryIndex, into: &buf) FfiConverterTypeUserLocation.write(snappedUserLocation, into: &buf) FfiConverterSequenceTypeRouteStep.write(remainingSteps, into: &buf) FfiConverterSequenceTypeWaypoint.write(remainingWaypoints, into: &buf) From a71fd0ac75deae8f9078f5ea02bb305d6a0b7432 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 15:01:57 +0900 Subject: [PATCH 14/16] Cleanup --- .../ferrostar/src/navigation_controller/mod.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/common/ferrostar/src/navigation_controller/mod.rs b/common/ferrostar/src/navigation_controller/mod.rs index dc9803de..734e37c6 100644 --- a/common/ferrostar/src/navigation_controller/mod.rs +++ b/common/ferrostar/src/navigation_controller/mod.rs @@ -179,6 +179,7 @@ impl NavigationController { match state { TripState::Idle => TripState::Idle, TripState::Navigating { + current_step_geometry_index, ref remaining_steps, ref remaining_waypoints, deviation, @@ -199,18 +200,13 @@ impl NavigationController { let snapped_user_location = snap_user_location_to_line(location, ¤t_step_linestring); - let current_step_geometry_index = index_of_closest_segment_origin( - snapped_user_location, - ¤t_step_linestring, - ); - let progress = calculate_trip_progress( &snapped_user_location.into(), ¤t_step_linestring, remaining_steps, ); let intermediate_state = TripState::Navigating { - current_step_geometry_index, + current_step_geometry_index: *current_step_geometry_index, snapped_user_location, remaining_steps: remaining_steps.clone(), remaining_waypoints: remaining_waypoints.clone(), @@ -234,11 +230,12 @@ impl NavigationController { } { TripState::Idle => TripState::Idle, TripState::Navigating { - current_step_geometry_index: current_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, progress, + // Explicitly recalculated + current_step_geometry_index: _, deviation: _, visual_instruction: _, spoken_instruction: _, @@ -261,8 +258,13 @@ impl NavigationController { .get_current_spoken_instruction(progress.distance_to_next_maneuver) .cloned(); + let current_step_geometry_index = index_of_closest_segment_origin( + snapped_user_location, + ¤t_step_linestring, + ); + TripState::Navigating { - current_step_geometry_index: current_geometry_index, + current_step_geometry_index, snapped_user_location, remaining_steps, remaining_waypoints, From b11b6a55e0d15cd9ed8c4abca333f01c25c8022f Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 15:14:57 +0900 Subject: [PATCH 15/16] Fix Android mock --- .../com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt index 74d9b368..116612d8 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/mock/MockNavigationState.kt @@ -33,7 +33,7 @@ fun NavigationState.Companion.pedestrianExample(): NavigationState { return NavigationState( tripState = TripState.Navigating( - currentGeometryIndex = 0u, + currentStepGeometryIndex = 0u, snappedUserLocation = UserLocation.pedestrianExample(), remainingSteps = listOf(), remainingWaypoints = listOf(), From 69345d8d8acd3609927c5613b2b6d90d05d7cc58 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 3 Sep 2024 15:44:47 +0900 Subject: [PATCH 16/16] iOS updates --- Package.resolved | 8 ++++---- .../xcshareddata/swiftpm/Package.resolved | 14 +++++++------- apple/Sources/FerrostarCore/FerrostarCore.swift | 2 +- .../FerrostarCore/Mock/MockNavigationState.swift | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Package.resolved b/Package.resolved index 470b0036..cc571caa 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/stadiamaps/maplibre-swiftui-dsl-playground", "state" : { - "revision" : "02f5a62009bc991a9dc59011785c83c347d7eea6", - "version" : "0.0.23" + "revision" : "a789bbee505a1344a87d9a5f999455ed55acdcde", + "version" : "0.0.28" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Kolos65/Mockable.git", "state" : { - "revision" : "81ccaead99a3c038c09345caa2888ae74b644ee9", - "version" : "0.0.9" + "revision" : "da977ecb20974c4b1cf185f5fd38771b2d4674fb", + "version" : "0.0.10" } }, { diff --git a/apple/DemoApp/Ferrostar Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apple/DemoApp/Ferrostar Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1600eef9..b59e42cd 100644 --- a/apple/DemoApp/Ferrostar Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/apple/DemoApp/Ferrostar Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git", "state" : { - "revision" : "cf66f087af489ebc091c03cbd4f38d0540135871", - "version" : "6.5.3" + "revision" : "abe762f1e19e03a4c6943d2aad92c219da384b29", + "version" : "6.5.4" } }, { @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/stadiamaps/maplibre-swiftui-dsl-playground", "state" : { - "revision" : "02f5a62009bc991a9dc59011785c83c347d7eea6", - "version" : "0.0.23" + "revision" : "a789bbee505a1344a87d9a5f999455ed55acdcde", + "version" : "0.0.28" } }, { @@ -33,14 +33,14 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Kolos65/Mockable.git", "state" : { - "revision" : "81ccaead99a3c038c09345caa2888ae74b644ee9", - "version" : "0.0.9" + "revision" : "da977ecb20974c4b1cf185f5fd38771b2d4674fb", + "version" : "0.0.10" } }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", + "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", "version" : "509.1.1" diff --git a/apple/Sources/FerrostarCore/FerrostarCore.swift b/apple/Sources/FerrostarCore/FerrostarCore.swift index 7c3c0340..44760e8e 100644 --- a/apple/Sources/FerrostarCore/FerrostarCore.swift +++ b/apple/Sources/FerrostarCore/FerrostarCore.swift @@ -274,7 +274,7 @@ public protocol FerrostarCoreDelegate: AnyObject { switch newState { case let .navigating( - currentGeometryIndex: _, + currentStepGeometryIndex: _, snappedUserLocation: _, remainingSteps: _, remainingWaypoints: remainingWaypoints, diff --git a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift index a0eaca22..033e864b 100644 --- a/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift +++ b/apple/Sources/FerrostarCore/Mock/MockNavigationState.swift @@ -5,7 +5,7 @@ import Foundation public extension NavigationState { static let pedestrianExample = NavigationState( tripState: .navigating( - currentGeometryIndex: 0, + currentStepGeometryIndex: 0, snappedUserLocation: UserLocation( latitude: samplePedestrianWaypoints.first!.lat, longitude: samplePedestrianWaypoints.first!.lng, @@ -37,7 +37,7 @@ public extension NavigationState { return NavigationState( tripState: .navigating( - currentGeometryIndex: 0, + currentStepGeometryIndex: 0, snappedUserLocation: UserLocation( coordinates: samplePedestrianWaypoints.first!, horizontalAccuracy: 10,