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
//! Edge length functions for `TransitEdge`.
use geo::{CoordFloat, EuclideanLength, HaversineLength};
use num_traits::FromPrimitive;
use std::iter::Sum;

use crate::core::TransitEdge;

/// EdgeLength trait provides the length of an element.
/// It is designed to work with types that implement the `CoordFloat`, `FromPrimitive`, and `Sum` traits.
pub trait EdgeLength<T: CoordFloat + Sum> {
    /// Returns the Euclidean length of the element.
    fn length(&self) -> T;
}

/// EdgeLength trait implementation for `TransitEdge`.
/// Returns the Euclidean length of the `TransitEdge`.
impl<T: CoordFloat + FromPrimitive + Sum> EdgeLength<T> for TransitEdge<T> {
    fn length(&self) -> T {
        self.euclidean_length()
    }
}

/// EuclideanLength trait implementation for `TransitEdge`.
/// Returns the Euclidean length of the `TransitEdge`.
impl<T: CoordFloat + Sum> EuclideanLength<T> for TransitEdge<T> {
    fn euclidean_length(&self) -> T {
        self.path.euclidean_length()
    }
}

/// HaversineLength trait implementation for `TransitEdge`.
/// Returns the Haversine (great-circle) length of the `TransitEdge`.
impl<T: CoordFloat + FromPrimitive> HaversineLength<T> for TransitEdge<T> {
    fn haversine_length(&self) -> T {
        self.path.haversine_length()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use geo::LineString;

    #[test]
    fn test_edge_length() {
        let line = LineString::from(vec![(0.0, 0.0), (1.0, 1.0)]);
        let edge = TransitEdge {
            id: 1,
            source: 1,
            target: 2,
            length: 1.0,
            path: line.clone(),
        };

        assert_eq!(edge.length(), (2f64).sqrt());
    }

    #[test]
    fn test_euclidean_length() {
        let line = LineString::from(vec![(0.0, 0.0), (1.0, 1.0)]);
        let edge = TransitEdge {
            id: 1,
            source: 1,
            target: 2,
            length: 1.0,
            path: line.clone(),
        };

        assert_eq!(edge.euclidean_length(), (2f64).sqrt());
    }

    #[test]
    fn test_haversine_length() {
        let line = LineString::from(vec![(-179.9, 0.0), (179.9, 0.0)]);
        let edge = TransitEdge {
            id: 1,
            source: 1,
            target: 2,
            length: 1.0,
            path: line.clone(),
        };

        let approx_circumference = 2.0 * std::f64::consts::PI * 6371.0 * 1000.0; // Approx. Earth radius in m
        let expected_length = approx_circumference * (0.2 / 360.0); // 0.2 degrees out of 360 degrees
        assert!((edge.haversine_length() - expected_length).abs() < 1.0); // Allow 1 km error
    }
}