direction_code.hpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2015, 2017, 2019.
  4. // Modifications copyright (c) 2015-2019 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP
  12. #include <boost/geometry/core/access.hpp>
  13. #include <boost/geometry/arithmetic/infinite_line_functions.hpp>
  14. #include <boost/geometry/algorithms/detail/make/make.hpp>
  15. #include <boost/geometry/util/math.hpp>
  16. #include <boost/geometry/util/select_coordinate_type.hpp>
  17. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  18. #include <boost/mpl/assert.hpp>
  19. namespace boost { namespace geometry
  20. {
  21. #ifndef DOXYGEN_NO_DETAIL
  22. namespace detail
  23. {
  24. template <typename CSTag>
  25. struct direction_code_impl
  26. {
  27. BOOST_MPL_ASSERT_MSG((false), NOT_IMPLEMENTED_FOR_THIS_CS, (CSTag));
  28. };
  29. template <>
  30. struct direction_code_impl<cartesian_tag>
  31. {
  32. template <typename Point1, typename Point2>
  33. static inline int apply(Point1 const& segment_a, Point1 const& segment_b,
  34. Point2 const& point)
  35. {
  36. typedef typename geometry::select_coordinate_type
  37. <
  38. Point1, Point2
  39. >::type calc_t;
  40. typedef model::infinite_line<calc_t> line_type;
  41. // point b is often equal to the specified point, check that first
  42. line_type const q = detail::make::make_infinite_line<calc_t>(segment_b, point);
  43. if (arithmetic::is_degenerate(q))
  44. {
  45. return 0;
  46. }
  47. line_type const p = detail::make::make_infinite_line<calc_t>(segment_a, segment_b);
  48. if (arithmetic::is_degenerate(p))
  49. {
  50. return 0;
  51. }
  52. // p extends a-b if direction is similar
  53. return arithmetic::similar_direction(p, q) ? 1 : -1;
  54. }
  55. };
  56. template <>
  57. struct direction_code_impl<spherical_equatorial_tag>
  58. {
  59. template <typename Point1, typename Point2>
  60. static inline int apply(Point1 const& segment_a, Point1 const& segment_b,
  61. Point2 const& p)
  62. {
  63. typedef typename coordinate_type<Point1>::type coord1_t;
  64. typedef typename coordinate_type<Point2>::type coord2_t;
  65. typedef typename cs_angular_units<Point1>::type units_t;
  66. typedef typename cs_angular_units<Point2>::type units2_t;
  67. BOOST_MPL_ASSERT_MSG((boost::is_same<units_t, units2_t>::value),
  68. NOT_IMPLEMENTED_FOR_DIFFERENT_UNITS,
  69. (units_t, units2_t));
  70. typedef typename geometry::select_coordinate_type <Point1, Point2>::type calc_t;
  71. typedef math::detail::constants_on_spheroid<coord1_t, units_t> constants1;
  72. typedef math::detail::constants_on_spheroid<coord2_t, units_t> constants2;
  73. static coord1_t const pi_half1 = constants1::max_latitude();
  74. static coord2_t const pi_half2 = constants2::max_latitude();
  75. static calc_t const c0 = 0;
  76. coord1_t const a0 = geometry::get<0>(segment_a);
  77. coord1_t const a1 = geometry::get<1>(segment_a);
  78. coord1_t const b0 = geometry::get<0>(segment_b);
  79. coord1_t const b1 = geometry::get<1>(segment_b);
  80. coord2_t const p0 = geometry::get<0>(p);
  81. coord2_t const p1 = geometry::get<1>(p);
  82. if ( (math::equals(b0, a0) && math::equals(b1, a1))
  83. || (math::equals(b0, p0) && math::equals(b1, p1)) )
  84. {
  85. return 0;
  86. }
  87. bool const is_a_pole = math::equals(pi_half1, math::abs(a1));
  88. bool const is_b_pole = math::equals(pi_half1, math::abs(b1));
  89. bool const is_p_pole = math::equals(pi_half2, math::abs(p1));
  90. if ( is_b_pole && ((is_a_pole && math::sign(b1) == math::sign(a1))
  91. || (is_p_pole && math::sign(b1) == math::sign(p1))) )
  92. {
  93. return 0;
  94. }
  95. // NOTE: as opposed to the implementation for cartesian CS
  96. // here point b is the origin
  97. calc_t const dlon1 = math::longitude_distance_signed<units_t, calc_t>(b0, a0);
  98. calc_t const dlon2 = math::longitude_distance_signed<units_t, calc_t>(b0, p0);
  99. bool is_antilon1 = false, is_antilon2 = false;
  100. calc_t const dlat1 = latitude_distance_signed<units_t, calc_t>(b1, a1, dlon1, is_antilon1);
  101. calc_t const dlat2 = latitude_distance_signed<units_t, calc_t>(b1, p1, dlon2, is_antilon2);
  102. calc_t mx = is_a_pole || is_b_pole || is_p_pole ?
  103. c0 :
  104. (std::min)(is_antilon1 ? c0 : math::abs(dlon1),
  105. is_antilon2 ? c0 : math::abs(dlon2));
  106. calc_t my = (std::min)(math::abs(dlat1),
  107. math::abs(dlat2));
  108. int s1 = 0, s2 = 0;
  109. if (mx >= my)
  110. {
  111. s1 = dlon1 > 0 ? 1 : -1;
  112. s2 = dlon2 > 0 ? 1 : -1;
  113. }
  114. else
  115. {
  116. s1 = dlat1 > 0 ? 1 : -1;
  117. s2 = dlat2 > 0 ? 1 : -1;
  118. }
  119. return s1 == s2 ? -1 : 1;
  120. }
  121. template <typename Units, typename T>
  122. static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, bool & is_antilon)
  123. {
  124. typedef math::detail::constants_on_spheroid<T, Units> constants;
  125. static T const pi = constants::half_period();
  126. static T const c0 = 0;
  127. T res = lat2 - lat1;
  128. is_antilon = math::equals(math::abs(lon_ds), pi);
  129. if (is_antilon)
  130. {
  131. res = lat2 + lat1;
  132. if (res >= c0)
  133. res = pi - res;
  134. else
  135. res = -pi - res;
  136. }
  137. return res;
  138. }
  139. };
  140. template <>
  141. struct direction_code_impl<spherical_polar_tag>
  142. {
  143. template <typename Point1, typename Point2>
  144. static inline int apply(Point1 segment_a, Point1 segment_b,
  145. Point2 p)
  146. {
  147. typedef math::detail::constants_on_spheroid
  148. <
  149. typename coordinate_type<Point1>::type,
  150. typename cs_angular_units<Point1>::type
  151. > constants1;
  152. typedef math::detail::constants_on_spheroid
  153. <
  154. typename coordinate_type<Point2>::type,
  155. typename cs_angular_units<Point2>::type
  156. > constants2;
  157. geometry::set<1>(segment_a,
  158. constants1::max_latitude() - geometry::get<1>(segment_a));
  159. geometry::set<1>(segment_b,
  160. constants1::max_latitude() - geometry::get<1>(segment_b));
  161. geometry::set<1>(p,
  162. constants2::max_latitude() - geometry::get<1>(p));
  163. return direction_code_impl
  164. <
  165. spherical_equatorial_tag
  166. >::apply(segment_a, segment_b, p);
  167. }
  168. };
  169. // if spherical_tag is passed then pick cs_tag based on Point1 type
  170. // with spherical_equatorial_tag as the default
  171. template <>
  172. struct direction_code_impl<spherical_tag>
  173. {
  174. template <typename Point1, typename Point2>
  175. static inline int apply(Point1 segment_a, Point1 segment_b,
  176. Point2 p)
  177. {
  178. return direction_code_impl
  179. <
  180. typename boost::mpl::if_c
  181. <
  182. boost::is_same
  183. <
  184. typename geometry::cs_tag<Point1>::type,
  185. spherical_polar_tag
  186. >::value,
  187. spherical_polar_tag,
  188. spherical_equatorial_tag
  189. >::type
  190. >::apply(segment_a, segment_b, p);
  191. }
  192. };
  193. template <>
  194. struct direction_code_impl<geographic_tag>
  195. : direction_code_impl<spherical_equatorial_tag>
  196. {};
  197. // Gives sense of direction for point p, collinear w.r.t. segment (a,b)
  198. // Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a
  199. // Returns 1 if p goes forward, so extends (a,b)
  200. // Returns 0 if p is equal with b, or if (a,b) is degenerate
  201. // Note that it does not do any collinearity test, that should be done before
  202. template <typename CSTag, typename Point1, typename Point2>
  203. inline int direction_code(Point1 const& segment_a, Point1 const& segment_b,
  204. Point2 const& p)
  205. {
  206. return direction_code_impl<CSTag>::apply(segment_a, segment_b, p);
  207. }
  208. } // namespace detail
  209. #endif //DOXYGEN_NO_DETAIL
  210. }} // namespace boost::geometry
  211. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP