1use crate::{
2 constants::EARTH_RADIUS, latlng::LatLng, point::Point, rectangle::Rectangle, utils::deg_to_rad,
3};
4
5pub fn point_to_point_distance_cosine(start_point: Point, end_point: Point) -> f64 {
8 (start_point.y.sin() * end_point.y.sin()
9 + start_point.y.cos() * end_point.y.cos() * (start_point.x - end_point.x).cos())
10 .acos()
11 * EARTH_RADIUS
12}
13
14pub fn point_to_point_haversine(start: LatLng, end: LatLng) -> f64 {
16 let start_radian = start.convert_to_radian();
17 let end_radian = end.convert_to_radian();
18 let dif_lat = end_radian.lat - start_radian.lat;
19 let dif_lng = end_radian.lng - start_radian.lng;
20
21 let a = (dif_lat / 2.0).sin() * (dif_lat / 2.0).sin()
22 + start_radian.lat.cos()
23 * end_radian.lat.cos()
24 * (dif_lng / 2.0).sin()
25 * (dif_lng / 2.0).sin();
26 let c = 2.0 * (a.sqrt().atan2((1.0 - a).sqrt()));
27 let d = EARTH_RADIUS * c;
28 return d;
29}
30
31pub fn point_to_point_cartesian_distance(source: Point, target: Point) -> f64 {
33 ((target.x - source.x).powi(2) + (target.y - source.y).powi(2)).sqrt()
34}
35
36pub fn point_to_rectangle_distance(p: Point, r: Rectangle) -> f64 {
40 let pre_max_x = (r.min_point.x - p.x).max(0.0);
41 let dx = (pre_max_x).max(p.x - r.max_point.x);
42 let pre_max_y = (r.min_point.y - p.y).max(0.0);
43 let dy = (pre_max_y).max(p.y - r.max_point.y);
44 return (dx * dx + dy * dy).sqrt();
45}
46
47pub fn point_to_rectangle_distance_geographic(l: LatLng, b: Rectangle) -> f64 {
51 let converted = l.convert_to_radian();
52 let c_as_point = converted.convert_to_point();
53
54 if check_between_longs(&l, &b) {
55 if l.lat < b.min_point.y {
58 return point_to_point_distance_cosine(
59 c_as_point,
60 Point {
61 x: c_as_point.x,
62 y: deg_to_rad(b.min_point.y),
63 },
64 );
65 }
66 if l.lat > b.max_point.y {
67 return point_to_point_distance_cosine(
68 c_as_point,
69 Point {
70 x: c_as_point.x,
71 y: deg_to_rad(b.max_point.y),
72 },
73 );
74 }
75 return 0.0;
77 }
78
79 if l.lng < b.min_point.x {
81 if l.lat > b.max_point.y {
83 return point_to_point_distance_cosine(
84 c_as_point,
85 Point {
86 x: deg_to_rad(b.min_point.x),
87 y: deg_to_rad(b.max_point.y),
88 },
89 );
90 }
91 if l.lat < b.min_point.y {
93 return point_to_point_distance_cosine(
94 c_as_point,
95 Point {
96 x: deg_to_rad(b.min_point.x),
97 y: deg_to_rad(b.min_point.y),
98 },
99 );
100 }
101 return point_to_point_distance_cosine(
103 c_as_point,
104 Point {
105 x: deg_to_rad(b.min_point.x),
106 y: c_as_point.y,
107 },
108 );
109 }
110
111 if l.lat > b.max_point.y {
114 return point_to_point_distance_cosine(
115 c_as_point,
116 Point {
117 x: deg_to_rad(b.max_point.x),
118 y: deg_to_rad(b.max_point.y),
119 },
120 );
121 }
122 if l.lat < b.min_point.y {
124 return point_to_point_distance_cosine(
125 c_as_point,
126 Point {
127 x: deg_to_rad(b.max_point.x),
128 y: deg_to_rad(b.min_point.y),
129 },
130 );
131 }
132 return point_to_point_distance_cosine(
134 c_as_point,
135 Point {
136 x: deg_to_rad(b.max_point.x),
137 y: c_as_point.y,
138 },
139 );
140}
141
142fn check_between_longs(l: &LatLng, b: &Rectangle) -> bool {
143 if l.lng >= b.min_point.x && l.lng <= b.max_point.x {
144 return true;
145 }
146 return false;
147}
148
149pub fn mid_point_geographic_coordinates(a: LatLng, b: LatLng) -> LatLng {
152 let bx = b.lat.cos() * (b.lng - a.lng).cos();
153 let by = b.lat.cos() * (b.lng - a.lng).sin();
154 let lng_mid = a.lng + by.atan2(a.lat.cos() + bx); let lat_mid = (a.lat.sin() + b.lat.sin())
156 .atan2(((a.lat.cos() + bx) * (a.lat.cos() + bx) + by * by).sqrt());
157
158 LatLng {
159 lng: lng_mid,
160 lat: lat_mid,
161 }
162}
163
164pub fn find_bearing(start_point: Point, end_point: Point) -> f64 {
167 let y = (end_point.x - start_point.x).sin() * end_point.y.cos();
168 let x = start_point.y.cos() * end_point.y.sin()
169 - start_point.y.sin() * end_point.y.cos() * (end_point.x - start_point.x).cos();
170 y.atan2(x)
171}
172
173#[cfg(test)]
174mod tests {
175 use crate::utils::deg_to_rad;
176
177 use super::*;
178
179 #[test]
180 fn test_point_to_point_distance_cosine() {
181 let start_lat = 51.27936;
182 let start_lng = 1.07263;
183 let end_lat = 51.27741;
184 let end_lng = 1.07281;
185
186 let start_point = Point {
187 x: deg_to_rad(start_lng),
188 y: deg_to_rad(start_lat),
189 };
190
191 let end_point = Point {
192 x: deg_to_rad(end_lng),
193 y: deg_to_rad(end_lat),
194 };
195
196 let distance = point_to_point_distance_cosine(start_point, end_point);
197
198 assert_eq!(distance.round(), 217.0);
199 }
200 #[test]
201 fn test_point_to_point_distance_haversine() {
202 let start_lat = 51.27936;
203 let start_lng = 1.07263;
204 let end_lat = 51.27741;
205 let end_lng = 1.07281;
206
207 let start_lat_lng = LatLng {
208 lat: start_lat,
209 lng: start_lng,
210 };
211
212 let end_lat_lng = LatLng {
213 lat: end_lat,
214 lng: end_lng,
215 };
216
217 let distance = point_to_point_haversine(start_lat_lng, end_lat_lng);
218
219 assert_eq!(distance.round(), 217.0);
220 }
221
222 #[test]
223 fn test_point_to_bounding_box_geographic() {
224 let test_bbox = Rectangle {
225 min_point: Point {
226 x: 1.070678,
227 y: 51.279336,
228 },
229 max_point: Point {
230 x: 1.071720,
231 y: 51.277559,
232 },
233 };
234
235 let start_point = LatLng {
237 lat: 51.27936,
238 lng: 1.07263,
239 };
240 let distance = point_to_rectangle_distance_geographic(start_point, test_bbox);
241
242 assert_eq!(distance.round(), 210.0);
243 }
244}