multi_point.hpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Boost.Geometry
  2. // Copyright (c) 2017, 2019 Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, 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_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP
  8. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP
  9. #include <algorithm>
  10. #include <vector>
  11. #include <boost/range.hpp>
  12. #include <boost/type_traits/is_same.hpp>
  13. #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
  14. #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
  15. #include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
  16. #include <boost/geometry/algorithms/detail/within/point_in_geometry.hpp>
  17. #include <boost/geometry/algorithms/envelope.hpp>
  18. #include <boost/geometry/algorithms/detail/partition.hpp>
  19. #include <boost/geometry/core/tag.hpp>
  20. #include <boost/geometry/core/tag_cast.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/geometries/box.hpp>
  23. #include <boost/geometry/index/rtree.hpp>
  24. #include <boost/geometry/policies/compare.hpp>
  25. #include <boost/geometry/strategies/covered_by.hpp>
  26. #include <boost/geometry/strategies/disjoint.hpp>
  27. namespace boost { namespace geometry {
  28. #ifndef DOXYGEN_NO_DETAIL
  29. namespace detail { namespace within {
  30. struct multi_point_point
  31. {
  32. template <typename MultiPoint, typename Point, typename Strategy>
  33. static inline bool apply(MultiPoint const& multi_point,
  34. Point const& point,
  35. Strategy const& strategy)
  36. {
  37. typedef typename boost::range_const_iterator<MultiPoint>::type iterator;
  38. for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it )
  39. {
  40. if (! strategy.apply(*it, point))
  41. {
  42. return false;
  43. }
  44. }
  45. // all points of MultiPoint inside Point
  46. return true;
  47. }
  48. };
  49. // NOTE: currently the strategy is ignored, math::equals() is used inside geometry::less<>
  50. struct multi_point_multi_point
  51. {
  52. template <typename MultiPoint1, typename MultiPoint2, typename Strategy>
  53. static inline bool apply(MultiPoint1 const& multi_point1,
  54. MultiPoint2 const& multi_point2,
  55. Strategy const& /*strategy*/)
  56. {
  57. typedef typename boost::range_value<MultiPoint2>::type point2_type;
  58. typedef typename Strategy::cs_tag cs_tag;
  59. typedef geometry::less<void, -1, cs_tag> less_type;
  60. less_type const less = less_type();
  61. std::vector<point2_type> points2(boost::begin(multi_point2), boost::end(multi_point2));
  62. std::sort(points2.begin(), points2.end(), less);
  63. bool result = false;
  64. typedef typename boost::range_const_iterator<MultiPoint1>::type iterator;
  65. for ( iterator it = boost::begin(multi_point1) ; it != boost::end(multi_point1) ; ++it )
  66. {
  67. if (! std::binary_search(points2.begin(), points2.end(), *it, less))
  68. {
  69. return false;
  70. }
  71. else
  72. {
  73. result = true;
  74. }
  75. }
  76. return result;
  77. }
  78. };
  79. // TODO: the complexity could be lesser
  80. // the second geometry could be "prepared"/sorted
  81. // For Linear geometries partition could be used
  82. // For Areal geometries point_in_geometry() would have to call the winding
  83. // strategy differently, currently it linearly calls the strategy for each
  84. // segment. So the segments would have to be sorted in a way consistent with
  85. // the strategy and then the strategy called only for the segments in range.
  86. template <bool Within>
  87. struct multi_point_single_geometry
  88. {
  89. template <typename MultiPoint, typename LinearOrAreal, typename Strategy>
  90. static inline bool apply(MultiPoint const& multi_point,
  91. LinearOrAreal const& linear_or_areal,
  92. Strategy const& strategy)
  93. {
  94. //typedef typename boost::range_value<MultiPoint>::type point1_type;
  95. typedef typename point_type<LinearOrAreal>::type point2_type;
  96. typedef model::box<point2_type> box2_type;
  97. // Create envelope of geometry
  98. box2_type box;
  99. geometry::envelope(linear_or_areal, box, strategy.get_envelope_strategy());
  100. geometry::detail::expand_by_epsilon(box);
  101. typedef typename Strategy::disjoint_point_box_strategy_type point_in_box_type;
  102. // Test each Point with envelope and then geometry if needed
  103. // If in the exterior, break
  104. bool result = false;
  105. typedef typename boost::range_const_iterator<MultiPoint>::type iterator;
  106. for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it )
  107. {
  108. int in_val = 0;
  109. // exterior of box and of geometry
  110. if (! point_in_box_type::apply(*it, box)
  111. || (in_val = point_in_geometry(*it, linear_or_areal, strategy)) < 0)
  112. {
  113. result = false;
  114. break;
  115. }
  116. // interior : interior/boundary
  117. if (Within ? in_val > 0 : in_val >= 0)
  118. {
  119. result = true;
  120. }
  121. }
  122. return result;
  123. }
  124. };
  125. // TODO: same here, probably the complexity could be lesser
  126. template <bool Within>
  127. struct multi_point_multi_geometry
  128. {
  129. template <typename MultiPoint, typename LinearOrAreal, typename Strategy>
  130. static inline bool apply(MultiPoint const& multi_point,
  131. LinearOrAreal const& linear_or_areal,
  132. Strategy const& strategy)
  133. {
  134. typedef typename point_type<LinearOrAreal>::type point2_type;
  135. typedef model::box<point2_type> box2_type;
  136. static const bool is_linear = is_same
  137. <
  138. typename tag_cast
  139. <
  140. typename tag<LinearOrAreal>::type,
  141. linear_tag
  142. >::type,
  143. linear_tag
  144. >::value;
  145. typename Strategy::envelope_strategy_type const
  146. envelope_strategy = strategy.get_envelope_strategy();
  147. // TODO: box pairs could be constructed on the fly, inside the rtree
  148. // Prepare range of envelopes and ids
  149. std::size_t count2 = boost::size(linear_or_areal);
  150. typedef std::pair<box2_type, std::size_t> box_pair_type;
  151. typedef std::vector<box_pair_type> box_pair_vector;
  152. box_pair_vector boxes(count2);
  153. for (std::size_t i = 0 ; i < count2 ; ++i)
  154. {
  155. geometry::envelope(linear_or_areal, boxes[i].first, envelope_strategy);
  156. geometry::detail::expand_by_epsilon(boxes[i].first);
  157. boxes[i].second = i;
  158. }
  159. // Create R-tree
  160. typedef strategy::index::services::from_strategy
  161. <
  162. Strategy
  163. > index_strategy_from;
  164. typedef index::parameters
  165. <
  166. index::rstar<4>, typename index_strategy_from::type
  167. > index_parameters_type;
  168. index::rtree<box_pair_type, index_parameters_type>
  169. rtree(boxes.begin(), boxes.end(),
  170. index_parameters_type(index::rstar<4>(), index_strategy_from::get(strategy)));
  171. // For each point find overlapping envelopes and test corresponding single geometries
  172. // If a point is in the exterior break
  173. bool result = false;
  174. typedef typename boost::range_const_iterator<MultiPoint>::type iterator;
  175. for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it )
  176. {
  177. // TODO: investigate the possibility of using satisfies
  178. // TODO: investigate the possibility of using iterative queries (optimization below)
  179. box_pair_vector inters_boxes;
  180. rtree.query(index::intersects(*it), std::back_inserter(inters_boxes));
  181. bool found_interior = false;
  182. bool found_boundary = false;
  183. int boundaries = 0;
  184. typedef typename box_pair_vector::const_iterator box_iterator;
  185. for (box_iterator box_it = inters_boxes.begin() ;
  186. box_it != inters_boxes.end() ; ++box_it )
  187. {
  188. int const in_val = point_in_geometry(*it,
  189. range::at(linear_or_areal, box_it->second), strategy);
  190. if (in_val > 0)
  191. {
  192. found_interior = true;
  193. }
  194. else if (in_val == 0)
  195. {
  196. ++boundaries;
  197. }
  198. // If the result was set previously (interior or
  199. // interior/boundary found) the only thing that needs to be
  200. // done for other points is to make sure they're not
  201. // overlapping the exterior no need to analyse boundaries.
  202. if (result && in_val >= 0)
  203. {
  204. break;
  205. }
  206. }
  207. if (boundaries > 0)
  208. {
  209. if (is_linear && boundaries % 2 == 0)
  210. {
  211. found_interior = true;
  212. }
  213. else
  214. {
  215. found_boundary = true;
  216. }
  217. }
  218. // exterior
  219. if (! found_interior && ! found_boundary)
  220. {
  221. result = false;
  222. break;
  223. }
  224. // interior : interior/boundary
  225. if (Within ? found_interior : (found_interior || found_boundary))
  226. {
  227. result = true;
  228. }
  229. }
  230. return result;
  231. }
  232. };
  233. }} // namespace detail::within
  234. #endif // DOXYGEN_NO_DETAIL
  235. }} // namespace boost::geometry
  236. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP