gistools/geometry/ll/
mod.rs1use super::{S1Angle, S2CellId, S2Point};
2use core::{
3 cmp::Ordering,
4 ops::{Add, Div, Mul, Neg, Sub},
5};
6use libm::{asin, atan2, cos, fmin, sin, sqrt};
7use s2json::{GetM, GetXY, MValue, NewXY, VectorPoint};
8
9#[derive(Clone, PartialEq, Debug)]
38pub struct LonLat<M: Clone + Default = MValue>(pub VectorPoint<M>);
39impl GetXY for LonLat {
40 fn x(&self) -> f64 {
41 self.0.x
42 }
43 fn y(&self) -> f64 {
44 self.0.y
45 }
46}
47impl<M: Clone + Default> GetM<M> for LonLat<M> {
48 fn m(&self) -> Option<&M> {
49 self.0.m.as_ref()
50 }
51}
52impl NewXY for LonLat {
53 fn new_xy(x: f64, y: f64) -> LonLat {
54 LonLat(VectorPoint::new(x, y, None, None))
55 }
56}
57impl<M: Clone + Default> LonLat<M> {
58 pub fn new(lon: f64, lat: f64, m: Option<M>) -> LonLat<M> {
60 LonLat(VectorPoint::new(lon, lat, None, m))
61 }
62
63 pub fn take(&mut self) -> VectorPoint<M> {
65 core::mem::take(&mut self.0)
66 }
67
68 #[inline]
70 pub fn lon(&self) -> f64 {
71 self.0.x
72 }
73
74 #[inline]
76 pub fn lat(&self) -> f64 {
77 self.0.y
78 }
79
80 pub fn from_s2cellid(cellid: S2CellId) -> Self {
82 let p = cellid.to_point();
83 Self::from_s2_point(&p)
84 }
85
86 pub fn from_s2_point(p: &S2Point) -> Self {
88 let lon_rad = atan2(p.y + 0.0, p.x + 0.0);
89 let lat_rad = atan2(p.z, sqrt(p.x * p.x + p.y * p.y));
90 let ll = LonLat::new((lon_rad).to_degrees(), (lat_rad).to_degrees(), None);
91 debug_assert!(ll.is_valid(), "from_s2_point() called on invalid LonLat");
92
93 ll
94 }
95
96 pub fn normalize(&mut self) {
98 let mut lon = self.lon();
99 let mut lat = self.lat();
100 lon = ((((lon + 180.) % 360.) + 360.) % 360.) - 180.;
102 lat = lat.clamp(-90., 90.);
104
105 self.0.x = lon;
106 self.0.y = lat;
107 }
108
109 pub fn coords(self) -> (f64, f64) {
111 (self.lon(), self.lat())
112 }
113
114 pub fn to_angles(&self) -> (S1Angle, S1Angle) {
116 (S1Angle::from_degrees(self.lon()), S1Angle::from_degrees(self.lat()))
117 }
118
119 pub fn is_valid(&self) -> bool {
122 let lat = self.lat();
125 let lon = self.lon();
126
127 (-90.0..=90.0).contains(&lat) && (-180.0..=180.0).contains(&lon)
128 }
129
130 pub fn to_point(&self) -> S2Point {
143 debug_assert!(self.is_valid(), "to_point() called on invalid LonLat");
144 let lon: f64 = (self.lon()).to_radians();
145 let lat: f64 = (self.lat()).to_radians();
146 S2Point::new(
147 cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat), )
151 }
152
153 pub fn to_point_gl(&self) -> S2Point {
155 debug_assert!(self.is_valid(), "to_point_gl() called on invalid LonLat");
156 let lon: f64 = (self.lon()).to_radians();
157 let lat: f64 = (self.lat()).to_radians();
158 S2Point::new(
159 cos(lat) * sin(lon), sin(lat), cos(lat) * cos(lon), )
163 }
164
165 pub fn get_distance<M2: Clone + Default>(&self, b: &LonLat<M2>) -> f64 {
175 debug_assert!(self.is_valid(), "get_bearing() called on invalid LonLat (self)");
185 debug_assert!(b.is_valid(), "get_bearing() called on invalid LonLat (b)");
186
187 let lat1 = self.lat().to_radians();
188 let lat2 = b.lat().to_radians();
189 let lon1 = self.lon().to_radians();
190 let lon2 = b.lon().to_radians();
191 let dlat = sin(0.5 * (lat2 - lat1));
192 let dlon = sin(0.5 * (lon2 - lon1));
193 let x = dlat * dlat + dlon * dlon * cos(lat1) * cos(lat2);
194 2. * asin(sqrt(fmin(1., x)))
195 }
196
197 pub fn get_bearing<M2: Clone + Default>(&self, b: &LonLat<M2>) -> f64 {
199 debug_assert!(self.is_valid(), "get_bearing() called on invalid LonLat (self)");
200 debug_assert!(b.is_valid(), "get_bearing() called on invalid LonLat (self)");
201 let lat1 = self.lat().to_radians();
202 let lat2 = b.lat().to_radians();
203 let lon1 = self.lon().to_radians();
204 let lon2 = b.lon().to_radians();
205 let y = sin(lon2 - lon1) * cos(lat2);
206 let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
207
208 (atan2(y, x).to_degrees() + 360.) % 360.
209 }
210}
211impl<M: Clone + Default> From<S2CellId> for LonLat<M> {
212 fn from(c: S2CellId) -> Self {
213 LonLat::from_s2cellid(c)
214 }
215}
216impl<M: Clone + Default> From<&S2Point> for LonLat<M> {
217 fn from(p: &S2Point) -> Self {
218 LonLat::from_s2_point(p)
219 }
220}
221impl<M1: Clone + Default, M2: Clone + Default> Add<LonLat<M2>> for LonLat<M1> {
222 type Output = LonLat<M1>;
223 fn add(self, rhs: LonLat<M2>) -> Self::Output {
224 LonLat::new(self.lon() + rhs.lon(), self.lat() + rhs.lat(), self.0.m)
225 }
226}
227impl<M1: Clone + Default, M2: Clone + Default> Sub<LonLat<M2>> for LonLat<M1> {
228 type Output = LonLat<M1>;
229 fn sub(self, rhs: LonLat<M2>) -> Self::Output {
230 LonLat::new(self.lon() - rhs.lon(), self.lat() - rhs.lat(), self.0.m)
231 }
232}
233impl<M1: Clone + Default, M2: Clone + Default> Mul<LonLat<M2>> for LonLat<M1> {
234 type Output = LonLat<M1>;
235 fn mul(self, rhs: LonLat<M2>) -> Self::Output {
236 LonLat::new(self.lon() * rhs.lon(), self.lat() * rhs.lat(), self.0.m)
237 }
238}
239impl<M1: Clone + Default, M2: Clone + Default> Div<LonLat<M2>> for LonLat<M1> {
240 type Output = LonLat<M1>;
241 fn div(self, rhs: LonLat<M2>) -> Self::Output {
242 LonLat::new(self.lon() / rhs.lon(), self.lat() / rhs.lat(), self.0.m)
243 }
244}
245impl Neg for LonLat {
246 type Output = LonLat;
247 fn neg(self) -> Self::Output {
248 LonLat::new(-self.lon(), -self.lat(), self.0.m)
249 }
250}
251impl Eq for LonLat {}
252impl Ord for LonLat {
253 fn cmp(&self, other: &Self) -> Ordering {
254 match self.lon().partial_cmp(&other.lon()) {
255 Some(Ordering::Equal) => {}
256 other => return other.unwrap(), }
258 match self.lat().partial_cmp(&other.lat()) {
259 Some(order) => order,
260 None => Ordering::Equal, }
262 }
263}
264impl PartialOrd for LonLat {
265 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
266 Some(self.cmp(other))
267 }
268}