1use std::fmt::Display;
3use std::str::FromStr;
4
5use crate::point::Point2d;
6use crate::traits::{Coord2dLike, IsOk, LngLatLike};
7use crate::UtilesCoreResult;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct LngLat {
12 pub xy: Point2d<f64>,
14}
15
16impl LngLat {
17 #[must_use]
19 pub fn new(lng: f64, lat: f64) -> Self {
20 LngLat {
21 xy: Point2d::new(lng, lat),
22 }
23 }
24
25 #[must_use]
27 pub fn lng(&self) -> f64 {
28 self.xy.x
29 }
30
31 #[must_use]
33 pub fn lat(&self) -> f64 {
34 self.xy.y
35 }
36
37 #[must_use]
39 pub fn lon(&self) -> f64 {
40 self.xy.x
41 }
42
43 #[must_use]
45 pub fn x(&self) -> f64 {
46 self.xy.x
47 }
48
49 #[must_use]
51 pub fn y(&self) -> f64 {
52 self.xy.y
53 }
54
55 #[must_use]
57 pub fn geo_wrap(&self) -> LngLat {
58 LngLat {
59 xy: Point2d::new(Self::geo_wrap_lng(self.xy.x), self.xy.y),
60 }
61 }
62
63 #[must_use]
66 pub fn geo_wrap_lng(lng: f64) -> f64 {
67 if lng < -180.0 {
68 lng + 360.0
69 } else if lng > 180.0 {
70 lng - 360.0
71 } else {
72 lng
73 }
74 }
75}
76
77impl Coord2dLike for LngLat {
78 fn x(&self) -> f64 {
79 self.xy.x
80 }
81
82 fn y(&self) -> f64 {
83 self.xy.y
84 }
85}
86
87impl LngLatLike for LngLat {
88 fn lng(&self) -> f64 {
89 self.xy.x
90 }
91
92 fn lat(&self) -> f64 {
93 self.xy.y
94 }
95}
96
97impl IsOk for LngLat {
98 fn ok(&self) -> UtilesCoreResult<Self> {
99 if self.xy.x >= -180.0
100 && self.xy.x <= 180.0
101 && self.xy.y >= -90.0
102 && self.xy.y <= 90.0
103 {
104 Ok(*self)
105 } else {
106 Err(crate::UtilesCoreError::InvalidLngLat(format!(
107 "Invalid LngLat coordinates: x: {}, y: {}",
108 self.xy.x, self.xy.y
109 )))
110 }
111 }
112}
113
114impl Display for LngLat {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 write!(f, "({}, {})", self.xy.x, self.xy.y)
117 }
118}
119
120impl IntoIterator for LngLat {
121 type Item = (f64, f64);
122 type IntoIter = std::iter::Once<Self::Item>;
123
124 fn into_iter(self) -> Self::IntoIter {
125 std::iter::once((self.xy.x, self.xy.y))
126 }
127}
128
129impl From<(f64, f64)> for LngLat {
130 fn from(xy: (f64, f64)) -> Self {
131 LngLat::new(xy.0, xy.1)
132 }
133}
134
135impl FromStr for LngLat {
136 type Err = std::num::ParseFloatError; fn from_str(s: &str) -> Result<Self, Self::Err> {
139 let parts: Vec<&str> = s.split(',').collect();
141 let x = parts[0].parse::<f64>()?;
143 let y = parts[1].parse::<f64>()?;
144 Ok(LngLat::new(x, y))
145 }
146}
147
148#[must_use]
150#[inline]
151pub fn wrap_lon(lon: f64) -> f64 {
152 (lon + 180.0).rem_euclid(360.0) - 180.0
153}