1#[cfg(all(feature = "libm", not(feature = "std")))]
13use num_traits::float::Float;
14
15const EARTH_CIRCUMFERENCE: f32 = 39_940_653.; #[must_use]
23pub fn point_from_dxy(dx: f32, dy: f32, ref_pos: &geo_types::Point) -> geo_types::Point {
24 dxy_to_geo(dx, dy, ref_pos).into()
25}
26
27#[cfg(feature = "_dsrc_2_2_1")]
29impl From<crate::standards::dsrc_2_2_1::etsi_its_dsrc::Position3D> for geo_types::Point {
30 fn from(other: crate::standards::dsrc_2_2_1::etsi_its_dsrc::Position3D) -> Self {
31 geo_types::Point::new(other.long.as_deg(), other.lat.as_deg())
32 }
33}
34#[cfg(feature = "_dsrc_2_2_1")]
35impl From<geo_types::Point> for crate::standards::dsrc_2_2_1::etsi_its_dsrc::Position3D {
36 fn from(other: geo_types::Point) -> Self {
37 use crate::standards::cdd_2_2_1::etsi_its_cdd::{Latitude, Longitude};
38
39 Self {
40 lat: Latitude::from_deg(other.y()),
41 long: Longitude::from_deg(other.x()),
42 elevation: None,
43 regional: None,
44 }
45 }
46}
47#[cfg(feature = "_dsrc_2_2_1")]
48impl From<crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeLLmD64b> for geo_types::Point {
49 fn from(other: crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeLLmD64b) -> Self {
50 geo_types::Point::new(other.lon.as_deg(), other.lat.as_deg())
51 }
52}
53#[cfg(feature = "_dsrc_2_2_1")]
54impl From<&crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeLLmD64b> for geo_types::Point {
55 fn from(other: &crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeLLmD64b) -> Self {
56 geo_types::Point::new(other.lon.as_deg(), other.lat.as_deg())
57 }
58}
59
60#[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
62macro_rules! refpos_to_point {
63 ($t:ty) => {
64 impl From<$t> for geo_types::Point {
65 fn from(other: $t) -> Self {
66 geo_types::Point::new(other.longitude.as_deg(), other.latitude.as_deg())
67 }
68 }
69 };
70}
71
72#[cfg(feature = "_cdd_1_3_1_1")]
74refpos_to_point!(crate::standards::cdd_1_3_1_1::its_container::ReferencePosition);
75
76#[cfg(feature = "_cdd_2_2_1")]
78refpos_to_point!(crate::standards::cdd_2_2_1::etsi_its_cdd::ReferencePosition);
79
80#[cfg(any(feature = "_cdd_1_3_1_1", feature = "denm_2_2_1"))]
83macro_rules! ph_to_line_string {
84 ($t:ty) => {
85 impl $t {
86 #[allow(
93 clippy::missing_panics_doc,
94 reason = "panic on last().unwrap() is impossible here"
95 )]
96 #[must_use]
97 pub fn to_line_string(&self, ref_pos: geo_types::Point) -> geo_types::LineString {
98 self.0
99 .iter()
100 .fold(alloc::vec![ref_pos], |mut acc, pt| {
101 let origin = acc.last().unwrap();
103 let delta = geo_types::Point::new(
104 pt.path_position.delta_longitude.as_deg(),
105 pt.path_position.delta_latitude.as_deg(),
106 );
107
108 acc.push(*origin + delta);
109 acc
110 })
111 .into()
112 }
113 }
114 };
115}
116
117#[cfg(feature = "_cdd_1_3_1_1")]
119ph_to_line_string!(crate::standards::cdd_1_3_1_1::its_container::PathHistory);
120
121#[cfg(feature = "denm_2_2_1")]
123ph_to_line_string!(crate::standards::cdd_2_2_1::etsi_its_cdd::Path);
124
125#[cfg(feature = "mapem_2_2_1")]
127impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeSetXY {
128 #[allow(
135 clippy::missing_panics_doc,
136 reason = "panic on last().unwrap() is impossible here"
137 )]
138 #[must_use]
139 pub fn to_line_string(&self, ref_pos: geo_types::Point) -> geo_types::LineString {
140 let mut path = self.0.iter().fold(alloc::vec![ref_pos], |mut acc, pt| {
141 let origin = acc.last().unwrap();
143 let pos = pt.delta.to_geo(origin);
144
145 acc.push(pos);
146 acc
147 });
148 path.remove(0); path.into()
151 }
152
153 #[must_use]
160 pub fn to_line_string_xy(&self, ref_pos: geo_types::Point) -> geo_types::LineString {
161 let mut prev_delta = geo_types::Point::default();
162 let mut prev_ref_pos = ref_pos;
163 self.0
164 .iter()
165 .map(|pt| {
166 let node = match &pt.delta {
167 crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeOffsetPointXY::node_LatLon(
168 node_llm_d64b,
169 ) => {
170 let result = pt.delta.to_ddist(&prev_ref_pos);
171
172 prev_ref_pos = node_llm_d64b.into();
174 result
175 }
176 _ => prev_delta + pt.delta.to_ddist(&ref_pos),
177 };
178 prev_delta = node;
179
180 node
181 })
182 .collect()
183 }
184}
185
186#[cfg(feature = "mapem_2_2_1")]
187impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeOffsetPointXY {
188 #[must_use]
193 pub fn to_geo(&self, ref_pos: &geo_types::Point) -> geo_types::Point {
194 use crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeOffsetPointXY;
195
196 let (lon, lat) = match self {
197 NodeOffsetPointXY::node_XY1(etsi) => {
198 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
199 }
200 NodeOffsetPointXY::node_XY2(etsi) => {
201 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
202 }
203 NodeOffsetPointXY::node_XY3(etsi) => {
204 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
205 }
206 NodeOffsetPointXY::node_XY4(etsi) => {
207 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
208 }
209 NodeOffsetPointXY::node_XY5(etsi) => {
210 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
211 }
212 NodeOffsetPointXY::node_XY6(etsi) => {
213 dxy_to_geo((&etsi.x).into(), (&etsi.y).into(), ref_pos)
214 }
215 NodeOffsetPointXY::node_LatLon(etsi) => (etsi.lon.as_deg(), etsi.lat.as_deg()),
216 NodeOffsetPointXY::regional(_) => (0., 0.),
217 };
218
219 geo_types::Point::new(lon, lat)
220 }
221
222 #[must_use]
229 pub fn to_ddist(&self, ref_pos: &geo_types::Point) -> geo_types::Point {
230 use crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeOffsetPointXY;
231
232 let (x, y) = match self {
233 NodeOffsetPointXY::node_XY1(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
234 NodeOffsetPointXY::node_XY2(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
235 NodeOffsetPointXY::node_XY3(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
236 NodeOffsetPointXY::node_XY4(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
237 NodeOffsetPointXY::node_XY5(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
238 NodeOffsetPointXY::node_XY6(etsi) => ((&etsi.x).into(), (&etsi.y).into()),
239 NodeOffsetPointXY::node_LatLon(etsi) => latlon_to_dcart(etsi, ref_pos),
240 NodeOffsetPointXY::regional(_) => (0., 0.),
241 };
242
243 geo_types::Point::new(x.into(), y.into())
244 }
245}
246
247#[cfg(any(feature = "std", feature = "libm"))]
248fn dxy_to_geo(dx: f32, dy: f32, ref_pos: &geo_types::Point) -> (f64, f64) {
252 let dlat = f64::from(dy) / f64::from(EARTH_CIRCUMFERENCE) * 360.;
254
255 let ref_lat_rad = ref_pos.to_radians().y();
257 let dlon = f64::from(dx) / (f64::from(EARTH_CIRCUMFERENCE) * ref_lat_rad.cos()) * 360.;
258
259 (ref_pos.x() + dlon, ref_pos.y() + dlat)
260}
261
262#[cfg(all(feature = "mapem_2_2_1", any(feature = "std", feature = "libm")))]
266fn latlon_to_dcart(
267 dpos: &crate::standards::dsrc_2_2_1::etsi_its_dsrc::NodeLLmD64b,
268 ref_pos: &geo_types::Point,
269) -> (f32, f32) {
270 #[allow(clippy::cast_possible_truncation)]
272 let dlat_deg = dpos.lat.as_deg() as f32 - ref_pos.y() as f32;
273 let dy = dlat_deg / 360. * EARTH_CIRCUMFERENCE;
274
275 #[allow(clippy::cast_possible_truncation)]
277 let ref_lat_rad = ref_pos.to_radians().y() as f32;
278 #[allow(clippy::cast_possible_truncation)]
279 let dlon_deg = dpos.lon.as_deg() as f32 - ref_pos.x() as f32;
280 let dx = dlon_deg / 360. * (EARTH_CIRCUMFERENCE * ref_lat_rad.cos());
281
282 (dx, dy)
283}
284
285#[cfg(feature = "cpm_1")]
286impl crate::standards::cpm_1::cpm_pdu_descriptions::NodeOffsetPointZ {
287 #[must_use]
288 pub fn to_meters(&self) -> f32 {
289 use crate::standards::cpm_1::cpm_pdu_descriptions::NodeOffsetPointZ;
290
291 match self {
292 NodeOffsetPointZ::node_Z1(etsi) => etsi.into(),
293 NodeOffsetPointZ::node_Z2(etsi) => etsi.into(),
294 NodeOffsetPointZ::node_Z3(etsi) => etsi.into(),
295 NodeOffsetPointZ::node_Z4(etsi) => etsi.into(),
296 NodeOffsetPointZ::node_Z5(etsi) => etsi.into(),
297 NodeOffsetPointZ::node_Z6(etsi) => etsi.into(),
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 #[test]
305 fn dxy_to_geo_test() {
306 use crate::geo_utils::dxy_to_geo;
307
308 let ref_pos = geo_types::point! {x: 9.936_521, y: 53.550_728};
309
310 assert_eq!((ref_pos.x(), ref_pos.y()), dxy_to_geo(0., 0., &ref_pos));
312
313 let (dlon, dlat) = dxy_to_geo(0., 185., &ref_pos);
315 assert_float_eq::assert_float_absolute_eq!(ref_pos.x() + 0., dlon);
316 assert_float_eq::assert_float_absolute_eq!(ref_pos.y() + 0.001_667, dlat);
317
318 let (dlon, dlat) = dxy_to_geo(0., -185., &ref_pos);
319 assert_float_eq::assert_float_absolute_eq!(ref_pos.x() + 0., dlon);
320 assert_float_eq::assert_float_absolute_eq!(ref_pos.y() + -0.001_667, dlat);
321
322 let (dlon, dlat) = dxy_to_geo(66., 0., &ref_pos);
324 assert_float_eq::assert_float_absolute_eq!(ref_pos.x() + 0.001_001, dlon);
325 assert_float_eq::assert_float_absolute_eq!(ref_pos.y() + 0., dlat);
326
327 let (dlon, dlat) = dxy_to_geo(66., 185., &ref_pos);
329 assert_float_eq::assert_float_absolute_eq!(ref_pos.x() + 0.001_001, dlon);
330 assert_float_eq::assert_float_absolute_eq!(ref_pos.y() + 0.001_667, dlat);
331 }
332
333 #[test]
334 #[cfg(feature = "mapem_2_2_1")]
335 fn nodeset_to_line_string() {
336 use crate::standards::cdd_2_2_1::etsi_its_cdd::{Latitude, Longitude};
337 use crate::standards::dsrc_2_2_1::etsi_its_dsrc::{
338 NodeLLmD64b,
339 NodeOffsetPointXY,
340 NodeSetXY,
341 NodeXY,
342 NodeXY32b,
343 OffsetB16,
344 };
345
346 let ref_pos = geo_types::point! {x: 9.936_521, y: 53.550_728};
347
348 {
350 let point1 = NodeLLmD64b::new(
351 Longitude::from_deg(ref_pos.x() + 0.001),
352 Latitude::from_deg(ref_pos.y() + 0.005),
353 );
354 let point2 = NodeLLmD64b::new(
355 Longitude::from_deg(ref_pos.x() + -0.042),
356 Latitude::from_deg(ref_pos.y() + 0.),
357 );
358 let nodes = alloc::vec![
359 NodeXY::new(NodeOffsetPointXY::node_LatLon(point1), None),
360 NodeXY::new(NodeOffsetPointXY::node_LatLon(point2), None),
361 ];
362
363 let geo_nodes = NodeSetXY(nodes).to_line_string(ref_pos).into_points();
364 let geo_point1 = geo_types::Point::new(9.936_521 + 0.001, 53.550_728 + 0.005);
365 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x());
366 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y());
367 let geo_point2 = geo_types::Point::new(9.936_521 - 0.042, 53.550_728 + 0.);
368 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x());
369 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y());
370 }
371
372 {
374 let point1 = NodeXY32b::new(
375 OffsetB16::from_meters(0.).unwrap(),
376 OffsetB16::from_meters(185.).unwrap(),
377 );
378 let point2 = NodeXY32b::new(
379 OffsetB16::from_meters(66.).unwrap(),
380 OffsetB16::from_meters(0.).unwrap(),
381 );
382 let nodes = alloc::vec![
383 NodeXY::new(NodeOffsetPointXY::node_XY6(point1), None),
384 NodeXY::new(NodeOffsetPointXY::node_XY6(point2), None),
385 ];
386
387 let geo_nodes = NodeSetXY(nodes).to_line_string(ref_pos).into_points();
388 let geo_point1 = geo_types::Point::new(9.936_521, 53.550_728 + 0.001_667);
389 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x());
390 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y());
391
392 let geo_point2 = geo_types::Point::new(9.936_521 + 0.001_001, 53.550_728 + 0.001_667);
393 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x());
394 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y());
395 }
396
397 {
399 let point1 = NodeXY32b::new(
400 OffsetB16::from_meters(0.).unwrap(),
401 OffsetB16::from_meters(185.).unwrap(),
402 );
403 let point2 = NodeXY32b::new(
404 OffsetB16::from_meters(66.).unwrap(),
405 OffsetB16::from_meters(0.).unwrap(),
406 );
407 let point3 = NodeLLmD64b::new(
408 Longitude::from_deg(9.930_123),
409 Latitude::from_deg(53.550_420),
410 );
411 let point4 = NodeXY32b::new(
412 OffsetB16::from_meters(66.).unwrap(),
413 OffsetB16::from_meters(0.).unwrap(),
414 );
415 let nodes = alloc::vec![
416 NodeXY::new(NodeOffsetPointXY::node_XY6(point1), None),
417 NodeXY::new(NodeOffsetPointXY::node_XY6(point2), None),
418 NodeXY::new(NodeOffsetPointXY::node_LatLon(point3), None),
419 NodeXY::new(NodeOffsetPointXY::node_XY6(point4), None),
420 ];
421
422 let geo_nodes = NodeSetXY(nodes).to_line_string(ref_pos).into_points();
423 let geo_point1 = geo_types::Point::new(9.936_521, 53.550_728 + 0.001_667);
424 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x());
425 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y());
426
427 let geo_point2 = geo_types::Point::new(9.936_521 + 0.001_001, 53.550_728 + 0.001_667);
428 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x());
429 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y());
430
431 let geo_point3 = geo_types::Point::new(9.930_123, 53.550_420);
432 assert_float_eq::assert_float_absolute_eq!(geo_point3.x(), geo_nodes[2].x());
433 assert_float_eq::assert_float_absolute_eq!(geo_point3.y(), geo_nodes[2].y());
434
435 let geo_point4 = geo_types::Point::new(9.930_123 + 0.001_001, 53.550_420);
436 assert_float_eq::assert_float_absolute_eq!(geo_point4.x(), geo_nodes[3].x());
437 assert_float_eq::assert_float_absolute_eq!(geo_point4.y(), geo_nodes[3].y());
438 }
439 }
440
441 #[test]
442 #[cfg(feature = "mapem_2_2_1")]
443 fn nodeset_to_line_string_xy() {
444 use crate::standards::cdd_2_2_1::etsi_its_cdd::{Latitude, Longitude};
445 use crate::standards::dsrc_2_2_1::etsi_its_dsrc::{
446 NodeLLmD64b,
447 NodeOffsetPointXY,
448 NodeSetXY,
449 NodeXY,
450 NodeXY32b,
451 OffsetB16,
452 };
453
454 let ref_pos = geo_types::point! {x: 9.936_521, y: 53.550_728};
455
456 {
458 let point1 = NodeLLmD64b::new(
459 Longitude::from_deg(ref_pos.x()),
460 Latitude::from_deg(ref_pos.y() + 0.001_667),
461 );
462 let point2 = NodeLLmD64b::new(
463 Longitude::from_deg(ref_pos.x() + 0.001_001),
464 Latitude::from_deg(ref_pos.y() + 0.001_667),
465 );
466 let nodes = alloc::vec![
467 NodeXY::new(NodeOffsetPointXY::node_LatLon(point1), None),
468 NodeXY::new(NodeOffsetPointXY::node_LatLon(point2), None),
469 ];
470
471 let geo_nodes = NodeSetXY(nodes).to_line_string_xy(ref_pos).into_points();
472 let geo_point1 = geo_types::Point::new(0., 184.95);
473 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x(), 0.01);
474 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y(), 0.01);
475 let geo_point2 = geo_types::Point::new(66., 0.);
476 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x(), 0.01);
477 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y(), 0.01);
478 }
479
480 {
482 let point1 = NodeXY32b::new(
483 OffsetB16::from_meters(0.).unwrap(),
484 OffsetB16::from_meters(185.).unwrap(),
485 );
486 let point2 = NodeXY32b::new(
487 OffsetB16::from_meters(66.).unwrap(),
488 OffsetB16::from_meters(0.).unwrap(),
489 );
490 let nodes = alloc::vec![
491 NodeXY::new(NodeOffsetPointXY::node_XY6(point1), None),
492 NodeXY::new(NodeOffsetPointXY::node_XY6(point2), None),
493 ];
494
495 let geo_nodes = NodeSetXY(nodes).to_line_string_xy(ref_pos).into_points();
496 let geo_point1 = geo_types::Point::new(0., 185.);
497 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x());
498 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y());
499
500 let geo_point2 = geo_types::Point::new(0. + 66., 185. + 0.);
501 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x());
502 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y());
503 }
504
505 {
507 let point1 = NodeXY32b::new(
508 OffsetB16::from_meters(0.).unwrap(),
509 OffsetB16::from_meters(185.).unwrap(),
510 );
511 let point2 = NodeXY32b::new(
512 OffsetB16::from_meters(66.).unwrap(),
513 OffsetB16::from_meters(0.).unwrap(),
514 );
515 let point3 = NodeLLmD64b::new(
516 Longitude::from_deg(ref_pos.x() + 0.001_001), Latitude::from_deg(ref_pos.y() + 0.001_667), );
519 let point4 = NodeXY32b::new(
520 OffsetB16::from_meters(42.).unwrap(),
521 OffsetB16::from_meters(23.).unwrap(),
522 );
523 let nodes = alloc::vec![
524 NodeXY::new(NodeOffsetPointXY::node_XY6(point1), None),
525 NodeXY::new(NodeOffsetPointXY::node_XY6(point2), None),
526 NodeXY::new(NodeOffsetPointXY::node_LatLon(point3), None),
527 NodeXY::new(NodeOffsetPointXY::node_XY6(point4), None),
528 ];
529
530 let geo_nodes = NodeSetXY(nodes).to_line_string_xy(ref_pos).into_points();
531 let geo_point1 = geo_types::Point::new(0., 185.);
532 assert_float_eq::assert_float_absolute_eq!(geo_point1.x(), geo_nodes[0].x());
533 assert_float_eq::assert_float_absolute_eq!(geo_point1.y(), geo_nodes[0].y());
534
535 let geo_point2 = geo_types::Point::new(0. + 66., 185. + 0.);
536 assert_float_eq::assert_float_absolute_eq!(geo_point2.x(), geo_nodes[1].x());
537 assert_float_eq::assert_float_absolute_eq!(geo_point2.y(), geo_nodes[1].y());
538
539 let geo_point3 = geo_types::Point::new(66., 184.95);
540 assert_float_eq::assert_float_absolute_eq!(geo_point3.x(), geo_nodes[2].x(), 0.01);
541 assert_float_eq::assert_float_absolute_eq!(geo_point3.y(), geo_nodes[2].y(), 0.01);
542
543 let geo_point4 = geo_types::Point::new(66. + 42., 184.95 + 23.);
544 assert_float_eq::assert_float_absolute_eq!(geo_point4.x(), geo_nodes[3].x(), 0.01);
545 assert_float_eq::assert_float_absolute_eq!(geo_point4.y(), geo_nodes[3].y(), 0.01);
546 }
547 }
548}