thomas_inverse.hpp 7.6 KB

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