1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
use geo::{Coord, HaversineDistance, Point};
use uom::si::{f64::Length, length::meter};
use crate::{
algorithms::{points_in_front, Distance},
prelude::RailwayEdge,
};
/// Algorithms for railway edges.
pub trait RailwayEdgeAlgos {
/// Calculates the distance between the current location and the last coordinate in the linestring.
///
/// # Arguments
///
/// * `current_location` - A `Coord<f64>` representing the current location on the edge.
/// * `direction_coord` - A `Coord<f64>` representing the target direction along the edge.
///
/// # Returns
///
/// A `f64` representing the distance to the last coordinate in the linestring.
///
fn distance_to_end(&self, current_location: Coord<f64>, direction_coord: Coord<f64>) -> Length;
/// Calculates a new position on the edge based on the given parameters.
///
/// # Arguments
///
/// * `current_location` - A `Coord<f64>` representing the current location on the edge.
/// * `distance_to_travel` - A `f64` representing the distance to travel along the edge from the current location.
/// * `direction_coord` - A `Coord<f64>` representing the target direction along the edge.
///
/// # Returns
///
/// A `Coord<f64>` representing the new position on the edge after traveling the specified distance in the given direction.
///
fn position_on_edge(
&self,
current_location: Coord<f64>,
distance_to_travel: Length,
direction_coord: Coord<f64>,
) -> Coord<f64>;
}
impl RailwayEdgeAlgos for RailwayEdge {
fn distance_to_end(&self, current_location: Coord<f64>, direction_coord: Coord<f64>) -> Length {
// Get the points in front of the current_location in the direction of direction_coord
let points_in_front = points_in_front(&self.path, current_location, direction_coord);
// If there are no points in front, return 0.0
if points_in_front.is_empty() {
return Length::new::<meter>(0.0);
}
let mut total_distance = Length::new::<meter>(0.0);
let mut current_point = current_location;
for next_point in points_in_front {
let segment_distance = current_point.distance(&next_point);
total_distance += segment_distance;
current_point = next_point;
}
total_distance
}
fn position_on_edge(
&self,
current_location: Coord<f64>,
distance_to_travel: Length,
direction_coord: Coord<f64>,
) -> Coord<f64> {
// Get the points in front of the current_location in the direction of direction_coord
let points_in_front = points_in_front(&self.path, current_location, direction_coord);
// If there are no points in front, return the current_location
if points_in_front.is_empty() {
return current_location;
}
// Calculate the remaining distance to travel
let mut remaining_distance = distance_to_travel.get::<meter>();
// Iterate through the points in front and find the point where the remaining_distance is reached
let mut current_point = current_location;
let mut new_position = current_location;
for next_point in points_in_front {
let current_point_geo = Point::new(current_point.x, current_point.y);
let next_point_geo = Point::new(next_point.x, next_point.y);
// Use haversine_distance instead of euclidean_distance
let segment_distance = current_point_geo.haversine_distance(&next_point_geo);
let ratio = remaining_distance / segment_distance;
new_position.x = current_point.x + ratio * (next_point.x - current_point.x);
new_position.y = current_point.y + ratio * (next_point.y - current_point.y);
if remaining_distance < segment_distance {
break;
} else {
current_point = next_point;
remaining_distance -= segment_distance;
}
}
new_position
}
}