meridian_direct.hpp 4.9 KB

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