multipolygon.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2014-2019, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Licensed under the Boost Software License version 1.0.
  6. // http://www.boost.org/users/license.html
  7. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
  8. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
  9. #include <deque>
  10. #include <vector>
  11. #include <boost/core/ignore_unused.hpp>
  12. #include <boost/iterator/filter_iterator.hpp>
  13. #include <boost/range.hpp>
  14. #include <boost/geometry/core/exterior_ring.hpp>
  15. #include <boost/geometry/core/interior_rings.hpp>
  16. #include <boost/geometry/core/ring_type.hpp>
  17. #include <boost/geometry/core/tags.hpp>
  18. #include <boost/geometry/util/condition.hpp>
  19. #include <boost/geometry/util/range.hpp>
  20. #include <boost/geometry/geometries/box.hpp>
  21. #include <boost/geometry/algorithms/validity_failure_type.hpp>
  22. #include <boost/geometry/algorithms/within.hpp>
  23. #include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
  24. #include <boost/geometry/algorithms/detail/partition.hpp>
  25. #include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
  26. #include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
  27. #include <boost/geometry/algorithms/detail/is_valid/polygon.hpp>
  28. #include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
  29. #include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
  30. #include <boost/geometry/algorithms/dispatch/is_valid.hpp>
  31. #include <boost/geometry/strategies/intersection.hpp>
  32. namespace boost { namespace geometry
  33. {
  34. #ifndef DOXYGEN_NO_DETAIL
  35. namespace detail { namespace is_valid
  36. {
  37. template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
  38. class is_valid_multipolygon
  39. : is_valid_polygon
  40. <
  41. typename boost::range_value<MultiPolygon>::type,
  42. true // check only the validity of rings
  43. >
  44. {
  45. private:
  46. typedef is_valid_polygon
  47. <
  48. typename boost::range_value<MultiPolygon>::type,
  49. true
  50. > base;
  51. template
  52. <
  53. typename PolygonIterator,
  54. typename TurnIterator,
  55. typename VisitPolicy,
  56. typename Strategy
  57. >
  58. static inline
  59. bool are_polygon_interiors_disjoint(PolygonIterator polygons_first,
  60. PolygonIterator polygons_beyond,
  61. TurnIterator turns_first,
  62. TurnIterator turns_beyond,
  63. VisitPolicy& visitor,
  64. Strategy const& strategy)
  65. {
  66. boost::ignore_unused(visitor);
  67. // collect all polygons that have crossing turns
  68. std::set<signed_size_type> multi_indices;
  69. for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
  70. {
  71. if (! tit->touch_only)
  72. {
  73. multi_indices.insert(tit->operations[0].seg_id.multi_index);
  74. multi_indices.insert(tit->operations[1].seg_id.multi_index);
  75. }
  76. }
  77. typedef geometry::model::box<typename point_type<MultiPolygon>::type> box_type;
  78. typedef typename base::template partition_item<PolygonIterator, box_type> item_type;
  79. // put polygon iterators without turns in a vector
  80. std::vector<item_type> polygon_iterators;
  81. signed_size_type multi_index = 0;
  82. for (PolygonIterator it = polygons_first; it != polygons_beyond;
  83. ++it, ++multi_index)
  84. {
  85. if (multi_indices.find(multi_index) == multi_indices.end())
  86. {
  87. polygon_iterators.push_back(item_type(it));
  88. }
  89. }
  90. // prepare strategies
  91. typedef typename Strategy::envelope_strategy_type envelope_strategy_type;
  92. envelope_strategy_type const envelope_strategy
  93. = strategy.get_envelope_strategy();
  94. typedef typename Strategy::disjoint_box_box_strategy_type disjoint_box_box_strategy_type;
  95. disjoint_box_box_strategy_type const disjoint_strategy
  96. = strategy.get_disjoint_box_box_strategy();
  97. // call partition to check if polygons are disjoint from each other
  98. typename base::template item_visitor_type<Strategy> item_visitor(strategy);
  99. geometry::partition
  100. <
  101. geometry::model::box<typename point_type<MultiPolygon>::type>
  102. >::apply(polygon_iterators, item_visitor,
  103. typename base::template expand_box
  104. <
  105. envelope_strategy_type
  106. >(envelope_strategy),
  107. typename base::template overlaps_box
  108. <
  109. envelope_strategy_type,
  110. disjoint_box_box_strategy_type
  111. >(envelope_strategy, disjoint_strategy));
  112. if (item_visitor.items_overlap)
  113. {
  114. return visitor.template apply<failure_intersecting_interiors>();
  115. }
  116. else
  117. {
  118. return visitor.template apply<no_failure>();
  119. }
  120. }
  121. class has_multi_index
  122. {
  123. public:
  124. has_multi_index(signed_size_type multi_index)
  125. : m_multi_index(multi_index)
  126. {}
  127. template <typename Turn>
  128. inline bool operator()(Turn const& turn) const
  129. {
  130. return turn.operations[0].seg_id.multi_index == m_multi_index
  131. && turn.operations[1].seg_id.multi_index == m_multi_index;
  132. }
  133. private:
  134. signed_size_type const m_multi_index;
  135. };
  136. template <typename Predicate>
  137. struct has_property_per_polygon
  138. {
  139. template
  140. <
  141. typename PolygonIterator,
  142. typename TurnIterator,
  143. typename VisitPolicy,
  144. typename Strategy
  145. >
  146. static inline bool apply(PolygonIterator polygons_first,
  147. PolygonIterator polygons_beyond,
  148. TurnIterator turns_first,
  149. TurnIterator turns_beyond,
  150. VisitPolicy& visitor,
  151. Strategy const& strategy)
  152. {
  153. signed_size_type multi_index = 0;
  154. for (PolygonIterator it = polygons_first; it != polygons_beyond;
  155. ++it, ++multi_index)
  156. {
  157. has_multi_index index_predicate(multi_index);
  158. typedef boost::filter_iterator
  159. <
  160. has_multi_index, TurnIterator
  161. > filtered_turn_iterator;
  162. filtered_turn_iterator filtered_turns_first(index_predicate,
  163. turns_first,
  164. turns_beyond);
  165. filtered_turn_iterator filtered_turns_beyond(index_predicate,
  166. turns_beyond,
  167. turns_beyond);
  168. if (! Predicate::apply(*it,
  169. filtered_turns_first,
  170. filtered_turns_beyond,
  171. visitor,
  172. strategy))
  173. {
  174. return false;
  175. }
  176. }
  177. return true;
  178. }
  179. };
  180. template
  181. <
  182. typename PolygonIterator,
  183. typename TurnIterator,
  184. typename VisitPolicy,
  185. typename Strategy
  186. >
  187. static inline bool have_holes_inside(PolygonIterator polygons_first,
  188. PolygonIterator polygons_beyond,
  189. TurnIterator turns_first,
  190. TurnIterator turns_beyond,
  191. VisitPolicy& visitor,
  192. Strategy const& strategy)
  193. {
  194. return has_property_per_polygon
  195. <
  196. typename base::has_holes_inside
  197. >::apply(polygons_first, polygons_beyond,
  198. turns_first, turns_beyond, visitor, strategy);
  199. }
  200. template
  201. <
  202. typename PolygonIterator,
  203. typename TurnIterator,
  204. typename VisitPolicy,
  205. typename Strategy
  206. >
  207. static inline bool have_connected_interior(PolygonIterator polygons_first,
  208. PolygonIterator polygons_beyond,
  209. TurnIterator turns_first,
  210. TurnIterator turns_beyond,
  211. VisitPolicy& visitor,
  212. Strategy const& strategy)
  213. {
  214. return has_property_per_polygon
  215. <
  216. typename base::has_connected_interior
  217. >::apply(polygons_first, polygons_beyond,
  218. turns_first, turns_beyond, visitor, strategy);
  219. }
  220. template <typename VisitPolicy, typename Strategy>
  221. struct per_polygon
  222. {
  223. per_polygon(VisitPolicy& policy, Strategy const& strategy)
  224. : m_policy(policy)
  225. , m_strategy(strategy)
  226. {}
  227. template <typename Polygon>
  228. inline bool apply(Polygon const& polygon) const
  229. {
  230. return base::apply(polygon, m_policy, m_strategy);
  231. }
  232. VisitPolicy& m_policy;
  233. Strategy const& m_strategy;
  234. };
  235. public:
  236. template <typename VisitPolicy, typename Strategy>
  237. static inline bool apply(MultiPolygon const& multipolygon,
  238. VisitPolicy& visitor,
  239. Strategy const& strategy)
  240. {
  241. typedef debug_validity_phase<MultiPolygon> debug_phase;
  242. if (BOOST_GEOMETRY_CONDITION(AllowEmptyMultiGeometries)
  243. && boost::empty(multipolygon))
  244. {
  245. return visitor.template apply<no_failure>();
  246. }
  247. // check validity of all polygons ring
  248. debug_phase::apply(1);
  249. if (! detail::check_iterator_range
  250. <
  251. per_polygon<VisitPolicy, Strategy>,
  252. false // do not check for empty multipolygon (done above)
  253. >::apply(boost::begin(multipolygon),
  254. boost::end(multipolygon),
  255. per_polygon<VisitPolicy, Strategy>(visitor, strategy)))
  256. {
  257. return false;
  258. }
  259. // compute turns and check if all are acceptable
  260. debug_phase::apply(2);
  261. typedef has_valid_self_turns<MultiPolygon, typename Strategy::cs_tag> has_valid_turns;
  262. std::deque<typename has_valid_turns::turn_type> turns;
  263. bool has_invalid_turns =
  264. ! has_valid_turns::apply(multipolygon, turns, visitor, strategy);
  265. debug_print_turns(turns.begin(), turns.end());
  266. if (has_invalid_turns)
  267. {
  268. return false;
  269. }
  270. // check if each polygon's interior rings are inside the
  271. // exterior and not one inside the other
  272. debug_phase::apply(3);
  273. if (! have_holes_inside(boost::begin(multipolygon),
  274. boost::end(multipolygon),
  275. turns.begin(),
  276. turns.end(),
  277. visitor,
  278. strategy))
  279. {
  280. return false;
  281. }
  282. // check that each polygon's interior is connected
  283. debug_phase::apply(4);
  284. if (! have_connected_interior(boost::begin(multipolygon),
  285. boost::end(multipolygon),
  286. turns.begin(),
  287. turns.end(),
  288. visitor,
  289. strategy))
  290. {
  291. return false;
  292. }
  293. // check if polygon interiors are disjoint
  294. debug_phase::apply(5);
  295. return are_polygon_interiors_disjoint(boost::begin(multipolygon),
  296. boost::end(multipolygon),
  297. turns.begin(),
  298. turns.end(),
  299. visitor,
  300. strategy);
  301. }
  302. };
  303. }} // namespace detail::is_valid
  304. #endif // DOXYGEN_NO_DETAIL
  305. #ifndef DOXYGEN_NO_DISPATCH
  306. namespace dispatch
  307. {
  308. // Not clear what the definition is.
  309. // Right now we check that each element is simple (in fact valid), and
  310. // that the MultiPolygon is also valid.
  311. //
  312. // Reference (for validity of MultiPolygons): OGC 06-103r4 (6.1.14)
  313. template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
  314. struct is_valid
  315. <
  316. MultiPolygon, multi_polygon_tag, AllowEmptyMultiGeometries
  317. > : detail::is_valid::is_valid_multipolygon
  318. <
  319. MultiPolygon, AllowEmptyMultiGeometries
  320. >
  321. {};
  322. } // namespace dispatch
  323. #endif // DOXYGEN_NO_DISPATCH
  324. }} // namespace boost::geometry
  325. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP