geo/algorithm/
haversine_bearing.rs1use crate::{CoordFloat, Point};
2
3pub trait HaversineBearing<T: CoordFloat> {
9 fn haversine_bearing(&self, point: Point<T>) -> T;
25}
26
27impl<T> HaversineBearing<T> for Point<T>
28where
29 T: CoordFloat,
30{
31 fn haversine_bearing(&self, point: Point<T>) -> T {
32 let (lng_a, lat_a) = (self.x().to_radians(), self.y().to_radians());
33 let (lng_b, lat_b) = (point.x().to_radians(), point.y().to_radians());
34 let delta_lng = lng_b - lng_a;
35 let s = lat_b.cos() * delta_lng.sin();
36 let c = lat_a.cos() * lat_b.sin() - lat_a.sin() * lat_b.cos() * delta_lng.cos();
37
38 T::atan2(s, c).to_degrees()
39 }
40}
41
42#[cfg(test)]
43mod test {
44 use crate::point;
45 use crate::HaversineBearing;
46 use crate::HaversineDestination;
47
48 #[test]
49 fn north_bearing() {
50 let p_1 = point!(x: 9., y: 47.);
51 let p_2 = point!(x: 9., y: 48.);
52 let bearing = p_1.haversine_bearing(p_2);
53 assert_relative_eq!(bearing, 0.);
54 }
55
56 #[test]
57 fn equatorial_east_bearing() {
58 let p_1 = point!(x: 9., y: 0.);
59 let p_2 = point!(x: 10., y: 0.);
60 let bearing = p_1.haversine_bearing(p_2);
61 assert_relative_eq!(bearing, 90.);
62 }
63
64 #[test]
65 fn east_bearing() {
66 let p_1 = point!(x: 9., y: 10.);
67 let p_2 = point!(x: 18.12961917258341, y: 9.875828894123304);
68
69 let bearing = p_1.haversine_bearing(p_2);
70 assert_relative_eq!(bearing, 90.);
71 }
72
73 #[test]
74 fn northeast_bearing() {
75 let p_1 = point!(x: 9.177789688110352f64, y: 48.776781529534965);
76 let p_2 = point!(x: 9.274409949623548, y: 48.84033274015048);
77 let bearing = p_1.haversine_bearing(p_2);
78 assert_relative_eq!(bearing, 45., epsilon = 1.0e-6);
79 }
80
81 #[test]
82 fn consistent_with_destination() {
83 let p_1 = point!(x: 9.177789688110352f64, y: 48.776781529534965);
84 let p_2 = p_1.haversine_destination(45., 10000.);
85
86 let b_1 = p_1.haversine_bearing(p_2);
87 assert_relative_eq!(b_1, 45., epsilon = 1.0e-6);
88 }
89}