meridian_direct.hpp 5.0 KB

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