|
| 1 | +use std::f64::consts::PI; |
| 2 | + |
| 3 | +const EARTH_RADIUS: f64 = 6371000.0; |
| 4 | + |
| 5 | +pub fn rhumb_dist(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 { |
| 6 | + let phi1 = lat1 * PI / 180.00; |
| 7 | + let phi2 = lat2 * PI / 180.00; |
| 8 | + let del_phi = phi2 - phi1; |
| 9 | + let mut del_lambda = (long2 - long1) * PI / 180.00; |
| 10 | + |
| 11 | + if del_lambda > PI { |
| 12 | + del_lambda -= 2.00 * PI; |
| 13 | + } else if del_lambda < -PI { |
| 14 | + del_lambda += 2.00 * PI; |
| 15 | + } |
| 16 | + |
| 17 | + let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.00 + PI / 4.00).tan()).ln(); |
| 18 | + let q = match del_psi.abs() > 1e-12 { |
| 19 | + true => del_phi / del_psi, |
| 20 | + false => phi1.cos(), |
| 21 | + }; |
| 22 | + |
| 23 | + (del_phi.powf(2.00) + (q * del_lambda).powf(2.00)).sqrt() * EARTH_RADIUS |
| 24 | +} |
| 25 | + |
| 26 | +pub fn rhumb_bearing(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 { |
| 27 | + let phi1 = lat1 * PI / 180.00; |
| 28 | + let phi2 = lat2 * PI / 180.00; |
| 29 | + let mut del_lambda = (long2 - long1) * PI / 180.00; |
| 30 | + |
| 31 | + if del_lambda > PI { |
| 32 | + del_lambda -= 2.00 * PI; |
| 33 | + } else if del_lambda < -PI { |
| 34 | + del_lambda += 2.00 * PI; |
| 35 | + } |
| 36 | + |
| 37 | + let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.00 + PI / 4.00).tan()).ln(); |
| 38 | + let bearing = del_lambda.atan2(del_psi) * 180.0 / PI; |
| 39 | + (bearing + 360.00) % 360.00 |
| 40 | +} |
| 41 | +pub fn rhumb_destination(lat: f64, long: f64, distance: f64, bearing: f64) -> (f64, f64) { |
| 42 | + let del = distance / EARTH_RADIUS; |
| 43 | + let phi1 = lat * PI / 180.00; |
| 44 | + let lambda1 = long * PI / 180.00; |
| 45 | + let theta = bearing * PI / 180.00; |
| 46 | + |
| 47 | + let del_phi = del * theta.cos(); |
| 48 | + let phi2 = (phi1 + del_phi).clamp(-PI / 2.0, PI / 2.0); |
| 49 | + |
| 50 | + let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.0 + PI / 4.0).tan()).ln(); |
| 51 | + let q = match del_psi.abs() > 1e-12 { |
| 52 | + true => del_phi / del_psi, |
| 53 | + false => phi1.cos(), |
| 54 | + }; |
| 55 | + |
| 56 | + let del_lambda = del * theta.sin() / q; |
| 57 | + let lambda2 = lambda1 + del_lambda; |
| 58 | + |
| 59 | + (phi2 * 180.00 / PI, lambda2 * 180.00 / PI) |
| 60 | +} |
| 61 | + |
| 62 | +//TESTS |
| 63 | +#[cfg(test)] |
| 64 | +mod tests { |
| 65 | + use super::*; |
| 66 | + |
| 67 | + #[test] |
| 68 | + fn test_rhumb_distance() { |
| 69 | + let distance = rhumb_dist(28.5416, 77.2006, 28.5457, 77.1928); |
| 70 | + assert!(distance > 700.00 && distance < 1000.0); |
| 71 | + } |
| 72 | + |
| 73 | + #[test] |
| 74 | + fn test_rhumb_bearing() { |
| 75 | + let bearing = rhumb_bearing(28.5416, 77.2006, 28.5457, 77.1928); |
| 76 | + assert!((bearing - 300.0).abs() < 5.0); |
| 77 | + } |
| 78 | + |
| 79 | + #[test] |
| 80 | + fn test_rhumb_destination_point() { |
| 81 | + let (lat, lng) = rhumb_destination(28.5457, 77.1928, 1000.00, 305.0); |
| 82 | + assert!((lat - 28.550).abs() < 0.010); |
| 83 | + assert!((lng - 77.1851).abs() < 0.010); |
| 84 | + } |
| 85 | +} |
0 commit comments