1use crate::models::GnssPosition;
4use geo::Point;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct ProjectedPosition {
49 pub original: GnssPosition,
51
52 pub projected_coords: Point<f64>,
54
55 pub netelement_id: String,
57
58 pub measure_meters: f64,
60
61 pub projection_distance_meters: f64,
63
64 pub crs: String,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
70 pub intrinsic: Option<f64>,
71}
72
73impl ProjectedPosition {
74 pub fn new(
76 original: GnssPosition,
77 projected_coords: Point<f64>,
78 netelement_id: String,
79 measure_meters: f64,
80 projection_distance_meters: f64,
81 crs: String,
82 ) -> Self {
83 Self {
84 original,
85 projected_coords,
86 netelement_id,
87 measure_meters,
88 projection_distance_meters,
89 crs,
90 intrinsic: None,
91 }
92 }
93
94 pub fn with_intrinsic(
96 original: GnssPosition,
97 projected_coords: Point<f64>,
98 netelement_id: String,
99 measure_meters: f64,
100 projection_distance_meters: f64,
101 crs: String,
102 intrinsic: f64,
103 ) -> Self {
104 Self {
105 original,
106 projected_coords,
107 netelement_id,
108 measure_meters,
109 projection_distance_meters,
110 crs,
111 intrinsic: Some(intrinsic),
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use chrono::{FixedOffset, TimeZone};
120 use std::collections::HashMap;
121
122 #[test]
123 fn test_projected_position_creation() {
124 let timestamp = FixedOffset::east_opt(3600)
125 .unwrap()
126 .with_ymd_and_hms(2025, 12, 9, 14, 30, 0)
127 .unwrap();
128
129 let original = GnssPosition {
130 latitude: 50.8503,
131 longitude: 4.3517,
132 timestamp,
133 crs: "EPSG:4326".to_string(),
134 metadata: HashMap::new(),
135 heading: None,
136 distance: None,
137 };
138
139 let projected = ProjectedPosition::new(
140 original.clone(),
141 Point::new(4.3517, 50.8503),
142 "NE001".to_string(),
143 100.5,
144 2.3,
145 "EPSG:4326".to_string(),
146 );
147
148 assert_eq!(projected.netelement_id, "NE001");
149 assert_eq!(projected.measure_meters, 100.5);
150 assert_eq!(projected.projection_distance_meters, 2.3);
151 assert!(projected.intrinsic.is_none());
152 }
153
154 #[test]
155 fn test_projected_position_with_intrinsic() {
156 let timestamp = FixedOffset::east_opt(3600)
157 .unwrap()
158 .with_ymd_and_hms(2025, 12, 9, 14, 30, 0)
159 .unwrap();
160
161 let original = GnssPosition {
162 latitude: 50.8503,
163 longitude: 4.3517,
164 timestamp,
165 crs: "EPSG:4326".to_string(),
166 metadata: HashMap::new(),
167 heading: None,
168 distance: None,
169 };
170
171 let projected = ProjectedPosition::with_intrinsic(
172 original.clone(),
173 Point::new(4.3517, 50.8503),
174 "NE001".to_string(),
175 100.5,
176 2.3,
177 "EPSG:4326".to_string(),
178 0.75,
179 );
180
181 assert_eq!(projected.netelement_id, "NE001");
182 assert_eq!(projected.measure_meters, 100.5);
183 assert_eq!(projected.projection_distance_meters, 2.3);
184 assert_eq!(projected.intrinsic, Some(0.75));
185 }
186
187 #[test]
188 fn test_projected_position_preserves_original_data() {
189 let timestamp = FixedOffset::east_opt(3600)
190 .unwrap()
191 .with_ymd_and_hms(2025, 12, 9, 14, 30, 0)
192 .unwrap();
193
194 let mut metadata = HashMap::new();
195 metadata.insert("speed".to_string(), "50.5".to_string());
196 metadata.insert("quality".to_string(), "high".to_string());
197
198 let original = GnssPosition {
199 latitude: 50.8503,
200 longitude: 4.3517,
201 timestamp,
202 crs: "EPSG:4326".to_string(),
203 metadata: metadata.clone(),
204 heading: Some(90.0),
205 distance: Some(150.5),
206 };
207
208 let projected = ProjectedPosition::new(
209 original.clone(),
210 Point::new(4.3517, 50.8503),
211 "NE001".to_string(),
212 100.5,
213 2.3,
214 "EPSG:4326".to_string(),
215 );
216
217 assert_eq!(projected.original.latitude, 50.8503);
219 assert_eq!(projected.original.longitude, 4.3517);
220 assert_eq!(projected.original.heading, Some(90.0));
221 assert_eq!(projected.original.distance, Some(150.5));
222 assert_eq!(projected.original.metadata, metadata);
223 assert_eq!(projected.original.timestamp, timestamp);
224 }
225
226 #[test]
227 fn test_projected_position_different_crs() {
228 let timestamp = FixedOffset::east_opt(0)
229 .unwrap()
230 .with_ymd_and_hms(2025, 12, 9, 14, 30, 0)
231 .unwrap();
232
233 let original = GnssPosition {
234 latitude: 50.8503,
235 longitude: 4.3517,
236 timestamp,
237 crs: "EPSG:4326".to_string(),
238 metadata: HashMap::new(),
239 heading: None,
240 distance: None,
241 };
242
243 let projected = ProjectedPosition::new(
245 original.clone(),
246 Point::new(649775.0, 667946.0), "NE001".to_string(),
248 100.5,
249 2.3,
250 "EPSG:31370".to_string(), );
252
253 assert_eq!(projected.crs, "EPSG:31370");
254 assert_eq!(projected.original.crs, "EPSG:4326");
255 assert_ne!(projected.projected_coords.x(), projected.original.longitude);
256 }
257}