1use crate::area::AreaKm2;
4use crate::geo::{BoundingBox, OutletCoord, WkbGeometry};
5use crate::id::UnitId;
6use crate::level::Level;
7
8#[derive(Debug, Clone, PartialEq)]
15pub struct CatchmentUnit {
16 id: UnitId,
17 level: Level,
18 parent_id: Option<UnitId>,
19 area: AreaKm2,
20 upstream_area: Option<AreaKm2>,
21 outlet: OutletCoord,
22 bbox: BoundingBox,
23 geometry: WkbGeometry,
24 source_id: Option<String>,
25 level_label: Option<String>,
26}
27
28impl CatchmentUnit {
29 #[allow(clippy::too_many_arguments)]
34 pub fn new(
35 id: UnitId,
36 level: Level,
37 parent_id: Option<UnitId>,
38 area: AreaKm2,
39 upstream_area: Option<AreaKm2>,
40 outlet: OutletCoord,
41 bbox: BoundingBox,
42 geometry: WkbGeometry,
43 source_id: Option<String>,
44 level_label: Option<String>,
45 ) -> Self {
46 Self {
47 id,
48 level,
49 parent_id,
50 area,
51 upstream_area,
52 outlet,
53 bbox,
54 geometry,
55 source_id,
56 level_label,
57 }
58 }
59
60 pub fn id(&self) -> UnitId {
62 self.id
63 }
64
65 pub fn level(&self) -> Level {
67 self.level
68 }
69
70 pub fn parent_id(&self) -> Option<UnitId> {
72 self.parent_id
73 }
74
75 pub fn area(&self) -> AreaKm2 {
77 self.area
78 }
79
80 pub fn upstream_area(&self) -> Option<AreaKm2> {
84 self.upstream_area
85 }
86
87 pub fn outlet(&self) -> OutletCoord {
89 self.outlet
90 }
91
92 pub fn bbox(&self) -> &BoundingBox {
94 &self.bbox
95 }
96
97 pub fn geometry(&self) -> &WkbGeometry {
99 &self.geometry
100 }
101
102 pub fn source_id(&self) -> Option<&str> {
104 self.source_id.as_deref()
105 }
106
107 pub fn level_label(&self) -> Option<&str> {
109 self.level_label.as_deref()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 fn test_unit_id(raw: i64) -> UnitId {
118 UnitId::new(raw).unwrap()
119 }
120
121 fn test_level(raw: i16) -> Level {
122 Level::new(raw).unwrap()
123 }
124
125 fn test_outlet() -> OutletCoord {
126 OutletCoord::new(0.0, 0.0).unwrap()
127 }
128
129 fn test_bbox() -> BoundingBox {
130 BoundingBox::new(-10.0, -5.0, 10.0, 5.0).unwrap()
131 }
132
133 fn test_wkb() -> WkbGeometry {
134 WkbGeometry::new(vec![0x01, 0x02, 0x03]).unwrap()
135 }
136
137 fn test_area(km2: f32) -> AreaKm2 {
138 AreaKm2::new(km2).unwrap()
139 }
140
141 #[test]
142 fn valid_catchment_unit_getters_return_expected_values() {
143 let id = test_unit_id(42);
144 let level = test_level(1);
145 let parent_id = Some(test_unit_id(7));
146 let area = test_area(100.0);
147 let upstream_area = Some(test_area(500.0));
148 let outlet = test_outlet();
149 let bbox = test_bbox();
150 let geometry = test_wkb();
151
152 let unit = CatchmentUnit::new(
153 id,
154 level,
155 parent_id,
156 area,
157 upstream_area,
158 outlet,
159 bbox,
160 geometry.clone(),
161 Some("src-42".to_string()),
162 Some("l1".to_string()),
163 );
164
165 assert_eq!(unit.id(), id);
166 assert_eq!(unit.level(), level);
167 assert_eq!(unit.parent_id(), parent_id);
168 assert_eq!(unit.area(), area);
169 assert_eq!(unit.upstream_area(), upstream_area);
170 assert_eq!(unit.outlet(), outlet);
171 assert_eq!(unit.bbox(), &bbox);
172 assert_eq!(unit.geometry(), &geometry);
173 assert_eq!(unit.source_id(), Some("src-42"));
174 assert_eq!(unit.level_label(), Some("l1"));
175 }
176
177 #[test]
178 fn upstream_area_none_returns_none() {
179 let unit = CatchmentUnit::new(
180 test_unit_id(1),
181 test_level(0),
182 None,
183 test_area(50.0),
184 None,
185 test_outlet(),
186 test_bbox(),
187 test_wkb(),
188 None,
189 None,
190 );
191
192 assert_eq!(unit.upstream_area(), None);
193 }
194
195 #[test]
196 fn upstream_area_some_returns_some() {
197 let up_area = test_area(999.9);
198 let unit = CatchmentUnit::new(
199 test_unit_id(1),
200 test_level(0),
201 None,
202 test_area(50.0),
203 Some(up_area),
204 test_outlet(),
205 test_bbox(),
206 test_wkb(),
207 None,
208 None,
209 );
210
211 assert_eq!(unit.upstream_area(), Some(up_area));
212 }
213}