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