polygon.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2014-2023, Oracle and/or its affiliates.
  4. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  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. // Licensed under the Boost Software License version 1.0.
  8. // http://www.boost.org/users/license.html
  9. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
  11. #include <cstddef>
  12. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  13. #include <iostream>
  14. #endif // BOOST_GEOMETRY_TEST_DEBUG
  15. #include <algorithm>
  16. #include <deque>
  17. #include <iterator>
  18. #include <set>
  19. #include <vector>
  20. #include <boost/core/ignore_unused.hpp>
  21. #include <boost/range/begin.hpp>
  22. #include <boost/range/end.hpp>
  23. #include <boost/geometry/core/assert.hpp>
  24. #include <boost/geometry/core/exterior_ring.hpp>
  25. #include <boost/geometry/core/interior_rings.hpp>
  26. #include <boost/geometry/core/ring_type.hpp>
  27. #include <boost/geometry/core/tags.hpp>
  28. #include <boost/geometry/util/constexpr.hpp>
  29. #include <boost/geometry/util/range.hpp>
  30. #include <boost/geometry/util/sequence.hpp>
  31. #include <boost/geometry/geometries/box.hpp>
  32. #include <boost/geometry/iterators/point_iterator.hpp>
  33. #include <boost/geometry/algorithms/covered_by.hpp>
  34. #include <boost/geometry/algorithms/disjoint.hpp>
  35. #include <boost/geometry/algorithms/expand.hpp>
  36. #include <boost/geometry/algorithms/num_interior_rings.hpp>
  37. #include <boost/geometry/algorithms/validity_failure_type.hpp>
  38. #include <boost/geometry/algorithms/detail/point_on_border.hpp>
  39. #include <boost/geometry/algorithms/within.hpp>
  40. #include <boost/geometry/algorithms/detail/partition.hpp>
  41. #include <boost/geometry/algorithms/detail/is_valid/complement_graph.hpp>
  42. #include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
  43. #include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
  44. #include <boost/geometry/algorithms/detail/is_valid/ring.hpp>
  45. #include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
  46. #include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
  47. #include <boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp>
  48. #include <boost/geometry/algorithms/dispatch/is_valid.hpp>
  49. // TEMP
  50. #include <boost/geometry/strategies/envelope/cartesian.hpp>
  51. #include <boost/geometry/strategies/envelope/geographic.hpp>
  52. #include <boost/geometry/strategies/envelope/spherical.hpp>
  53. namespace boost { namespace geometry
  54. {
  55. #ifndef DOXYGEN_NO_DETAIL
  56. namespace detail { namespace is_valid
  57. {
  58. template <typename Polygon, bool CheckRingValidityOnly = false>
  59. class is_valid_polygon
  60. {
  61. protected:
  62. template <typename VisitPolicy, typename Strategy>
  63. struct is_invalid_ring
  64. {
  65. is_invalid_ring(VisitPolicy& policy, Strategy const& strategy)
  66. : m_policy(policy)
  67. , m_strategy(strategy)
  68. {}
  69. template <typename Ring>
  70. inline bool operator()(Ring const& ring) const
  71. {
  72. return ! detail::is_valid::is_valid_ring
  73. <
  74. Ring, false, true
  75. >::apply(ring, m_policy, m_strategy);
  76. }
  77. VisitPolicy& m_policy;
  78. Strategy const& m_strategy;
  79. };
  80. template <typename InteriorRings, typename VisitPolicy, typename Strategy>
  81. static bool has_valid_interior_rings(InteriorRings const& interior_rings,
  82. VisitPolicy& visitor,
  83. Strategy const& strategy)
  84. {
  85. return std::none_of(boost::begin(interior_rings),
  86. boost::end(interior_rings),
  87. is_invalid_ring<VisitPolicy, Strategy>(visitor, strategy));
  88. }
  89. struct has_valid_rings
  90. {
  91. template <typename VisitPolicy, typename Strategy>
  92. static inline bool apply(Polygon const& polygon,
  93. VisitPolicy& visitor,
  94. Strategy const& strategy)
  95. {
  96. typedef debug_validity_phase<Polygon> debug_phase;
  97. typedef typename ring_type<Polygon>::type ring_type;
  98. // check validity of exterior ring
  99. debug_phase::apply(1);
  100. if (! detail::is_valid::is_valid_ring
  101. <
  102. ring_type,
  103. false // do not check self intersections
  104. >::apply(exterior_ring(polygon), visitor, strategy))
  105. {
  106. return false;
  107. }
  108. // check validity of interior rings
  109. debug_phase::apply(2);
  110. return has_valid_interior_rings(geometry::interior_rings(polygon),
  111. visitor,
  112. strategy);
  113. }
  114. };
  115. // Iterator value_type is a Ring or Polygon
  116. template <typename Iterator, typename Box>
  117. struct partition_item
  118. {
  119. explicit partition_item(Iterator it)
  120. : m_it(it)
  121. , m_is_initialized(false)
  122. {}
  123. Iterator get() const
  124. {
  125. return m_it;
  126. }
  127. template <typename EnvelopeStrategy>
  128. Box const& get_envelope(EnvelopeStrategy const& strategy) const
  129. {
  130. if (! m_is_initialized)
  131. {
  132. m_box = geometry::return_envelope<Box>(*m_it, strategy);
  133. m_is_initialized = true;
  134. }
  135. return m_box;
  136. }
  137. private:
  138. Iterator m_it;
  139. mutable Box m_box;
  140. mutable bool m_is_initialized;
  141. };
  142. // structs for partition -- start
  143. template <typename Strategy>
  144. struct expand_box
  145. {
  146. explicit expand_box(Strategy const& strategy)
  147. : m_strategy(strategy)
  148. {}
  149. template <typename Box, typename Iterator>
  150. inline void apply(Box& total, partition_item<Iterator, Box> const& item) const
  151. {
  152. geometry::expand(total,
  153. item.get_envelope(m_strategy),
  154. m_strategy);
  155. }
  156. Strategy const& m_strategy;
  157. };
  158. template <typename Strategy>
  159. struct overlaps_box
  160. {
  161. explicit overlaps_box(Strategy const& strategy)
  162. : m_strategy(strategy)
  163. {}
  164. template <typename Box, typename Iterator>
  165. inline bool apply(Box const& box, partition_item<Iterator, Box> const& item) const
  166. {
  167. return ! geometry::disjoint(item.get_envelope(m_strategy),
  168. box,
  169. m_strategy);
  170. }
  171. Strategy const& m_strategy;
  172. };
  173. template <typename Strategy>
  174. struct item_visitor_type
  175. {
  176. bool items_overlap;
  177. Strategy const& m_strategy;
  178. explicit item_visitor_type(Strategy const& strategy)
  179. : items_overlap(false)
  180. , m_strategy(strategy)
  181. {}
  182. template <typename Iterator, typename Box>
  183. inline bool apply(partition_item<Iterator, Box> const& item1,
  184. partition_item<Iterator, Box> const& item2)
  185. {
  186. typedef util::type_sequence
  187. <
  188. geometry::de9im::static_mask<'T'>,
  189. geometry::de9im::static_mask<'*', 'T'>,
  190. geometry::de9im::static_mask<'*', '*', '*', 'T'>
  191. > relate_mask_t;
  192. if ( ! items_overlap
  193. && geometry::relate(*item1.get(), *item2.get(), relate_mask_t(), m_strategy) )
  194. {
  195. items_overlap = true;
  196. return false; // interrupt
  197. }
  198. return true;
  199. }
  200. };
  201. // structs for partition -- end
  202. template
  203. <
  204. typename RingIterator,
  205. typename ExteriorRing,
  206. typename TurnIterator,
  207. typename VisitPolicy,
  208. typename Strategy
  209. >
  210. static inline bool are_holes_inside(RingIterator rings_first,
  211. RingIterator rings_beyond,
  212. ExteriorRing const& exterior_ring,
  213. TurnIterator turns_first,
  214. TurnIterator turns_beyond,
  215. VisitPolicy& visitor,
  216. Strategy const& strategy)
  217. {
  218. boost::ignore_unused(visitor);
  219. // collect the interior ring indices that have turns with the
  220. // exterior ring
  221. std::set<signed_size_type> ring_indices;
  222. for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
  223. {
  224. if (tit->operations[0].seg_id.ring_index == -1)
  225. {
  226. BOOST_GEOMETRY_ASSERT(tit->operations[1].seg_id.ring_index != -1);
  227. ring_indices.insert(tit->operations[1].seg_id.ring_index);
  228. }
  229. else if (tit->operations[1].seg_id.ring_index == -1)
  230. {
  231. BOOST_GEOMETRY_ASSERT(tit->operations[0].seg_id.ring_index != -1);
  232. ring_indices.insert(tit->operations[0].seg_id.ring_index);
  233. }
  234. }
  235. signed_size_type ring_index = 0;
  236. for (RingIterator it = rings_first; it != rings_beyond;
  237. ++it, ++ring_index)
  238. {
  239. // do not examine interior rings that have turns with the
  240. // exterior ring
  241. if (ring_indices.find(ring_index) == ring_indices.end()
  242. && ! geometry::covered_by(range::front(*it), exterior_ring, strategy))
  243. {
  244. return visitor.template apply<failure_interior_rings_outside>();
  245. }
  246. }
  247. // collect all rings (exterior and/or interior) that have turns
  248. for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
  249. {
  250. ring_indices.insert(tit->operations[0].seg_id.ring_index);
  251. ring_indices.insert(tit->operations[1].seg_id.ring_index);
  252. }
  253. typedef geometry::model::box<typename point_type<Polygon>::type> box_type;
  254. typedef partition_item<RingIterator, box_type> item_type;
  255. // put iterators for interior rings without turns in a vector
  256. std::vector<item_type> ring_iterators;
  257. ring_index = 0;
  258. for (RingIterator it = rings_first; it != rings_beyond;
  259. ++it, ++ring_index)
  260. {
  261. if (ring_indices.find(ring_index) == ring_indices.end())
  262. {
  263. ring_iterators.push_back(item_type(it));
  264. }
  265. }
  266. // call partition to check if interior rings are disjoint from
  267. // each other
  268. item_visitor_type<Strategy> item_visitor(strategy);
  269. geometry::partition
  270. <
  271. box_type
  272. >::apply(ring_iterators, item_visitor,
  273. expand_box<Strategy>(strategy),
  274. overlaps_box<Strategy>(strategy));
  275. if (item_visitor.items_overlap)
  276. {
  277. return visitor.template apply<failure_nested_interior_rings>();
  278. }
  279. else
  280. {
  281. return visitor.template apply<no_failure>();
  282. }
  283. }
  284. template
  285. <
  286. typename InteriorRings,
  287. typename ExteriorRing,
  288. typename TurnIterator,
  289. typename VisitPolicy,
  290. typename Strategy
  291. >
  292. static inline bool are_holes_inside(InteriorRings const& interior_rings,
  293. ExteriorRing const& exterior_ring,
  294. TurnIterator first,
  295. TurnIterator beyond,
  296. VisitPolicy& visitor,
  297. Strategy const& strategy)
  298. {
  299. return are_holes_inside(boost::begin(interior_rings),
  300. boost::end(interior_rings),
  301. exterior_ring,
  302. first,
  303. beyond,
  304. visitor,
  305. strategy);
  306. }
  307. struct has_holes_inside
  308. {
  309. template <typename TurnIterator, typename VisitPolicy, typename Strategy>
  310. static inline bool apply(Polygon const& polygon,
  311. TurnIterator first,
  312. TurnIterator beyond,
  313. VisitPolicy& visitor,
  314. Strategy const& strategy)
  315. {
  316. return are_holes_inside(geometry::interior_rings(polygon),
  317. geometry::exterior_ring(polygon),
  318. first,
  319. beyond,
  320. visitor,
  321. strategy);
  322. }
  323. };
  324. struct has_connected_interior
  325. {
  326. template <typename TurnIterator, typename VisitPolicy, typename Strategy>
  327. static inline bool apply(Polygon const& polygon,
  328. TurnIterator first,
  329. TurnIterator beyond,
  330. VisitPolicy& visitor,
  331. Strategy const& )
  332. {
  333. boost::ignore_unused(visitor);
  334. typedef typename std::iterator_traits
  335. <
  336. TurnIterator
  337. >::value_type turn_type;
  338. typedef complement_graph
  339. <
  340. typename turn_type::point_type,
  341. Strategy
  342. > graph;
  343. graph g(geometry::num_interior_rings(polygon) + 1);
  344. for (TurnIterator tit = first; tit != beyond; ++tit)
  345. {
  346. typename graph::vertex_handle v1 = g.add_vertex
  347. ( tit->operations[0].seg_id.ring_index + 1 );
  348. typename graph::vertex_handle v2 = g.add_vertex
  349. ( tit->operations[1].seg_id.ring_index + 1 );
  350. typename graph::vertex_handle vip = g.add_vertex(tit->point);
  351. g.add_edge(v1, vip);
  352. g.add_edge(v2, vip);
  353. }
  354. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  355. debug_print_complement_graph(std::cout, g);
  356. #endif // BOOST_GEOMETRY_TEST_DEBUG
  357. if (g.has_cycles())
  358. {
  359. return visitor.template apply<failure_disconnected_interior>();
  360. }
  361. else
  362. {
  363. return visitor.template apply<no_failure>();
  364. }
  365. }
  366. };
  367. public:
  368. template <typename VisitPolicy, typename Strategy>
  369. static inline bool apply(Polygon const& polygon,
  370. VisitPolicy& visitor,
  371. Strategy const& strategy)
  372. {
  373. if (! has_valid_rings::apply(polygon, visitor, strategy))
  374. {
  375. return false;
  376. }
  377. if BOOST_GEOMETRY_CONSTEXPR (CheckRingValidityOnly)
  378. {
  379. return true;
  380. }
  381. else // else prevents unreachable code warning
  382. {
  383. // compute turns and check if all are acceptable
  384. typedef debug_validity_phase<Polygon> debug_phase;
  385. debug_phase::apply(3);
  386. typedef has_valid_self_turns<Polygon, typename Strategy::cs_tag> has_valid_turns;
  387. std::deque<typename has_valid_turns::turn_type> turns;
  388. bool has_invalid_turns
  389. = ! has_valid_turns::apply(polygon, turns, visitor, strategy);
  390. debug_print_turns(turns.begin(), turns.end());
  391. if (has_invalid_turns)
  392. {
  393. return false;
  394. }
  395. // check if all interior rings are inside the exterior ring
  396. debug_phase::apply(4);
  397. if (! has_holes_inside::apply(polygon,
  398. turns.begin(), turns.end(),
  399. visitor,
  400. strategy))
  401. {
  402. return false;
  403. }
  404. // check whether the interior of the polygon is a connected set
  405. debug_phase::apply(5);
  406. return has_connected_interior::apply(polygon,
  407. turns.begin(),
  408. turns.end(),
  409. visitor,
  410. strategy);
  411. }
  412. }
  413. };
  414. }} // namespace detail::is_valid
  415. #endif // DOXYGEN_NO_DETAIL
  416. #ifndef DOXYGEN_NO_DISPATCH
  417. namespace dispatch
  418. {
  419. // A Polygon is always a simple geometric object provided that it is valid.
  420. //
  421. // Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1)
  422. template <typename Polygon, bool AllowEmptyMultiGeometries>
  423. struct is_valid
  424. <
  425. Polygon, polygon_tag, AllowEmptyMultiGeometries
  426. > : detail::is_valid::is_valid_polygon<Polygon>
  427. {};
  428. } // namespace dispatch
  429. #endif // DOXYGEN_NO_DISPATCH
  430. }} // namespace boost::geometry
  431. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP