gistools/geometry/wm/
lonlat.rs1use libm::{asin, atan2, cos, fabs, sin, sqrt};
2
3use core::cmp::Ordering;
4use core::f64::consts::PI;
5use core::ops::{Add, Div, Mul, Neg, Sub};
6
7use crate::s2::{S2CellId, S2Point};
8
9#[derive(Clone, Copy, Default, PartialEq, Debug)]
15pub struct LonLat {
16 pub lon: f64,
18 pub lat: f64,
20}
21impl LonLat {
22 pub fn new(lon: f64, lat: f64) -> Self {
25 LonLat { lon, lat }
26 }
27
28 pub fn from_s2cellid(cellid: &S2CellId) -> LonLat {
30 let p = cellid.to_point();
31 LonLat::from_s2_point(&p)
32 }
33
34 pub fn from_s2_point(p: &S2Point) -> LonLat {
36 let lon_rad = atan2(p.y + 0.0, p.x + 0.0);
37 let lat_rad = atan2(p.z, sqrt(p.x * p.x + p.y * p.y));
38 let ll = LonLat::new((lon_rad).to_degrees(), (lat_rad).to_degrees());
39 if !ll.is_valid() {
40 unreachable!();
41 }
42 ll
43 }
44
45 pub fn from_axis(&self, axis: u8) -> f64 {
47 if axis == 0 {
48 self.lon
49 } else {
50 self.lat
51 }
52 }
53
54 pub fn normalize(&mut self) {
56 let mut lon = self.lon;
57 let mut lat = self.lat;
58 while lon < -180. {
59 lon += 360.
60 }
61 while lon > 180. {
62 lon -= 360.
63 }
64 while lat < -90. {
65 lat += 180.
66 }
67 while lat > 90. {
68 lat -= 180.
69 }
70 }
71
72 pub fn coords(self) -> (f64, f64) {
74 (self.lon, self.lat)
75 }
76
77 pub fn is_valid(&self) -> bool {
80 fabs(self.lat) <= (PI / 2.0) && fabs(self.lon) <= PI
81 }
82
83 pub fn to_point(&self) -> S2Point {
101 if !self.is_valid() {
102 unreachable!();
103 }
104 let lon: f64 = (self.lon).to_degrees();
105 let lat: f64 = (self.lat).to_degrees();
106 S2Point::new(
107 cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat), )
111 }
112
113 pub fn to_point_gl(&self) -> S2Point {
115 let lon: f64 = (self.lon).to_degrees();
116 let lat: f64 = (self.lat).to_degrees();
117 S2Point::new(
118 cos(lat) * sin(lon), sin(lat), cos(lat) * cos(lon), )
122 }
123
124 pub fn get_distance(&self, b: &LonLat) -> f64 {
134 if !self.is_valid() || !b.is_valid() {
144 unreachable!();
145 }
146
147 let lat1 = self.lat;
148 let lat2 = b.lat;
149 let lon1 = self.lon;
150 let lon2 = b.lon;
151 let dlat = sin(0.5 * (lat2 - lat1));
152 let dlon = sin(0.5 * (lon2 - lon1));
153 let x = dlat * dlat + dlon * dlon * cos(lat1) * cos(lat2);
154 2. * asin(sqrt(f64::min(1., x)))
155 }
156}
157impl From<&S2CellId> for LonLat {
158 fn from(c: &S2CellId) -> Self {
159 LonLat::from_s2cellid(c)
160 }
161}
162impl From<&S2Point> for LonLat {
163 fn from(p: &S2Point) -> Self {
164 LonLat::from_s2_point(p)
165 }
166}
167impl Add<f64> for LonLat {
168 type Output = LonLat;
169 fn add(self, rhs: f64) -> Self::Output {
170 LonLat::new(self.lat + rhs, self.lon + rhs)
171 }
172}
173impl Add<LonLat> for LonLat {
174 type Output = LonLat;
175 fn add(self, rhs: LonLat) -> Self::Output {
176 LonLat::new(self.lat + rhs.lat, self.lon + rhs.lon)
177 }
178}
179impl Sub<LonLat> for LonLat {
180 type Output = LonLat;
181 fn sub(self, rhs: LonLat) -> Self::Output {
182 LonLat::new(self.lat - rhs.lat, self.lon - rhs.lon)
183 }
184}
185impl Sub<f64> for LonLat {
186 type Output = LonLat;
187 fn sub(self, rhs: f64) -> Self::Output {
188 LonLat::new(self.lat - rhs, self.lon - rhs)
189 }
190}
191impl Mul<f64> for LonLat {
192 type Output = LonLat;
193 fn mul(self, rhs: f64) -> Self::Output {
194 LonLat::new(self.lat * rhs, self.lon * rhs)
195 }
196}
197impl Mul<LonLat> for f64 {
198 type Output = LonLat;
199 fn mul(self, rhs: LonLat) -> Self::Output {
200 LonLat::new(self * rhs.lat, self * rhs.lon)
201 }
202}
203impl Div<f64> for LonLat {
204 type Output = LonLat;
205 fn div(self, rhs: f64) -> Self::Output {
206 LonLat::new(self.lat / rhs, self.lon / rhs)
207 }
208}
209impl Div<LonLat> for LonLat {
210 type Output = LonLat;
211 fn div(self, rhs: LonLat) -> Self::Output {
212 LonLat::new(self.lat / rhs.lat, self.lon / rhs.lon)
213 }
214}
215impl Neg for LonLat {
216 type Output = LonLat;
217 fn neg(self) -> Self::Output {
218 LonLat::new(-self.lat, -self.lon)
219 }
220}
221impl Eq for LonLat {}
222impl Ord for LonLat {
223 fn cmp(&self, other: &Self) -> Ordering {
224 match self.lon.partial_cmp(&other.lon) {
225 Some(Ordering::Equal) => {}
226 other => return other.unwrap(), }
228 match self.lat.partial_cmp(&other.lat) {
229 Some(order) => order,
230 None => Ordering::Equal, }
232 }
233}
234impl PartialOrd for LonLat {
235 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
236 Some(self.cmp(other))
237 }
238}