karney_direct.hpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Boost.Geometry
  2. // Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.
  3. // Contributed and/or modified by Adeel Ahmad,
  4. // as part of Google Summer of Code 2018 program.
  5. // This file was modified by Oracle on 2018-2020.
  6. // Modifications copyright (c) 2018-2020 Oracle and/or its affiliates.
  7. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  8. // Use, modification and distribution is subject to the Boost Software License,
  9. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. // This file is converted from GeographicLib, https://geographiclib.sourceforge.io
  12. // GeographicLib is originally written by Charles Karney.
  13. // Author: Charles Karney (2008-2017)
  14. // Last updated version of GeographicLib: 1.49
  15. // Original copyright notice:
  16. // Copyright (c) Charles Karney (2008-2017) <charles@karney.com> and licensed
  17. // under the MIT/X11 License. For more information, see
  18. // https://geographiclib.sourceforge.io
  19. #ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
  20. #define BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
  21. #include <boost/array.hpp>
  22. #include <boost/math/constants/constants.hpp>
  23. #include <boost/math/special_functions/hypot.hpp>
  24. #include <boost/geometry/formulas/flattening.hpp>
  25. #include <boost/geometry/formulas/result_direct.hpp>
  26. #include <boost/geometry/util/condition.hpp>
  27. #include <boost/geometry/util/math.hpp>
  28. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  29. #include <boost/geometry/util/series_expansion.hpp>
  30. namespace boost { namespace geometry { namespace formula
  31. {
  32. namespace se = series_expansion;
  33. /*!
  34. \brief The solution of the direct problem of geodesics on latlong coordinates,
  35. after Karney (2011).
  36. \author See
  37. - Charles F.F Karney, Algorithms for geodesics, 2011
  38. https://arxiv.org/pdf/1109.4448.pdf
  39. */
  40. template <
  41. typename CT,
  42. bool EnableCoordinates = true,
  43. bool EnableReverseAzimuth = false,
  44. bool EnableReducedLength = false,
  45. bool EnableGeodesicScale = false,
  46. size_t SeriesOrder = 8
  47. >
  48. class karney_direct
  49. {
  50. static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
  51. static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;
  52. static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcCoordinates || CalcQuantities;
  53. public:
  54. typedef result_direct<CT> result_type;
  55. template <typename T, typename Dist, typename Azi, typename Spheroid>
  56. static inline result_type apply(T const& lo1,
  57. T const& la1,
  58. Dist const& distance,
  59. Azi const& azimuth12,
  60. Spheroid const& spheroid)
  61. {
  62. result_type result;
  63. CT lon1 = lo1;
  64. CT const lat1 = la1;
  65. Azi azi12 = azimuth12;
  66. math::normalize_azimuth<degree, Azi>(azi12);
  67. CT const c0 = 0;
  68. CT const c1 = 1;
  69. CT const c2 = 2;
  70. CT const b = CT(get_radius<2>(spheroid));
  71. CT const f = formula::flattening<CT>(spheroid);
  72. CT const one_minus_f = c1 - f;
  73. CT const two_minus_f = c2 - f;
  74. CT const n = f / two_minus_f;
  75. CT const e2 = f * two_minus_f;
  76. CT const ep2 = e2 / math::sqr(one_minus_f);
  77. CT sin_alpha1, cos_alpha1;
  78. math::sin_cos_degrees<CT>(azi12, sin_alpha1, cos_alpha1);
  79. // Find the reduced latitude.
  80. CT sin_beta1, cos_beta1;
  81. math::sin_cos_degrees<CT>(lat1, sin_beta1, cos_beta1);
  82. sin_beta1 *= one_minus_f;
  83. math::normalize_unit_vector<CT>(sin_beta1, cos_beta1);
  84. cos_beta1 = (std::max)(c0, cos_beta1);
  85. // Obtain alpha 0 by solving the spherical triangle.
  86. CT const sin_alpha0 = sin_alpha1 * cos_beta1;
  87. CT const cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);
  88. CT const k2 = math::sqr(cos_alpha0) * ep2;
  89. CT const epsilon = k2 / (c2 * (c1 + math::sqrt(c1 + k2)) + k2);
  90. // Find the coefficients for A1 by computing the
  91. // series expansion using Horner scehme.
  92. CT const expansion_A1 = se::evaluate_A1<SeriesOrder>(epsilon);
  93. // Index zero element of coeffs_C1 is unused.
  94. se::coeffs_C1<SeriesOrder, CT> const coeffs_C1(epsilon);
  95. // Tau is an integration variable.
  96. CT const tau12 = distance / (b * (c1 + expansion_A1));
  97. CT const sin_tau12 = sin(tau12);
  98. CT const cos_tau12 = cos(tau12);
  99. CT sin_sigma1 = sin_beta1;
  100. CT sin_omega1 = sin_alpha0 * sin_beta1;
  101. CT cos_sigma1, cos_omega1;
  102. cos_sigma1 = cos_omega1 = sin_beta1 != c0 || cos_alpha1 != c0 ? cos_beta1 * cos_alpha1 : c1;
  103. math::normalize_unit_vector<CT>(sin_sigma1, cos_sigma1);
  104. CT const B11 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
  105. CT const sin_B11 = sin(B11);
  106. CT const cos_B11 = cos(B11);
  107. CT const sin_tau1 = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;
  108. CT const cos_tau1 = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;
  109. // Index zero element of coeffs_C1p is unused.
  110. se::coeffs_C1p<SeriesOrder, CT> const coeffs_C1p(epsilon);
  111. CT const B12 = - se::sin_cos_series
  112. (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
  113. cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
  114. coeffs_C1p);
  115. CT const sigma12 = tau12 - (B12 - B11);
  116. CT const sin_sigma12 = sin(sigma12);
  117. CT const cos_sigma12 = cos(sigma12);
  118. CT const sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;
  119. CT const cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;
  120. if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
  121. {
  122. CT const sin_alpha2 = sin_alpha0;
  123. CT const cos_alpha2 = cos_alpha0 * cos_sigma2;
  124. result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);
  125. // Convert the angle to radians.
  126. result.reverse_azimuth /= math::d2r<CT>();
  127. }
  128. if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
  129. {
  130. // Find the latitude at the second point.
  131. CT const sin_beta2 = cos_alpha0 * sin_sigma2;
  132. CT const cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);
  133. result.lat2 = atan2(sin_beta2, one_minus_f * cos_beta2);
  134. // Convert the coordinate to radians.
  135. result.lat2 /= math::d2r<CT>();
  136. // Find the longitude at the second point.
  137. CT const sin_omega2 = sin_alpha0 * sin_sigma2;
  138. CT const cos_omega2 = cos_sigma2;
  139. CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,
  140. cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);
  141. se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
  142. CT const A3 = math::horner_evaluate(epsilon, coeffs_A3.begin(), coeffs_A3.end());
  143. CT const A3c = -f * sin_alpha0 * A3;
  144. se::coeffs_C3<SeriesOrder, CT> const coeffs_C3(n, epsilon);
  145. CT const B31 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);
  146. CT const lam12 = omega12 + A3c *
  147. (sigma12 + (se::sin_cos_series
  148. (sin_sigma2,
  149. cos_sigma2,
  150. coeffs_C3) - B31));
  151. // Convert to radians to get the
  152. // longitudinal difference.
  153. CT lon12 = lam12 / math::d2r<CT>();
  154. // Add the longitude at first point to the longitudinal
  155. // difference and normalize the result.
  156. math::normalize_longitude<degree, CT>(lon1);
  157. math::normalize_longitude<degree, CT>(lon12);
  158. result.lon2 = lon1 + lon12;
  159. // For longitudes close to the antimeridian the result can be out
  160. // of range. Therefore normalize.
  161. // In other formulas this has to be done at the end because
  162. // otherwise differential quantities are calculated incorrectly.
  163. // But here it's ok since result.lon2 is not used after this point.
  164. math::normalize_longitude<degree, CT>(result.lon2);
  165. }
  166. if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
  167. {
  168. // Evaluate the coefficients for C2.
  169. // Index zero element of coeffs_C2 is unused.
  170. se::coeffs_C2<SeriesOrder, CT> const coeffs_C2(epsilon);
  171. CT const B21 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);
  172. CT const B22 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);
  173. // Find the coefficients for A2 by computing the
  174. // series expansion using Horner scehme.
  175. CT const expansion_A2 = se::evaluate_A2<SeriesOrder>(epsilon);
  176. CT const AB1 = (c1 + expansion_A1) * (B12 - B11);
  177. CT const AB2 = (c1 + expansion_A2) * (B22 - B21);
  178. CT const J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);
  179. CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_beta1));
  180. CT const dn2 = math::sqrt(c1 + k2 * math::sqr(sin_sigma2));
  181. // Find the reduced length.
  182. result.reduced_length = b * ((dn2 * (cos_sigma1 * sin_sigma2) -
  183. dn1 * (sin_sigma1 * cos_sigma2)) -
  184. cos_sigma1 * cos_sigma2 * J12);
  185. // Find the geodesic scale.
  186. CT const t = k2 * (sin_sigma2 - sin_sigma1) *
  187. (sin_sigma2 + sin_sigma1) / (dn1 + dn2);
  188. result.geodesic_scale = cos_sigma12 +
  189. (t * sin_sigma2 - cos_sigma2 * J12) *
  190. sin_sigma1 / dn1;
  191. }
  192. return result;
  193. }
  194. };
  195. }}} // namespace boost::geometry::formula
  196. #endif // BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP