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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use geo::{Coord, CoordNum, LineString};
use serde::{Deserialize, Serialize};

use super::{IdType, NodeId};

/// Type alias for an edge identifier.
pub type EdgeId = IdType;

/// Structure representing a connection between two `TransitNode` instances.
///
/// Each edge has a unique identifier and a path represented as a `LineString`.
/// The `LineString` type `T` is generic and can be any type that implements the `CoordNum` trait.
///
/// # Examples
///
/// ```
/// use geo::{coord, LineString};
/// use transit_grid::core::TransitEdge;
///
/// let edge = TransitEdge {
///     id: 1,
///     source: 1,
///     target: 2,
///     length: 1.0,
///     path: LineString(vec![coord! { x: 0.0, y: 0.0 }, coord! { x: 1.0, y: 1.0 }]),
/// };
/// assert_eq!(edge.id, 1);
/// assert_eq!(edge.source, 1);
/// assert_eq!(edge.target, 2);
/// assert_eq!(edge.path, LineString(vec![coord! { x: 0.0, y: 0.0 }, coord! { x: 1.0, y: 1.0 }]));
/// ```
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct TransitEdge<T: CoordNum> {
    /// A unique identifier for the edge.
    pub id: EdgeId,

    /// The identifier of the node where the edge starts.
    pub source: NodeId,

    /// The identifier of the node where the edge ends.
    pub target: NodeId,

    /// The length of the edge.
    pub length: T,

    /// The path of the edge, represented as a `LineString`.
    pub path: LineString<T>,
}

impl<T: CoordNum> Default for TransitEdge<T> {
    fn default() -> Self {
        Self {
            id: 0,
            source: 0,
            target: 0,
            length: T::zero(),
            path: LineString(vec![]),
        }
    }
}

/// Trait providing a way to get the coordinates of the source and target nodes of a path.
///
/// `PathCoordinates` can be implemented by any type that has a source and target coordinate.
/// This is useful in graph algorithms where you need to know the start and end point of an edge.
///
pub trait PathCoordinates<T: CoordNum> {
    /// Returns the source coordinate of the path.
    fn source_coordinate(&self) -> Coord<T>;
    /// Returns the target coordinate of the path.
    fn target_coordinate(&self) -> Coord<T>;
}

impl<T: CoordNum> PathCoordinates<T> for TransitEdge<T> {
    fn source_coordinate(&self) -> Coord<T> {
        self.path.points().next().unwrap().0
    }

    fn target_coordinate(&self) -> Coord<T> {
        self.path.points().last().unwrap().0
    }
}

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

    #[test]
    fn test_edge() {
        let edge = TransitEdge {
            id: 1,
            source: 1,
            target: 2,
            length: 1.0,
            path: LineString(vec![coord! { x:0.0, y:0.0}, coord! { x:1.0, y:1.0}]),
        };
        assert_eq!(edge.id, 1);
        assert_eq!(edge.source, 1);
        assert_eq!(edge.target, 2);
        assert_eq!(edge.length, 1.0);
        assert_eq!(
            edge.path,
            LineString(vec![coord! { x:0.0, y:0.0}, coord! { x:1.0, y:1.0}])
        );
    }

    #[test]
    fn test_edge_default() {
        let edge = TransitEdge::<f64>::default();
        assert_eq!(edge.id, 0);
        assert_eq!(edge.source, 0);
        assert_eq!(edge.target, 0);
        assert_eq!(edge.path, LineString::<f64>(vec![]));
    }

    #[test]
    fn test_edge_coordinates() {
        let edge = TransitEdge {
            id: 1,
            source: 1,
            target: 2,
            length: 1.0,
            path: LineString(vec![coord! { x: 0.0, y: 0.0 }, coord! { x: 1.0, y: 1.0 }]),
        };
        assert_eq!(edge.source_coordinate(), coord! { x: 0.0, y: 0.0 });
        assert_eq!(edge.target_coordinate(), coord! { x: 1.0, y: 1.0 });
    }
}