haversine_rs/lib.rs
1pub mod units;
2pub mod point;
3
4use point::Point;
5use units::Unit;
6
7/// Calculates the haversine of the given angle.
8///
9/// # Examples
10///
11/// ```
12/// use haversine_rs::haversine;
13///
14/// let theta = 0.0;
15/// assert!(haversine(theta) == 0.0);
16///
17/// let theta = 1.0;
18/// assert!(haversine(theta) == 0.22984884706593015);
19/// ```
20pub fn haversine(theta: f64) -> f64 {
21 (theta / 2.0).sin().powi(2)
22}
23
24/// Calculates the distance between two points.
25///
26/// # Examples
27///
28/// ```
29/// use haversine_rs::point::Point;
30/// use haversine_rs::units::Unit;
31/// use haversine_rs::distance;
32///
33/// let point_a = Point::new(40.7767644, -73.9761399);
34/// let point_b = Point::new(40.771209, -73.9673991);
35///
36/// let distance = distance(point_a, point_b, Unit::Miles);
37///
38/// assert!(distance == 0.5971169354597881);
39/// ```
40pub fn distance(point_a: Point, point_b: Point, unit: Unit) -> f64 {
41 let r = unit.earth_radius();
42
43 let point_a = point_a.to_radians();
44 let point_b = point_b.to_radians();
45
46 let delta_latitude = point_b.latitude - point_a.latitude;
47 let delta_longitude = point_b.longitude - point_a.longitude;
48
49 let a = haversine(delta_latitude) + point_a.latitude.cos()
50 * point_b.latitude.cos() * haversine(delta_longitude);
51
52 2.0 * r * a.sqrt().atan2((1.0 - a).sqrt())
53}
54
55/// Calculates the distance between multiple points.
56///
57/// # Examples
58///
59/// ```
60/// use haversine_rs::point::Point;
61/// use haversine_rs::units::Unit;
62/// use haversine_rs::distance_vec;
63///
64/// let point_x = Point::new(40.7767644, -73.9761399);
65/// let point_y = Point::new(40.773987, -73.971769);
66/// let point_z = Point::new(40.771209, -73.9673991);
67///
68/// let distance = distance_vec(vec![point_x, point_y, point_z], Unit::Miles);
69///
70/// assert!(distance == 0.5971169371087564);
71/// ```
72pub fn distance_vec(points: Vec<Point>, unit: Unit) -> f64 {
73 let mut total = 0.0;
74
75 for i in 0..=points.len() {
76 if i == points.len() - 1 {
77 break;
78 }
79 total += distance(points[i], points[i + 1], unit);
80 }
81 total
82}
83
84/// Calculates the bearing between two points.
85///
86/// # Examples
87///
88/// ```
89/// use haversine_rs::point::Point;
90/// use haversine_rs::bearing;
91///
92/// let point_a = Point::new(40.7767644, -73.9761399);
93/// let point_b = Point::new(40.771209, -73.9673991);
94///
95/// let bearing = bearing(point_a, point_b);
96///
97/// assert!(bearing == 130.00282723561608);
98/// ```
99pub fn bearing(point_a: Point, point_b: Point) -> f64 {
100 let point_a = point_a.to_radians();
101 let point_b = point_b.to_radians();
102
103 let delta_longitude = point_b.longitude - point_a.longitude;
104
105 let y = delta_longitude.sin() * point_b.latitude.cos();
106 let x = point_a.latitude.cos() * point_b.latitude.sin()
107 - point_a.latitude.sin() * point_b.latitude.cos() * delta_longitude.cos();
108
109 let result = y.atan2(x).to_degrees();
110
111 if result < 0.0 {
112 result + 360.0
113 } else {
114 result
115 }
116}
117
118/// Calculates the point at the given distance and bearing from the origin point.
119///
120/// # Examples
121///
122/// ```
123/// use haversine_rs::point::Point;
124/// use haversine_rs::units::Unit;
125/// use haversine_rs::find_point;
126///
127/// let origin = Point::new(40.7767644, -73.9761399);
128/// let distance = 0.5971169354597881;
129/// let bearing = 130.00282723561608;
130///
131/// let point = find_point(origin, distance, bearing, Unit::Miles);
132///
133/// println!("{:?}", point);
134/// assert!(point.latitude == 40.771209);
135/// assert!(point.longitude == -73.9673991);
136/// ```
137pub fn find_point(origin: Point, distance: f64, bearing: f64, unit: Unit) -> Point {
138 let r = unit.earth_radius();
139
140 let origin = origin.to_radians();
141
142 let delta = distance / r;
143
144 let theta = bearing.to_radians();
145
146 let latitude = (origin.latitude.sin() * delta.cos()
147 + origin.latitude.cos() * delta.sin() * theta.cos()).asin();
148
149 let longitude = origin.longitude
150 + (theta.sin() * delta.sin() * origin.latitude.cos()).atan2(delta.cos() - origin.latitude.sin() * latitude.sin());
151
152 Point::new(latitude.to_degrees(), longitude.to_degrees())
153}