closest_points_pt_seg.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Boost.Geometry
  2. // Copyright (c) 2021, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Licensed under the Boost Software License version 1.0.
  5. // http://www.boost.org/users/license.html
  6. #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP
  7. #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP
  8. #include <algorithm>
  9. #include <type_traits>
  10. #include <boost/config.hpp>
  11. #include <boost/concept_check.hpp>
  12. #include <boost/geometry/core/cs.hpp>
  13. #include <boost/geometry/core/access.hpp>
  14. #include <boost/geometry/core/coordinate_promotion.hpp>
  15. #include <boost/geometry/core/radian_access.hpp>
  16. #include <boost/geometry/core/tags.hpp>
  17. #include <boost/geometry/formulas/spherical.hpp>
  18. #include <boost/geometry/strategies/distance.hpp>
  19. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  20. #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
  21. #include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
  22. #include <boost/geometry/strategies/spherical/point_in_point.hpp>
  23. #include <boost/geometry/strategies/spherical/intersection.hpp>
  24. #include <boost/geometry/util/math.hpp>
  25. #include <boost/geometry/util/select_calculation_type.hpp>
  26. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  27. # include <boost/geometry/io/dsv/write.hpp>
  28. #endif
  29. namespace boost { namespace geometry
  30. {
  31. namespace strategy { namespace closest_points
  32. {
  33. template
  34. <
  35. typename CalculationType = void,
  36. typename Strategy = distance::comparable::haversine<double, CalculationType>
  37. >
  38. class cross_track
  39. {
  40. public:
  41. template <typename Point, typename PointOfSegment>
  42. struct calculation_type
  43. : promote_floating_point
  44. <
  45. typename select_calculation_type
  46. <
  47. Point,
  48. PointOfSegment,
  49. CalculationType
  50. >::type
  51. >
  52. {};
  53. using radius_type = typename Strategy::radius_type;
  54. cross_track() = default;
  55. explicit inline cross_track(typename Strategy::radius_type const& r)
  56. : m_strategy(r)
  57. {}
  58. inline cross_track(Strategy const& s)
  59. : m_strategy(s)
  60. {}
  61. template <typename Point, typename PointOfSegment>
  62. inline auto apply(Point const& p,
  63. PointOfSegment const& sp1,
  64. PointOfSegment const& sp2) const
  65. {
  66. using CT = typename calculation_type<Point, PointOfSegment>::type;
  67. // http://williams.best.vwh.net/avform.htm#XTE
  68. CT d3 = m_strategy.apply(sp1, sp2);
  69. if (geometry::math::equals(d3, 0.0))
  70. {
  71. // "Degenerate" segment, return either d1 or d2
  72. return sp1;
  73. }
  74. CT d1 = m_strategy.apply(sp1, p);
  75. CT d2 = m_strategy.apply(sp2, p);
  76. auto d_crs_pair = distance::detail::compute_cross_track_pair<CT>::apply(
  77. p, sp1, sp2);
  78. // d1, d2, d3 are in principle not needed, only the sign matters
  79. CT projection1 = cos(d_crs_pair.first) * d1 / d3;
  80. CT projection2 = cos(d_crs_pair.second) * d2 / d3;
  81. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  82. std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " "
  83. << crs_AD * geometry::math::r2d<CT>() << std::endl;
  84. std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " "
  85. << crs_AB * geometry::math::r2d<CT>() << std::endl;
  86. std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " "
  87. << crs_BA * geometry::math::r2d<CT>() << std::endl;
  88. std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " "
  89. << crs_BD * geometry::math::r2d<CT>() << std::endl;
  90. std::cout << "Projection AD-AB " << projection1 << " : "
  91. << d_crs1 * geometry::math::r2d<CT>() << std::endl;
  92. std::cout << "Projection BD-BA " << projection2 << " : "
  93. << d_crs2 * geometry::math::r2d<CT>() << std::endl;
  94. std::cout << " d1: " << (d1 )
  95. << " d2: " << (d2 )
  96. << std::endl;
  97. #endif
  98. if (projection1 > 0.0 && projection2 > 0.0)
  99. {
  100. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  101. CT XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) ));
  102. std::cout << "Projection ON the segment" << std::endl;
  103. std::cout << "XTD: " << XTD
  104. << " d1: " << (d1 * radius())
  105. << " d2: " << (d2 * radius())
  106. << std::endl;
  107. #endif
  108. auto distance = distance::detail::compute_cross_track_distance::apply(
  109. d_crs_pair.first, d1);
  110. CT lon1 = geometry::get_as_radian<0>(sp1);
  111. CT lat1 = geometry::get_as_radian<1>(sp1);
  112. CT lon2 = geometry::get_as_radian<0>(sp2);
  113. CT lat2 = geometry::get_as_radian<1>(sp2);
  114. CT dist = CT(2) * asin(math::sqrt(distance)) * m_strategy.radius();
  115. CT dist_d1 = CT(2) * asin(math::sqrt(d1)) * m_strategy.radius();
  116. // Note: this is similar to spherical computation in geographic
  117. // point_segment_distance formula
  118. CT earth_radius = m_strategy.radius();
  119. CT cos_frac = cos(dist_d1 / earth_radius) / cos(dist / earth_radius);
  120. CT s14_sph = cos_frac >= 1
  121. ? CT(0) : cos_frac <= -1 ? math::pi<CT>() * earth_radius
  122. : acos(cos_frac) * earth_radius;
  123. CT a12 = geometry::formula::spherical_azimuth<>(lon1, lat1, lon2, lat2);
  124. auto res_direct = geometry::formula::spherical_direct
  125. <
  126. true,
  127. false
  128. >(lon1, lat1, s14_sph, a12, srs::sphere<CT>(earth_radius));
  129. model::point
  130. <
  131. CT,
  132. dimension<PointOfSegment>::value,
  133. typename coordinate_system<PointOfSegment>::type
  134. > cp;
  135. geometry::set_from_radian<0>(cp, res_direct.lon2);
  136. geometry::set_from_radian<1>(cp, res_direct.lat2);
  137. return cp;
  138. }
  139. else
  140. {
  141. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
  142. std::cout << "Projection OUTSIDE the segment" << std::endl;
  143. #endif
  144. return d1 < d2 ? sp1 : sp2;
  145. }
  146. }
  147. template <typename T1, typename T2>
  148. inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
  149. {
  150. return m_strategy.radius() * (lat1 - lat2);
  151. }
  152. inline typename Strategy::radius_type radius() const
  153. { return m_strategy.radius(); }
  154. private :
  155. Strategy m_strategy;
  156. };
  157. }} // namespace strategy::closest_points
  158. }} // namespace boost::geometry
  159. #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP