geometry_strategy/geographic/
direct_karney.rs1use geometry_cs::Spheroid;
8
9use super::direct::DirectResult;
10
11#[cfg(feature = "std")]
12use geometry_coords::series_expansion::{
13 coefficients_a3, coefficients_c1, coefficients_c1p, coefficients_c3, evaluate_a1,
14 sin_cos_series,
15};
16
17#[cfg(feature = "std")]
18use super::direct::normalize_longitude;
19#[cfg(feature = "std")]
20use super::spheroid_calc::SpheroidCalc;
21
22#[derive(Debug, Clone, Copy)]
29pub struct KarneyDirect {
30 pub spheroid: Spheroid,
32}
33
34impl KarneyDirect {
35 pub const WGS84: Self = Self {
37 spheroid: Spheroid::WGS84,
38 };
39
40 #[cfg(feature = "std")]
48 #[inline]
49 #[must_use]
50 #[allow(
51 clippy::many_single_char_names,
52 clippy::similar_names,
53 clippy::float_cmp,
54 reason = "names and exact zero branches follow the cited Karney formula"
55 )]
56 pub fn apply(&self, lon1: f64, lat1: f64, distance: f64, azimuth12: f64) -> DirectResult {
57 let calc = SpheroidCalc::from(self.spheroid);
58 let b = calc.b;
59 let f = calc.f;
60 let one_minus_f = 1.0 - f;
61 let two_minus_f = 2.0 - f;
62 let n = f / two_minus_f;
63 let e2 = f * two_minus_f;
64 let ep2 = e2 / (one_minus_f * one_minus_f);
65
66 let sin_alpha1 = azimuth12.sin();
67 let sin_alpha1 = if sin_alpha1.abs() <= f64::EPSILON {
68 0.0
69 } else {
70 sin_alpha1
71 };
72 let cos_alpha1 = azimuth12.cos();
73 let cos_alpha1 = if cos_alpha1.abs() <= f64::EPSILON {
74 0.0
75 } else {
76 cos_alpha1
77 };
78 let mut sin_beta1 = lat1.sin() * one_minus_f;
79 let mut cos_beta1 = lat1.cos();
80 let beta_norm = sin_beta1.hypot(cos_beta1);
81 sin_beta1 /= beta_norm;
82 cos_beta1 = (cos_beta1 / beta_norm).max(0.0);
83
84 let sin_alpha0 = sin_alpha1 * cos_beta1;
85 let cos_alpha0 = cos_alpha1.hypot(sin_alpha1 * sin_beta1);
86 let k2 = cos_alpha0 * cos_alpha0 * ep2;
87 let epsilon = k2 / (2.0 * (1.0 + (1.0 + k2).sqrt()) + k2);
88 let expansion_a1 = evaluate_a1(epsilon);
89 let coefficients_c1 = coefficients_c1(epsilon);
90 let tau12 = distance / (b * (1.0 + expansion_a1));
91 let sin_tau12 = tau12.sin();
92 let cos_tau12 = tau12.cos();
93
94 let mut sin_sigma1 = sin_beta1;
95 let sin_omega1 = sin_alpha0 * sin_beta1;
96 let mut cos_sigma1 = if sin_beta1.abs() > f64::EPSILON || cos_alpha1.abs() > f64::EPSILON {
100 cos_beta1 * cos_alpha1
101 } else {
102 1.0
103 };
104 let cos_omega1 = cos_sigma1;
105 let sigma_norm = sin_sigma1.hypot(cos_sigma1);
106 sin_sigma1 /= sigma_norm;
107 cos_sigma1 /= sigma_norm;
108
109 let b11 = sin_cos_series(sin_sigma1, cos_sigma1, &coefficients_c1);
110 let sin_b11 = b11.sin();
111 let cos_b11 = b11.cos();
112 let sin_tau1 = sin_sigma1 * cos_b11 + cos_sigma1 * sin_b11;
113 let cos_tau1 = cos_sigma1 * cos_b11 - sin_sigma1 * sin_b11;
114 let coefficients_c1p = coefficients_c1p(epsilon);
115 let b12 = -sin_cos_series(
116 sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
117 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
118 &coefficients_c1p,
119 );
120 let sigma12 = tau12 - (b12 - b11);
121 let sin_sigma12 = sigma12.sin();
122 let cos_sigma12 = sigma12.cos();
123 let sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;
124 let cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;
125
126 let sin_alpha2 = sin_alpha0;
127 let cos_alpha2 = cos_alpha0 * cos_sigma2;
128 let reverse_azimuth = sin_alpha2.atan2(cos_alpha2);
129
130 let sin_beta2 = cos_alpha0 * sin_sigma2;
131 let cos_beta2 = sin_alpha0.hypot(cos_alpha0 * cos_sigma2);
132 let lat2 = sin_beta2.atan2(one_minus_f * cos_beta2);
133
134 let sin_omega2 = sin_alpha0 * sin_sigma2;
135 let cos_omega2 = cos_sigma2;
136 let omega12 = (sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1)
137 .atan2(cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);
138 let coefficients_a3 = coefficients_a3(n);
139 let a3 = coefficients_a3
140 .as_slice()
141 .iter()
142 .rev()
143 .fold(0.0, |value, coefficient| value * epsilon + coefficient);
144 let a3c = -f * sin_alpha0 * a3;
145 let coefficients_c3 = coefficients_c3(n, epsilon);
146 let b31 = sin_cos_series(sin_sigma1, cos_sigma1, &coefficients_c3);
147 let b32 = sin_cos_series(sin_sigma2, cos_sigma2, &coefficients_c3);
148 let lambda12 = omega12 + a3c * (sigma12 + b32 - b31);
149
150 DirectResult::solved(
151 lon1,
152 lat1,
153 azimuth12,
154 self.spheroid,
155 normalize_longitude(lon1 + lambda12),
156 lat2,
157 reverse_azimuth,
158 )
159 }
160}
161
162impl Default for KarneyDirect {
163 #[inline]
164 fn default() -> Self {
165 Self::WGS84
166 }
167}