thomas_inverse.hpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Boost.Geometry
  2. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2015-2018 Oracle and/or its affiliates.
  4. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_FORMULAS_THOMAS_INVERSE_HPP
  10. #define BOOST_GEOMETRY_FORMULAS_THOMAS_INVERSE_HPP
  11. #include <boost/math/constants/constants.hpp>
  12. #include <boost/geometry/core/radius.hpp>
  13. #include <boost/geometry/util/constexpr.hpp>
  14. #include <boost/geometry/util/math.hpp>
  15. #include <boost/geometry/formulas/differential_quantities.hpp>
  16. #include <boost/geometry/formulas/flattening.hpp>
  17. #include <boost/geometry/formulas/result_inverse.hpp>
  18. namespace boost { namespace geometry { namespace formula
  19. {
  20. /*!
  21. \brief The solution of the inverse problem of geodesics on latlong coordinates,
  22. Forsyth-Andoyer-Lambert type approximation with second order terms.
  23. \author See
  24. - Technical Report: PAUL D. THOMAS, MATHEMATICAL MODELS FOR NAVIGATION SYSTEMS, 1965
  25. http://www.dtic.mil/docs/citations/AD0627893
  26. - Technical Report: PAUL D. THOMAS, SPHEROIDAL GEODESICS, REFERENCE SYSTEMS, AND LOCAL GEOMETRY, 1970
  27. http://www.dtic.mil/docs/citations/AD0703541
  28. */
  29. template <
  30. typename CT,
  31. bool EnableDistance,
  32. bool EnableAzimuth,
  33. bool EnableReverseAzimuth = false,
  34. bool EnableReducedLength = false,
  35. bool EnableGeodesicScale = false
  36. >
  37. class thomas_inverse
  38. {
  39. static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
  40. static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
  41. static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
  42. static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
  43. public:
  44. typedef result_inverse<CT> result_type;
  45. template <typename T1, typename T2, typename Spheroid>
  46. static inline result_type apply(T1 const& lon1,
  47. T1 const& lat1,
  48. T2 const& lon2,
  49. T2 const& lat2,
  50. Spheroid const& spheroid)
  51. {
  52. result_type result;
  53. // coordinates in radians
  54. if ( math::equals(lon1, lon2) && math::equals(lat1, lat2) )
  55. {
  56. return result;
  57. }
  58. CT const c0 = 0;
  59. CT const c1 = 1;
  60. CT const c2 = 2;
  61. CT const c4 = 4;
  62. CT const pi_half = math::pi<CT>() / c2;
  63. CT const f = formula::flattening<CT>(spheroid);
  64. CT const one_minus_f = c1 - f;
  65. // CT const tan_theta1 = one_minus_f * tan(lat1);
  66. // CT const tan_theta2 = one_minus_f * tan(lat2);
  67. // CT const theta1 = atan(tan_theta1);
  68. // CT const theta2 = atan(tan_theta2);
  69. CT const theta1 = math::equals(lat1, pi_half) ? lat1 :
  70. math::equals(lat1, -pi_half) ? lat1 :
  71. atan(one_minus_f * tan(lat1));
  72. CT const theta2 = math::equals(lat2, pi_half) ? lat2 :
  73. math::equals(lat2, -pi_half) ? lat2 :
  74. atan(one_minus_f * tan(lat2));
  75. CT const theta_m = (theta1 + theta2) / c2;
  76. CT const d_theta_m = (theta2 - theta1) / c2;
  77. CT const d_lambda = lon2 - lon1;
  78. CT const d_lambda_m = d_lambda / c2;
  79. CT const sin_theta_m = sin(theta_m);
  80. CT const cos_theta_m = cos(theta_m);
  81. CT const sin_d_theta_m = sin(d_theta_m);
  82. CT const cos_d_theta_m = cos(d_theta_m);
  83. CT const sin2_theta_m = math::sqr(sin_theta_m);
  84. CT const cos2_theta_m = math::sqr(cos_theta_m);
  85. CT const sin2_d_theta_m = math::sqr(sin_d_theta_m);
  86. CT const cos2_d_theta_m = math::sqr(cos_d_theta_m);
  87. CT const sin_d_lambda_m = sin(d_lambda_m);
  88. CT const sin2_d_lambda_m = math::sqr(sin_d_lambda_m);
  89. CT const H = cos2_theta_m - sin2_d_theta_m;
  90. CT const L = sin2_d_theta_m + H * sin2_d_lambda_m;
  91. CT const cos_d = c1 - c2 * L;
  92. CT const d = acos(cos_d);
  93. CT const sin_d = sin(d);
  94. CT const one_minus_L = c1 - L;
  95. if ( math::equals(sin_d, c0)
  96. || math::equals(L, c0)
  97. || math::equals(one_minus_L, c0) )
  98. {
  99. return result;
  100. }
  101. CT const U = c2 * sin2_theta_m * cos2_d_theta_m / one_minus_L;
  102. CT const V = c2 * sin2_d_theta_m * cos2_theta_m / L;
  103. CT const X = U + V;
  104. CT const Y = U - V;
  105. CT const T = d / sin_d;
  106. CT const D = c4 * math::sqr(T);
  107. CT const E = c2 * cos_d;
  108. CT const A = D * E;
  109. CT const B = c2 * D;
  110. CT const C = T - (A - E) / c2;
  111. CT const f_sqr = math::sqr(f);
  112. CT const f_sqr_per_64 = f_sqr / CT(64);
  113. if BOOST_GEOMETRY_CONSTEXPR (EnableDistance)
  114. {
  115. CT const n1 = X * (A + C*X);
  116. CT const n2 = Y * (B + E*Y);
  117. CT const n3 = D*X*Y;
  118. CT const delta1d = f * (T*X-Y) / c4;
  119. CT const delta2d = f_sqr_per_64 * (n1 - n2 + n3);
  120. CT const a = get_radius<0>(spheroid);
  121. //result.distance = a * sin_d * (T - delta1d);
  122. result.distance = a * sin_d * (T - delta1d + delta2d);
  123. }
  124. if BOOST_GEOMETRY_CONSTEXPR (CalcAzimuths)
  125. {
  126. // NOTE: if both cos_latX == 0 then below we'd have 0 * INF
  127. // it's a situation when the endpoints are on the poles +-90 deg
  128. // in this case the azimuth could either be 0 or +-pi
  129. // but above always 0 is returned
  130. CT const F = c2*Y-E*(c4-X);
  131. CT const M = CT(32)*T-(CT(20)*T-A)*X-(B+c4)*Y;
  132. CT const G = f*T/c2 + f_sqr_per_64 * M;
  133. // TODO:
  134. // If d_lambda is close to 90 or -90 deg then tan(d_lambda) is big
  135. // and F is small. The result is not accurate.
  136. // In the edge case the result may be 2 orders of magnitude less
  137. // accurate than Andoyer's.
  138. CT const tan_d_lambda = tan(d_lambda);
  139. CT const Q = -(F*G*tan_d_lambda) / c4;
  140. CT const d_lambda_m_p = (d_lambda + Q) / c2;
  141. CT const tan_d_lambda_m_p = tan(d_lambda_m_p);
  142. CT const v = atan2(cos_d_theta_m, sin_theta_m * tan_d_lambda_m_p);
  143. CT const u = atan2(-sin_d_theta_m, cos_theta_m * tan_d_lambda_m_p);
  144. CT const pi = math::pi<CT>();
  145. if BOOST_GEOMETRY_CONSTEXPR (CalcFwdAzimuth)
  146. {
  147. CT alpha1 = v + u;
  148. if (alpha1 > pi)
  149. {
  150. alpha1 -= c2 * pi;
  151. }
  152. result.azimuth = alpha1;
  153. }
  154. if BOOST_GEOMETRY_CONSTEXPR (CalcRevAzimuth)
  155. {
  156. CT alpha2 = pi - (v - u);
  157. if (alpha2 > pi)
  158. {
  159. alpha2 -= c2 * pi;
  160. }
  161. result.reverse_azimuth = alpha2;
  162. }
  163. }
  164. if BOOST_GEOMETRY_CONSTEXPR (CalcQuantities)
  165. {
  166. typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
  167. quantities::apply(lon1, lat1, lon2, lat2,
  168. result.azimuth, result.reverse_azimuth,
  169. get_radius<2>(spheroid), f,
  170. result.reduced_length, result.geodesic_scale);
  171. }
  172. return result;
  173. }
  174. };
  175. }}} // namespace boost::geometry::formula
  176. #endif // BOOST_GEOMETRY_FORMULAS_THOMAS_INVERSE_HPP