File indexing completed on 2025-01-18 09:35:24
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef BOOST_GEOMETRY_FORMULAS_ANDOYER_INVERSE_HPP
0014 #define BOOST_GEOMETRY_FORMULAS_ANDOYER_INVERSE_HPP
0015
0016
0017 #include <boost/math/constants/constants.hpp>
0018
0019 #include <boost/geometry/core/radius.hpp>
0020
0021 #include <boost/geometry/util/condition.hpp>
0022 #include <boost/geometry/util/math.hpp>
0023
0024 #include <boost/geometry/formulas/differential_quantities.hpp>
0025 #include <boost/geometry/formulas/flattening.hpp>
0026 #include <boost/geometry/formulas/result_inverse.hpp>
0027
0028
0029 namespace boost { namespace geometry { namespace formula
0030 {
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 template <
0042 typename CT,
0043 bool EnableDistance,
0044 bool EnableAzimuth,
0045 bool EnableReverseAzimuth = false,
0046 bool EnableReducedLength = false,
0047 bool EnableGeodesicScale = false
0048 >
0049 class andoyer_inverse
0050 {
0051 static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
0052 static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
0053 static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
0054 static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
0055
0056 public:
0057 typedef result_inverse<CT> result_type;
0058
0059 template <typename T1, typename T2, typename Spheroid>
0060 static inline result_type apply(T1 const& lon1,
0061 T1 const& lat1,
0062 T2 const& lon2,
0063 T2 const& lat2,
0064 Spheroid const& spheroid)
0065 {
0066 result_type result;
0067
0068
0069
0070 if ( math::equals(lon1, lon2) && math::equals(lat1, lat2) )
0071 {
0072 return result;
0073 }
0074
0075 CT const c0 = CT(0);
0076 CT const c1 = CT(1);
0077 CT const pi = math::pi<CT>();
0078 CT const f = formula::flattening<CT>(spheroid);
0079
0080 CT const dlon = lon2 - lon1;
0081 CT const sin_dlon = sin(dlon);
0082 CT const cos_dlon = cos(dlon);
0083 CT const sin_lat1 = sin(lat1);
0084 CT const cos_lat1 = cos(lat1);
0085 CT const sin_lat2 = sin(lat2);
0086 CT const cos_lat2 = cos(lat2);
0087
0088
0089
0090
0091 CT cos_d = sin_lat1*sin_lat2 + cos_lat1*cos_lat2*cos_dlon;
0092
0093 if (cos_d < -c1)
0094 cos_d = -c1;
0095 else if (cos_d > c1)
0096 cos_d = c1;
0097
0098 CT const d = acos(cos_d);
0099 CT const sin_d = sin(d);
0100
0101 if ( BOOST_GEOMETRY_CONDITION(EnableDistance) )
0102 {
0103 CT const K = math::sqr(sin_lat1-sin_lat2);
0104 CT const L = math::sqr(sin_lat1+sin_lat2);
0105 CT const three_sin_d = CT(3) * sin_d;
0106
0107 CT const one_minus_cos_d = c1 - cos_d;
0108 CT const one_plus_cos_d = c1 + cos_d;
0109
0110
0111
0112 CT const H = math::equals(one_minus_cos_d, c0) ?
0113 c0 :
0114 (d + three_sin_d) / one_minus_cos_d;
0115 CT const G = math::equals(one_plus_cos_d, c0) ?
0116 c0 :
0117 (d - three_sin_d) / one_plus_cos_d;
0118
0119 CT const dd = -(f/CT(4))*(H*K+G*L);
0120
0121 CT const a = CT(get_radius<0>(spheroid));
0122
0123 result.distance = a * (d + dd);
0124 }
0125
0126 if ( BOOST_GEOMETRY_CONDITION(CalcAzimuths) )
0127 {
0128
0129 if (math::equals(sin_d, c0))
0130 {
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145 if (cos_d >= c0)
0146 {
0147 result.azimuth = c0;
0148 result.reverse_azimuth = c0;
0149 }
0150
0151 else
0152 {
0153
0154 if (! math::equals(sin_lat1, c1))
0155 {
0156 result.azimuth = c0;
0157 result.reverse_azimuth = pi;
0158 }
0159 else
0160 {
0161 result.azimuth = pi;
0162 result.reverse_azimuth = c0;
0163 }
0164 }
0165 }
0166 else
0167 {
0168 CT const c2 = CT(2);
0169
0170 CT A = c0;
0171 CT U = c0;
0172 if (math::equals(cos_lat2, c0))
0173 {
0174 if (sin_lat2 < c0)
0175 {
0176 A = pi;
0177 }
0178 }
0179 else
0180 {
0181 CT const tan_lat2 = sin_lat2/cos_lat2;
0182 CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon;
0183 A = atan2(sin_dlon, M);
0184 CT const sin_2A = sin(c2*A);
0185 U = (f/ c2)*math::sqr(cos_lat1)*sin_2A;
0186 }
0187
0188 CT B = c0;
0189 CT V = c0;
0190 if (math::equals(cos_lat1, c0))
0191 {
0192 if (sin_lat1 < c0)
0193 {
0194 B = pi;
0195 }
0196 }
0197 else
0198 {
0199 CT const tan_lat1 = sin_lat1/cos_lat1;
0200 CT const N = cos_lat2*tan_lat1-sin_lat2*cos_dlon;
0201 B = atan2(sin_dlon, N);
0202 CT const sin_2B = sin(c2*B);
0203 V = (f/ c2)*math::sqr(cos_lat2)*sin_2B;
0204 }
0205
0206 CT const T = d / sin_d;
0207
0208
0209
0210
0211
0212
0213 if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))
0214 {
0215 CT const dA = V*T - U;
0216 result.azimuth = A - dA;
0217 normalize_azimuth(result.azimuth, A, dA);
0218 }
0219
0220 if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
0221 {
0222 CT const dB = -U*T + V;
0223 if (B >= 0)
0224 result.reverse_azimuth = pi - B - dB;
0225 else
0226 result.reverse_azimuth = -pi - B - dB;
0227 normalize_azimuth(result.reverse_azimuth, B, dB);
0228 }
0229 }
0230 }
0231
0232 if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
0233 {
0234 CT const b = CT(get_radius<2>(spheroid));
0235
0236 typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 1> quantities;
0237 quantities::apply(dlon, sin_lat1, cos_lat1, sin_lat2, cos_lat2,
0238 result.azimuth, result.reverse_azimuth,
0239 b, f,
0240 result.reduced_length, result.geodesic_scale);
0241 }
0242
0243 return result;
0244 }
0245
0246 private:
0247 static inline void normalize_azimuth(CT & azimuth, CT const& A, CT const& dA)
0248 {
0249 CT const c0 = 0;
0250
0251 if (A >= c0)
0252 {
0253 if (dA >= c0)
0254 {
0255 if (azimuth < c0)
0256 {
0257 azimuth = c0;
0258 }
0259 }
0260 else
0261 {
0262 CT const pi = math::pi<CT>();
0263 if (azimuth > pi)
0264 {
0265 azimuth = pi;
0266 }
0267 }
0268 }
0269 else
0270 {
0271 if (dA <= c0)
0272 {
0273 if (azimuth > c0)
0274 {
0275 azimuth = c0;
0276 }
0277 }
0278 else
0279 {
0280 CT const minus_pi = -math::pi<CT>();
0281 if (azimuth < minus_pi)
0282 {
0283 azimuth = minus_pi;
0284 }
0285 }
0286 }
0287 }
0288 };
0289
0290 }}}
0291
0292
0293 #endif