distance_cross_track_box_box.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2016-2018 Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fisikopoulos, 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_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP
  9. #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP
  10. #include <boost/config.hpp>
  11. #include <boost/concept_check.hpp>
  12. #include <boost/mpl/if.hpp>
  13. #include <boost/type_traits/is_void.hpp>
  14. #include <boost/geometry/core/access.hpp>
  15. #include <boost/geometry/core/assert.hpp>
  16. #include <boost/geometry/core/point_type.hpp>
  17. #include <boost/geometry/core/radian_access.hpp>
  18. #include <boost/geometry/core/tags.hpp>
  19. #include <boost/geometry/strategies/distance.hpp>
  20. #include <boost/geometry/strategies/concepts/distance_concept.hpp>
  21. #include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
  22. #include <boost/geometry/util/math.hpp>
  23. #include <boost/geometry/algorithms/detail/assign_box_corners.hpp>
  24. namespace boost { namespace geometry
  25. {
  26. namespace strategy { namespace distance
  27. {
  28. namespace details
  29. {
  30. template <typename ReturnType>
  31. class cross_track_box_box_generic
  32. {
  33. public :
  34. template <typename Point, typename PPStrategy, typename PSStrategy>
  35. ReturnType static inline diagonal_case(Point topA,
  36. Point topB,
  37. Point bottomA,
  38. Point bottomB,
  39. bool north_shortest,
  40. bool non_overlap,
  41. PPStrategy pp_strategy,
  42. PSStrategy ps_strategy)
  43. {
  44. if (north_shortest && non_overlap)
  45. {
  46. return pp_strategy.apply(topA, bottomB);
  47. }
  48. if (north_shortest && !non_overlap)
  49. {
  50. return ps_strategy.apply(topA, topB, bottomB);
  51. }
  52. if (!north_shortest && non_overlap)
  53. {
  54. return pp_strategy.apply(bottomA, topB);
  55. }
  56. return ps_strategy.apply(bottomA, topB, bottomB);
  57. }
  58. template
  59. <
  60. typename Box1,
  61. typename Box2,
  62. typename PPStrategy,
  63. typename PSStrategy
  64. >
  65. ReturnType static inline apply (Box1 const& box1,
  66. Box2 const& box2,
  67. PPStrategy pp_strategy,
  68. PSStrategy ps_strategy)
  69. {
  70. // this method assumes that the coordinates of the point and
  71. // the box are normalized
  72. typedef typename point_type<Box1>::type box_point_type1;
  73. typedef typename point_type<Box2>::type box_point_type2;
  74. box_point_type1 bottom_left1, bottom_right1, top_left1, top_right1;
  75. geometry::detail::assign_box_corners(box1,
  76. bottom_left1, bottom_right1,
  77. top_left1, top_right1);
  78. box_point_type2 bottom_left2, bottom_right2, top_left2, top_right2;
  79. geometry::detail::assign_box_corners(box2,
  80. bottom_left2, bottom_right2,
  81. top_left2, top_right2);
  82. ReturnType lon_min1 = geometry::get_as_radian<0>(bottom_left1);
  83. ReturnType const lat_min1 = geometry::get_as_radian<1>(bottom_left1);
  84. ReturnType lon_max1 = geometry::get_as_radian<0>(top_right1);
  85. ReturnType const lat_max1 = geometry::get_as_radian<1>(top_right1);
  86. ReturnType lon_min2 = geometry::get_as_radian<0>(bottom_left2);
  87. ReturnType const lat_min2 = geometry::get_as_radian<1>(bottom_left2);
  88. ReturnType lon_max2 = geometry::get_as_radian<0>(top_right2);
  89. ReturnType const lat_max2 = geometry::get_as_radian<1>(top_right2);
  90. ReturnType const two_pi = math::two_pi<ReturnType>();
  91. // Test which sides of the boxes are closer and if boxes cross
  92. // antimeridian
  93. bool right_wrap;
  94. if (lon_min2 > 0 && lon_max2 < 0) // box2 crosses antimeridian
  95. {
  96. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  97. std::cout << "(box2 crosses antimeridian)";
  98. #endif
  99. right_wrap = lon_min2 - lon_max1 < lon_min1 - lon_max2;
  100. lon_max2 += two_pi;
  101. if (lon_min1 > 0 && lon_max1 < 0) // both boxes crosses antimeridian
  102. {
  103. lon_max1 += two_pi;
  104. }
  105. }
  106. else if (lon_min1 > 0 && lon_max1 < 0) // only box1 crosses antimeridian
  107. {
  108. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  109. std::cout << "(box1 crosses antimeridian)";
  110. #endif
  111. return apply(box2, box1, pp_strategy, ps_strategy);
  112. }
  113. else
  114. {
  115. right_wrap = lon_max1 <= lon_min2
  116. ? lon_min2 - lon_max1 < two_pi - (lon_max2 - lon_min1)
  117. : lon_min1 - lon_max2 > two_pi - (lon_max1 - lon_min2);
  118. }
  119. // Check1: if box2 crosses the band defined by the
  120. // minimum and maximum longitude of box1; if yes, determine
  121. // if the box2 is above, below or intersects/is inside box1 and compute
  122. // the distance (easy in this case)
  123. bool lon_min12 = lon_min1 <= lon_min2;
  124. bool right = lon_max1 <= lon_min2;
  125. bool left = lon_min1 >= lon_max2;
  126. bool lon_max12 = lon_max1 <= lon_max2;
  127. if ((lon_min12 && !right)
  128. || (!left && !lon_max12)
  129. || (!lon_min12 && lon_max12))
  130. {
  131. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  132. std::cout << "(up-down)\n";
  133. #endif
  134. if (lat_min1 > lat_max2)
  135. {
  136. return geometry::strategy::distance::services::result_from_distance
  137. <
  138. PSStrategy, box_point_type1, box_point_type2
  139. >::apply(ps_strategy, ps_strategy
  140. .vertical_or_meridian(lat_min1, lat_max2));
  141. }
  142. else if (lat_max1 < lat_min2)
  143. {
  144. return geometry::strategy::distance::services::result_from_distance
  145. <
  146. PSStrategy, box_point_type1, box_point_type2
  147. >::apply(ps_strategy, ps_strategy
  148. .vertical_or_meridian(lat_min2, lat_max1));
  149. }
  150. else
  151. {
  152. //BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max);
  153. return ReturnType(0);
  154. }
  155. }
  156. // Check2: if box2 is right/left of box1
  157. // the max lat of box2 should be less than the max lat of box1
  158. bool bottom_max;
  159. ReturnType top_common = (std::min)(lat_max1, lat_max2);
  160. ReturnType bottom_common = (std::max)(lat_min1, lat_min2);
  161. // true if the closest points are on northern hemisphere
  162. bool north_shortest = top_common + bottom_common > 0;
  163. // true if box bands do not overlap
  164. bool non_overlap = top_common < bottom_common;
  165. if (north_shortest)
  166. {
  167. bottom_max = lat_max1 >= lat_max2;
  168. }
  169. else
  170. {
  171. bottom_max = lat_min1 <= lat_min2;
  172. }
  173. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  174. std::cout << "(diagonal)";
  175. #endif
  176. if (bottom_max && !right_wrap)
  177. {
  178. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  179. std::cout << "(bottom left)";
  180. #endif
  181. return diagonal_case(top_right2, top_left1,
  182. bottom_right2, bottom_left1,
  183. north_shortest, non_overlap,
  184. pp_strategy, ps_strategy);
  185. }
  186. if (bottom_max && right_wrap)
  187. {
  188. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  189. std::cout << "(bottom right)";
  190. #endif
  191. return diagonal_case(top_left2, top_right1,
  192. bottom_left2, bottom_right1,
  193. north_shortest, non_overlap,
  194. pp_strategy, ps_strategy);
  195. }
  196. if (!bottom_max && !right_wrap)
  197. {
  198. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  199. std::cout << "(top left)";
  200. #endif
  201. return diagonal_case(top_left1, top_right2,
  202. bottom_left1, bottom_right2,
  203. north_shortest, non_overlap,
  204. pp_strategy, ps_strategy);
  205. }
  206. if (!bottom_max && right_wrap)
  207. {
  208. #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
  209. std::cout << "(top right)";
  210. #endif
  211. return diagonal_case(top_right1, top_left2,
  212. bottom_right1, bottom_left2,
  213. north_shortest, non_overlap,
  214. pp_strategy, ps_strategy);
  215. }
  216. return ReturnType(0);
  217. }
  218. };
  219. } //namespace details
  220. /*!
  221. \brief Strategy functor for distance box to box calculation
  222. \ingroup strategies
  223. \details Class which calculates the distance of a box to a box, for
  224. boxes on a sphere or globe
  225. \tparam CalculationType \tparam_calculation
  226. \tparam Strategy underlying point-segment distance strategy, defaults
  227. to cross track
  228. \qbk{
  229. [heading See also]
  230. [link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)]
  231. }
  232. */
  233. template
  234. <
  235. typename CalculationType = void,
  236. typename Strategy = haversine<double, CalculationType>
  237. >
  238. class cross_track_box_box
  239. {
  240. public:
  241. template <typename Box1, typename Box2>
  242. struct return_type
  243. : services::return_type<Strategy,
  244. typename point_type<Box1>::type,
  245. typename point_type<Box2>::type>
  246. {};
  247. typedef typename Strategy::radius_type radius_type;
  248. // strategy getters
  249. // point-segment strategy getters
  250. struct distance_ps_strategy
  251. {
  252. typedef cross_track<CalculationType, Strategy> type;
  253. };
  254. typedef typename strategy::distance::services::comparable_type
  255. <
  256. Strategy
  257. >::type pp_comparable_strategy;
  258. typedef typename boost::mpl::if_
  259. <
  260. boost::is_same
  261. <
  262. pp_comparable_strategy,
  263. Strategy
  264. >,
  265. typename strategy::distance::services::comparable_type
  266. <
  267. typename distance_ps_strategy::type
  268. >::type,
  269. typename distance_ps_strategy::type
  270. >::type ps_strategy_type;
  271. // constructors
  272. inline cross_track_box_box()
  273. {}
  274. explicit inline cross_track_box_box(typename Strategy::radius_type const& r)
  275. : m_strategy(r)
  276. {}
  277. inline cross_track_box_box(Strategy const& s)
  278. : m_strategy(s)
  279. {}
  280. // It might be useful in the future
  281. // to overload constructor with strategy info.
  282. // crosstrack(...) {}
  283. template <typename Box1, typename Box2>
  284. inline typename return_type<Box1, Box2>::type
  285. apply(Box1 const& box1, Box2 const& box2) const
  286. {
  287. #if !defined(BOOST_MSVC)
  288. BOOST_CONCEPT_ASSERT
  289. (
  290. (concepts::PointDistanceStrategy
  291. <
  292. Strategy,
  293. typename point_type<Box1>::type,
  294. typename point_type<Box2>::type
  295. >)
  296. );
  297. #endif
  298. typedef typename return_type<Box1, Box2>::type return_type;
  299. return details::cross_track_box_box_generic
  300. <return_type>::apply(box1, box2,
  301. m_strategy,
  302. ps_strategy_type(m_strategy));
  303. }
  304. inline typename Strategy::radius_type radius() const
  305. {
  306. return m_strategy.radius();
  307. }
  308. private:
  309. Strategy m_strategy;
  310. };
  311. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  312. namespace services
  313. {
  314. template <typename CalculationType, typename Strategy>
  315. struct tag<cross_track_box_box<CalculationType, Strategy> >
  316. {
  317. typedef strategy_tag_distance_box_box type;
  318. };
  319. template <typename CalculationType, typename Strategy, typename Box1, typename Box2>
  320. struct return_type<cross_track_box_box<CalculationType, Strategy>, Box1, Box2>
  321. : cross_track_box_box
  322. <
  323. CalculationType, Strategy
  324. >::template return_type<Box1, Box2>
  325. {};
  326. template <typename CalculationType, typename Strategy>
  327. struct comparable_type<cross_track_box_box<CalculationType, Strategy> >
  328. {
  329. typedef cross_track_box_box
  330. <
  331. CalculationType, typename comparable_type<Strategy>::type
  332. > type;
  333. };
  334. template <typename CalculationType, typename Strategy>
  335. struct get_comparable<cross_track_box_box<CalculationType, Strategy> >
  336. {
  337. typedef cross_track_box_box<CalculationType, Strategy> this_strategy;
  338. typedef typename comparable_type<this_strategy>::type comparable_type;
  339. public:
  340. static inline comparable_type apply(this_strategy const& strategy)
  341. {
  342. return comparable_type(strategy.radius());
  343. }
  344. };
  345. template <typename CalculationType, typename Strategy, typename Box1, typename Box2>
  346. struct result_from_distance
  347. <
  348. cross_track_box_box<CalculationType, Strategy>, Box1, Box2
  349. >
  350. {
  351. private:
  352. typedef cross_track_box_box<CalculationType, Strategy> this_strategy;
  353. typedef typename this_strategy::template return_type
  354. <
  355. Box1, Box2
  356. >::type return_type;
  357. public:
  358. template <typename T>
  359. static inline return_type apply(this_strategy const& strategy,
  360. T const& distance)
  361. {
  362. Strategy s(strategy.radius());
  363. return result_from_distance
  364. <
  365. Strategy,
  366. typename point_type<Box1>::type,
  367. typename point_type<Box2>::type
  368. >::apply(s, distance);
  369. }
  370. };
  371. // define cross_track_box_box<default_point_segment_strategy> as
  372. // default box-box strategy for the spherical equatorial coordinate system
  373. template <typename Box1, typename Box2, typename Strategy>
  374. struct default_strategy
  375. <
  376. box_tag, box_tag, Box1, Box2,
  377. spherical_equatorial_tag, spherical_equatorial_tag,
  378. Strategy
  379. >
  380. {
  381. typedef cross_track_box_box
  382. <
  383. void,
  384. typename boost::mpl::if_
  385. <
  386. boost::is_void<Strategy>,
  387. typename default_strategy
  388. <
  389. point_tag, point_tag,
  390. typename point_type<Box1>::type, typename point_type<Box2>::type,
  391. spherical_equatorial_tag, spherical_equatorial_tag
  392. >::type,
  393. Strategy
  394. >::type
  395. > type;
  396. };
  397. } // namespace services
  398. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  399. }} // namespace strategy::distance
  400. }} // namespace boost::geometry
  401. #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP