use std::sync::Arc;
use crate::deviation_detection::RouteDeviation;
use crate::deviation_detection::RouteDeviationTracking;
use crate::models::{
BoundingBox, GeographicCoordinate, Route, RouteStep, UserLocation, Waypoint, WaypointKind,
};
use crate::navigation_controller::models::{
CourseFiltering, NavigationControllerConfig, TripProgress, TripState, TripSummary,
WaypointAdvanceMode,
};
use crate::navigation_controller::step_advance::StepAdvanceCondition;
use crate::navigation_controller::step_advance::conditions::DistanceToEndOfStepCondition;
#[cfg(feature = "alloc")]
use alloc::string::ToString;
use chrono::{DateTime, Utc};
use geo::{BoundingRect, Coord, Distance, Haversine, LineString, Point, point};
use insta::{Settings, dynamic_redaction};
pub fn get_test_navigation_controller_config(
step_advance_condition: Arc<dyn StepAdvanceCondition>,
) -> NavigationControllerConfig {
NavigationControllerConfig {
waypoint_advance: WaypointAdvanceMode::WaypointWithinRange(100.0),
route_deviation_tracking: RouteDeviationTracking::StaticThreshold {
minimum_horizontal_accuracy: 0,
max_acceptable_deviation: 0.0,
},
snapped_location_course_filtering: CourseFiltering::Raw,
step_advance_condition,
arrival_step_advance_condition: Arc::new(DistanceToEndOfStepCondition {
distance: 5,
minimum_horizontal_accuracy: 0,
}),
}
}
pub fn get_test_step_advance_condition(distance: u16) -> Arc<dyn StepAdvanceCondition> {
Arc::new(DistanceToEndOfStepCondition {
distance,
minimum_horizontal_accuracy: 0,
})
}
pub fn gen_dummy_route_step(
start_lng: f64,
start_lat: f64,
end_lng: f64,
end_lat: f64,
) -> RouteStep {
RouteStep {
geometry: vec![
GeographicCoordinate {
lng: start_lng,
lat: start_lat,
},
GeographicCoordinate {
lng: end_lng,
lat: end_lat,
},
],
distance: Haversine.distance(
point!(x: start_lng, y: start_lat),
point!(x: end_lng, y: end_lat),
),
duration: 0.0,
road_name: None,
exits: vec![],
instruction: "".to_string(),
visual_instructions: vec![],
spoken_instructions: vec![],
annotations: None,
incidents: vec![],
driving_side: None,
roundabout_exit_number: None,
}
}
pub fn gen_route_step_with_coords(coordinates: Vec<Coord>) -> RouteStep {
if coordinates.len() < 2 {
panic!("A route step requires at least 2 coordinates");
}
let geo_coordinates: Vec<GeographicCoordinate> = coordinates
.into_iter()
.map(|coord| GeographicCoordinate {
lng: coord.x,
lat: coord.y,
})
.collect();
let points: Vec<Point> = geo_coordinates
.iter()
.map(|coord| point!(x: coord.lng, y: coord.lat))
.collect();
let mut total_distance = 0.0;
for i in 0..points.len() - 1 {
total_distance += Haversine.distance(points[i], points[i + 1]);
}
RouteStep {
geometry: geo_coordinates,
distance: total_distance,
duration: 0.0,
road_name: None,
exits: vec![],
instruction: "".to_string(),
visual_instructions: vec![],
spoken_instructions: vec![],
annotations: None,
incidents: vec![],
driving_side: None,
roundabout_exit_number: None,
}
}
pub fn gen_route_from_steps(steps: Vec<RouteStep>) -> Route {
let geometry: Vec<_> = steps
.iter()
.flat_map(|step| step.geometry.clone())
.collect();
let linestring = LineString::from_iter(geometry.iter().map(|point| Point::from(*point)));
let distance = steps.iter().fold(0.0, |acc, step| acc + step.distance);
let bbox = linestring.bounding_rect().unwrap();
Route {
geometry,
bbox: BoundingBox {
sw: GeographicCoordinate::from(bbox.min()),
ne: GeographicCoordinate::from(bbox.max()),
},
distance,
waypoints: vec![
Waypoint {
coordinate: steps.first().unwrap().geometry.first().cloned().unwrap(),
kind: WaypointKind::Break,
properties: None,
},
Waypoint {
coordinate: steps.last().unwrap().geometry.last().cloned().unwrap(),
kind: WaypointKind::Break,
properties: None,
},
],
steps,
}
}
fn create_timestamp_redaction()
-> impl Fn(insta::internals::Content, insta::internals::ContentPath<'_>) -> &'static str
+ Send
+ Sync
+ 'static {
|value, _path| {
if value.is_nil() {
"[none]"
} else if let Some(timestamp_str) = value.as_str() {
match timestamp_str.parse::<DateTime<Utc>>() {
Ok(_) => "[timestamp]",
Err(_) => "[invalid-timestamp]",
}
} else if let Some(timestamp_num) = value.as_i64() {
match DateTime::<Utc>::from_timestamp_millis(timestamp_num) {
Some(_) => "[timestamp]",
None => "[invalid-timestamp]",
}
} else {
"[unexpected-value]"
}
}
}
fn create_distance_redaction()
-> impl Fn(insta::internals::Content, insta::internals::ContentPath<'_>) -> String
+ Send
+ Sync
+ 'static {
|value, _path| {
if value.is_nil() {
"[none]".to_string()
} else if let Some(distance_str) = value.as_str() {
match distance_str.parse::<f64>() {
Ok(distance) => {
format!("{:.10}", distance)
}
Err(_) => "[invalid-distance]".to_string(),
}
} else if let Some(distance) = value.as_f64() {
format!("{:.10}", distance)
} else {
"[unexpected-value]".to_string()
}
}
}
pub(crate) fn nav_controller_insta_settings() -> Settings {
let mut settings = Settings::new();
settings.add_redaction(
".**.startedAt",
dynamic_redaction(create_timestamp_redaction()),
);
settings.add_redaction(
".**.endedAt",
dynamic_redaction(create_timestamp_redaction()),
);
settings.add_redaction(
".**.timestamp",
dynamic_redaction(create_timestamp_redaction()),
);
settings.add_redaction(
".**.initial_timestamp",
dynamic_redaction(create_timestamp_redaction()),
);
settings.add_redaction(
".**.distanceTraveled",
dynamic_redaction(create_distance_redaction()),
);
settings.add_redaction(
".**.snappedDistanceTraveled",
dynamic_redaction(create_distance_redaction()),
);
settings.add_redaction(
".**.distanceToNextManeuver",
dynamic_redaction(create_distance_redaction()),
);
settings.add_redaction(
".**.distanceRemaining",
dynamic_redaction(create_distance_redaction()),
);
settings.add_redaction(
".**.durationRemaining",
dynamic_redaction(create_distance_redaction()),
);
settings.add_redaction(".version", "[version]");
settings
}
pub fn get_navigating_trip_state(
user_location: UserLocation,
remaining_steps: Vec<RouteStep>,
remaining_waypoints: Vec<Waypoint>,
deviation: RouteDeviation,
) -> TripState {
TripState::Navigating {
current_step_geometry_index: Some(0),
user_location: user_location.clone(),
snapped_user_location: user_location,
remaining_steps,
remaining_waypoints,
progress: TripProgress {
distance_to_next_maneuver: 100.0,
distance_remaining: 1000.0,
duration_remaining: 600.0,
},
deviation,
summary: TripSummary {
distance_traveled: 0.0,
snapped_distance_traveled: 0.0,
started_at: Utc::now(),
ended_at: None,
},
visual_instruction: None,
spoken_instruction: None,
annotation_json: None,
}
}