|
| 1 | +// vincenty.rs |
| 2 | +const WGS84_A: f64 = 6378137.0; // Semi-major axis in meters |
| 3 | +const WGS84_B: f64 = 6356752.314245; // Semi-minor axis in meters |
| 4 | +const WGS84_F: f64 = 1.0 / 298.257223563; // Flattening |
| 5 | + |
| 6 | +pub fn vincenty(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 { |
| 7 | + let u1 = (1.0 - WGS84_F) * lat1.to_radians().tan(); |
| 8 | + let u2 = (1.0 - WGS84_F) * lat2.to_radians().tan(); |
| 9 | + let l = (lon2 - lon1).to_radians(); |
| 10 | + |
| 11 | + let sin_u1 = u1.sin(); |
| 12 | + let cos_u1 = u1.cos(); |
| 13 | + let sin_u2 = u2.sin(); |
| 14 | + let cos_u2 = u2.cos(); |
| 15 | + |
| 16 | + let lambda = l; |
| 17 | + let (sin_lambda, cos_lambda) = (lambda.sin(), lambda.cos()); |
| 18 | + |
| 19 | + let mut sin_sigma; |
| 20 | + let mut cos_sigma; |
| 21 | + let mut cos_sq_alpha; |
| 22 | + let mut cos2_sigma_m; |
| 23 | + let mut sigma = 0.0; |
| 24 | + |
| 25 | + let mut iteration_limit = 100; |
| 26 | + let mut lambda_prev = 0.0; |
| 27 | + |
| 28 | + while (lambda - lambda_prev).abs() > 1e-12 && iteration_limit > 0 { |
| 29 | + let sin_lambda = lambda.sin(); |
| 30 | + let cos_lambda = lambda.cos(); |
| 31 | + |
| 32 | + sin_sigma = ((cos_u2 * sin_lambda).powi(2) + |
| 33 | + (cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda).powi(2)).sqrt(); |
| 34 | + |
| 35 | + if sin_sigma == 0.0 { |
| 36 | + return 0.0; // coincident points |
| 37 | + } |
| 38 | + |
| 39 | + cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda; |
| 40 | + sigma = sin_sigma.atan2(cos_sigma); |
| 41 | + |
| 42 | + let sin_alpha = cos_u1 * cos_u2 * sin_lambda; |
| 43 | + cos_sq_alpha = 1.0 - sin_alpha.powi(2); |
| 44 | + |
| 45 | + cos2_sigma_m = if cos_sq_alpha == 0.0 { |
| 46 | + 0.0 |
| 47 | + } else { |
| 48 | + cos_u2 * cos_u1 * cos_lambda / cos_sq_alpha |
| 49 | + }; |
| 50 | + |
| 51 | + let c = WGS84_F / 16.0 * cos_sq_alpha * (4.0 + WGS84_F * (4.0 - 3.0 * cos_sq_alpha)); |
| 52 | + |
| 53 | + lambda_prev = lambda; |
| 54 | + lambda = l + (1.0 - c) * WGS84_F * sin_alpha * (sigma + c * sin_sigma * (cos2_sigma_m + c * cos_sigma * (-1.0 + 2.0 * cos2_sigma_m.powi(2)))); |
| 55 | + |
| 56 | + iteration_limit -= 1; |
| 57 | + } |
| 58 | + |
| 59 | + if iteration_limit == 0 { |
| 60 | + return f64::NAN; // formula failed to converge |
| 61 | + } |
| 62 | + |
| 63 | + let u_sq = cos_sq_alpha * (WGS84_A.powi(2) - WGS84_B.powi(2)) / WGS84_B.powi(2); |
| 64 | + let a = 1.0 + u_sq / 16384.0 * (4096.0 + u_sq * (-768.0 + u_sq * (320.0 - 175.0 * u_sq))); |
| 65 | + let b = u_sq / 1024.0 * (256.0 + u_sq * (-128.0 + u_sq * (74.0 - 47.0 * u_sq))); |
| 66 | + |
| 67 | + let delta_sigma = b * sin_sigma * (cos2_sigma_m + b / 4.0 * (cos_sigma * (-1.0 + 2.0 * cos2_sigma_m.powi(2)) - |
| 68 | + b / 6.0 * cos2_sigma_m * (-3.0 + 4.0 * sin_sigma.powi(2)) * (-3.0 + 4.0 * cos2_sigma_m.powi(2)))); |
| 69 | + |
| 70 | + let s = WGS84_B * a * (sigma - delta_sigma); |
| 71 | + |
| 72 | + s // distance in meters |
| 73 | +} |
0 commit comments