meridian_direct.hpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Boost.Geometry
  2. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 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_MERIDIAN_DIRECT_HPP
  10. #define BOOST_GEOMETRY_FORMULAS_MERIDIAN_DIRECT_HPP
  11. #include <boost/math/constants/constants.hpp>
  12. #include <boost/geometry/core/radius.hpp>
  13. #include <boost/geometry/formulas/differential_quantities.hpp>
  14. #include <boost/geometry/formulas/flattening.hpp>
  15. #include <boost/geometry/formulas/meridian_inverse.hpp>
  16. #include <boost/geometry/formulas/quarter_meridian.hpp>
  17. #include <boost/geometry/formulas/result_direct.hpp>
  18. #include <boost/geometry/util/constexpr.hpp>
  19. #include <boost/geometry/util/math.hpp>
  20. namespace boost { namespace geometry { namespace formula
  21. {
  22. /*!
  23. \brief Compute the direct geodesic problem on a meridian
  24. */
  25. template <
  26. typename CT,
  27. bool EnableCoordinates = true,
  28. bool EnableReverseAzimuth = false,
  29. bool EnableReducedLength = false,
  30. bool EnableGeodesicScale = false,
  31. unsigned int Order = 4
  32. >
  33. class meridian_direct
  34. {
  35. static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
  36. static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
  37. static const bool CalcCoordinates = EnableCoordinates || CalcRevAzimuth;
  38. public:
  39. typedef result_direct<CT> result_type;
  40. template <typename T, typename Dist, typename Spheroid>
  41. static inline result_type apply(T const& lo1,
  42. T const& la1,
  43. Dist const& distance,
  44. bool north,
  45. Spheroid const& spheroid)
  46. {
  47. result_type result;
  48. CT const half_pi = math::half_pi<CT>();
  49. CT const pi = math::pi<CT>();
  50. CT const one_and_a_half_pi = pi + half_pi;
  51. CT const c0 = 0;
  52. CT azimuth = north ? c0 : pi;
  53. if BOOST_GEOMETRY_CONSTEXPR (CalcCoordinates)
  54. {
  55. CT s0 = meridian_inverse<CT, Order>::apply(la1, spheroid);
  56. int signed_distance = north ? distance : -distance;
  57. result.lon2 = lo1;
  58. result.lat2 = apply(s0 + signed_distance, spheroid);
  59. }
  60. if BOOST_GEOMETRY_CONSTEXPR (CalcRevAzimuth)
  61. {
  62. result.reverse_azimuth = azimuth;
  63. if (result.lat2 > half_pi &&
  64. result.lat2 < one_and_a_half_pi)
  65. {
  66. result.reverse_azimuth = pi;
  67. }
  68. else if (result.lat2 > -one_and_a_half_pi &&
  69. result.lat2 < -half_pi)
  70. {
  71. result.reverse_azimuth = c0;
  72. }
  73. }
  74. if BOOST_GEOMETRY_CONSTEXPR (CalcQuantities)
  75. {
  76. CT const b = CT(get_radius<2>(spheroid));
  77. CT const f = formula::flattening<CT>(spheroid);
  78. boost::geometry::math::normalize_spheroidal_coordinates
  79. <
  80. boost::geometry::radian,
  81. double
  82. >(result.lon2, result.lat2);
  83. typedef differential_quantities
  84. <
  85. CT,
  86. EnableReducedLength,
  87. EnableGeodesicScale,
  88. Order
  89. > quantities;
  90. quantities::apply(lo1, la1, result.lon2, result.lat2,
  91. azimuth, result.reverse_azimuth,
  92. b, f,
  93. result.reduced_length, result.geodesic_scale);
  94. }
  95. return result;
  96. }
  97. // https://en.wikipedia.org/wiki/Meridian_arc#The_inverse_meridian_problem_for_the_ellipsoid
  98. // latitudes are assumed to be in radians and in [-pi/2,pi/2]
  99. template <typename T, typename Spheroid>
  100. static CT apply(T m, Spheroid const& spheroid)
  101. {
  102. CT const f = formula::flattening<CT>(spheroid);
  103. CT n = f / (CT(2) - f);
  104. CT mp = formula::quarter_meridian<CT>(spheroid);
  105. CT mu = geometry::math::pi<CT>()/CT(2) * m / mp;
  106. if (BOOST_GEOMETRY_CONDITION(Order == 0))
  107. {
  108. return mu;
  109. }
  110. CT H2 = 1.5 * n;
  111. if (BOOST_GEOMETRY_CONDITION(Order == 1))
  112. {
  113. return mu + H2 * sin(2*mu);
  114. }
  115. CT n2 = n * n;
  116. CT H4 = 1.3125 * n2;
  117. if (BOOST_GEOMETRY_CONDITION(Order == 2))
  118. {
  119. return mu + H2 * sin(2*mu) + H4 * sin(4*mu);
  120. }
  121. CT n3 = n2 * n;
  122. H2 -= 0.84375 * n3;
  123. CT H6 = 1.572916667 * n3;
  124. if (BOOST_GEOMETRY_CONDITION(Order == 3))
  125. {
  126. return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu);
  127. }
  128. CT n4 = n2 * n2;
  129. H4 -= 1.71875 * n4;
  130. CT H8 = 2.142578125 * n4;
  131. // Order 4 or higher
  132. return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu) + H8 * sin(8*mu);
  133. }
  134. };
  135. }}} // namespace boost::geometry::formula
  136. #endif // BOOST_GEOMETRY_FORMULAS_MERIDIAN_DIRECT_HPP