File indexing completed on 2025-06-30 08:14:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef BOOST_GEOMETRY_FORMULAS_THOMAS_DIRECT_HPP
0014 #define BOOST_GEOMETRY_FORMULAS_THOMAS_DIRECT_HPP
0015
0016
0017 #include <boost/math/constants/constants.hpp>
0018
0019 #include <boost/geometry/core/assert.hpp>
0020 #include <boost/geometry/core/radius.hpp>
0021
0022 #include <boost/geometry/util/constexpr.hpp>
0023 #include <boost/geometry/util/math.hpp>
0024 #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
0025
0026 #include <boost/geometry/formulas/differential_quantities.hpp>
0027 #include <boost/geometry/formulas/flattening.hpp>
0028 #include <boost/geometry/formulas/result_direct.hpp>
0029
0030
0031 namespace boost { namespace geometry { namespace formula
0032 {
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 template <
0045 typename CT,
0046 bool SecondOrder = true,
0047 bool EnableCoordinates = true,
0048 bool EnableReverseAzimuth = false,
0049 bool EnableReducedLength = false,
0050 bool EnableGeodesicScale = false
0051 >
0052 class thomas_direct
0053 {
0054 static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
0055 static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;
0056 static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcCoordinates || CalcQuantities;
0057
0058 public:
0059 typedef result_direct<CT> result_type;
0060
0061 template <typename T, typename Dist, typename Azi, typename Spheroid>
0062 static inline result_type apply(T const& lo1,
0063 T const& la1,
0064 Dist const& distance,
0065 Azi const& azimuth12,
0066 Spheroid const& spheroid)
0067 {
0068 result_type result;
0069
0070 CT const lon1 = lo1;
0071 CT const lat1 = la1;
0072
0073 CT const c0 = 0;
0074 CT const c1 = 1;
0075 CT const c2 = 2;
0076 CT const c4 = 4;
0077
0078 CT const a = CT(get_radius<0>(spheroid));
0079 CT const b = CT(get_radius<2>(spheroid));
0080 CT const f = formula::flattening<CT>(spheroid);
0081 CT const one_minus_f = c1 - f;
0082
0083 CT const pi = math::pi<CT>();
0084 CT const pi_half = pi / c2;
0085
0086 BOOST_GEOMETRY_ASSERT(-pi <= azimuth12 && azimuth12 <= pi);
0087
0088
0089
0090 CT azi12_alt = azimuth12;
0091 CT lat1_alt = lat1;
0092 bool alter_result = vflip_if_south(lat1, azimuth12, lat1_alt, azi12_alt);
0093
0094 CT const theta1 = math::equals(lat1_alt, pi_half) ? lat1_alt :
0095 math::equals(lat1_alt, -pi_half) ? lat1_alt :
0096 atan(one_minus_f * tan(lat1_alt));
0097 CT const sin_theta1 = sin(theta1);
0098 CT const cos_theta1 = cos(theta1);
0099
0100 CT const sin_a12 = sin(azi12_alt);
0101 CT const cos_a12 = cos(azi12_alt);
0102
0103 CT const M = cos_theta1 * sin_a12;
0104 CT const theta0 = acos(M);
0105 CT const sin_theta0 = sin(theta0);
0106
0107 CT const N = cos_theta1 * cos_a12;
0108 CT const C1 = f * M;
0109 CT const C2 = f * (c1 - math::sqr(M)) / c4;
0110 CT D = 0;
0111 CT P = 0;
0112 if BOOST_GEOMETRY_CONSTEXPR (SecondOrder)
0113 {
0114 D = (c1 - C2) * (c1 - C2 - C1 * M);
0115 P = C2 * (c1 + C1 * M / c2) / D;
0116 }
0117 else
0118 {
0119 D = c1 - c2 * C2 - C1 * M;
0120 P = C2 / D;
0121 }
0122
0123
0124
0125
0126
0127
0128
0129
0130 CT const cos_sigma1 = math::equals(sin_theta0, c0)
0131 ? c1
0132 : normalized1_1(sin_theta1 / sin_theta0);
0133 CT const sigma1 = acos(cos_sigma1);
0134 CT const d = distance / (a * D);
0135 CT const u = 2 * (sigma1 - d);
0136 CT const cos_d = cos(d);
0137 CT const sin_d = sin(d);
0138 CT const cos_u = cos(u);
0139 CT const sin_u = sin(u);
0140
0141 CT const W = c1 - c2 * P * cos_u;
0142 CT const V = cos_u * cos_d - sin_u * sin_d;
0143 CT const Y = c2 * P * V * W * sin_d;
0144 CT X = 0;
0145 CT d_sigma = d - Y;
0146 if BOOST_GEOMETRY_CONSTEXPR (SecondOrder)
0147 {
0148 X = math::sqr(C2) * sin_d * cos_d * (2 * math::sqr(V) - c1);
0149 d_sigma += X;
0150 }
0151 CT const sin_d_sigma = sin(d_sigma);
0152 CT const cos_d_sigma = cos(d_sigma);
0153
0154 if BOOST_GEOMETRY_CONSTEXPR (CalcRevAzimuth)
0155 {
0156 result.reverse_azimuth = atan2(M, N * cos_d_sigma - sin_theta1 * sin_d_sigma);
0157
0158 if (alter_result)
0159 {
0160 vflip_rev_azi(result.reverse_azimuth, azimuth12);
0161 }
0162 }
0163
0164 if BOOST_GEOMETRY_CONSTEXPR (CalcCoordinates)
0165 {
0166 CT const S_sigma = c2 * sigma1 - d_sigma;
0167 CT cos_S_sigma = 0;
0168 CT H = C1 * d_sigma;
0169 if BOOST_GEOMETRY_CONSTEXPR (SecondOrder)
0170 {
0171 cos_S_sigma = cos(S_sigma);
0172 H = H * (c1 - C2) - C1 * C2 * sin_d_sigma * cos_S_sigma;
0173 }
0174 CT const d_eta = atan2(sin_d_sigma * sin_a12, cos_theta1 * cos_d_sigma - sin_theta1 * sin_d_sigma * cos_a12);
0175 CT const d_lambda = d_eta - H;
0176
0177 result.lon2 = lon1 + d_lambda;
0178
0179 if (! math::equals(M, c0))
0180 {
0181 CT const sin_a21 = sin(result.reverse_azimuth);
0182 CT const tan_theta2 = (sin_theta1 * cos_d_sigma + N * sin_d_sigma) * sin_a21 / M;
0183 result.lat2 = atan(tan_theta2 / one_minus_f);
0184 }
0185 else
0186 {
0187 CT const sigma2 = S_sigma - sigma1;
0188
0189
0190 CT const tan_theta2 = cos(sigma2) / math::abs(sin(sigma2));
0191 result.lat2 = atan(tan_theta2 / one_minus_f);
0192 }
0193
0194 if (alter_result)
0195 {
0196 result.lat2 = -result.lat2;
0197 }
0198 }
0199
0200 if BOOST_GEOMETRY_CONSTEXPR (CalcQuantities)
0201 {
0202 typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
0203 quantities::apply(lon1, lat1, result.lon2, result.lat2,
0204 azimuth12, result.reverse_azimuth,
0205 b, f,
0206 result.reduced_length, result.geodesic_scale);
0207 }
0208
0209 if BOOST_GEOMETRY_CONSTEXPR (CalcCoordinates)
0210 {
0211
0212
0213
0214
0215 math::detail::normalize_angle_cond<radian>(result.lon2);
0216 }
0217
0218 return result;
0219 }
0220
0221 private:
0222 static inline bool vflip_if_south(CT const& lat1, CT const& azi12, CT & lat1_alt, CT & azi12_alt)
0223 {
0224 CT const c2 = 2;
0225 CT const pi = math::pi<CT>();
0226 CT const pi_half = pi / c2;
0227
0228 if (azi12 > pi_half)
0229 {
0230 azi12_alt = pi - azi12;
0231 lat1_alt = -lat1;
0232 return true;
0233 }
0234 else if (azi12 < -pi_half)
0235 {
0236 azi12_alt = -pi - azi12;
0237 lat1_alt = -lat1;
0238 return true;
0239 }
0240
0241 return false;
0242 }
0243
0244 static inline void vflip_rev_azi(CT & rev_azi, CT const& azimuth12)
0245 {
0246 CT const c0 = 0;
0247 CT const pi = math::pi<CT>();
0248
0249 if (rev_azi == c0)
0250 {
0251 rev_azi = azimuth12 >= 0 ? pi : -pi;
0252 }
0253 else if (rev_azi > c0)
0254 {
0255 rev_azi = pi - rev_azi;
0256 }
0257 else
0258 {
0259 rev_azi = -pi - rev_azi;
0260 }
0261 }
0262
0263 static inline CT normalized1_1(CT const& value)
0264 {
0265 CT const c1 = 1;
0266 return value > c1 ? c1 :
0267 value < -c1 ? -c1 :
0268 value;
0269 }
0270 };
0271
0272 }}}
0273
0274
0275 #endif