1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2014-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
8 // Licensed under the Boost Software License version 1.0.
9 // http://www.boost.org/users/license.html
11 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
12 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP
17 #include <boost/core/ignore_unused.hpp>
18 #include <boost/iterator/filter_iterator.hpp>
19 #include <boost/range.hpp>
21 #include <boost/geometry/core/exterior_ring.hpp>
22 #include <boost/geometry/core/interior_rings.hpp>
23 #include <boost/geometry/core/ring_type.hpp>
24 #include <boost/geometry/core/tags.hpp>
26 #include <boost/geometry/util/condition.hpp>
27 #include <boost/geometry/util/range.hpp>
29 #include <boost/geometry/geometries/box.hpp>
31 #include <boost/geometry/algorithms/validity_failure_type.hpp>
32 #include <boost/geometry/algorithms/within.hpp>
34 #include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
35 #include <boost/geometry/algorithms/detail/partition.hpp>
37 #include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
38 #include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
39 #include <boost/geometry/algorithms/detail/is_valid/polygon.hpp>
41 #include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
42 #include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
44 #include <boost/geometry/algorithms/dispatch/is_valid.hpp>
46 #include <boost/geometry/strategies/intersection.hpp>
49 namespace boost { namespace geometry
53 #ifndef DOXYGEN_NO_DETAIL
54 namespace detail { namespace is_valid
58 template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
59 class is_valid_multipolygon
62 typename boost::range_value<MultiPolygon>::type,
63 true // check only the validity of rings
67 typedef is_valid_polygon
69 typename boost::range_value<MultiPolygon>::type,
77 typename PolygonIterator,
78 typename TurnIterator,
83 bool are_polygon_interiors_disjoint(PolygonIterator polygons_first,
84 PolygonIterator polygons_beyond,
85 TurnIterator turns_first,
86 TurnIterator turns_beyond,
88 Strategy const& strategy)
90 boost::ignore_unused(visitor);
92 // collect all polygons that have crossing turns
93 std::set<signed_size_type> multi_indices;
94 for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
96 if (! tit->touch_only)
98 multi_indices.insert(tit->operations[0].seg_id.multi_index);
99 multi_indices.insert(tit->operations[1].seg_id.multi_index);
103 typedef geometry::model::box<typename point_type<MultiPolygon>::type> box_type;
104 typedef typename base::template partition_item<PolygonIterator, box_type> item_type;
106 // put polygon iterators without turns in a vector
107 std::vector<item_type> polygon_iterators;
108 signed_size_type multi_index = 0;
109 for (PolygonIterator it = polygons_first; it != polygons_beyond;
112 if (multi_indices.find(multi_index) == multi_indices.end())
114 polygon_iterators.push_back(item_type(it));
118 // prepare strategies
119 typedef typename Strategy::envelope_strategy_type envelope_strategy_type;
120 envelope_strategy_type const envelope_strategy
121 = strategy.get_envelope_strategy();
122 typedef typename Strategy::disjoint_box_box_strategy_type disjoint_box_box_strategy_type;
123 disjoint_box_box_strategy_type const disjoint_strategy
124 = strategy.get_disjoint_box_box_strategy();
126 // call partition to check if polygons are disjoint from each other
127 typename base::template item_visitor_type<Strategy> item_visitor(strategy);
131 geometry::model::box<typename point_type<MultiPolygon>::type>
132 >::apply(polygon_iterators, item_visitor,
133 typename base::template expand_box
135 envelope_strategy_type
136 >(envelope_strategy),
137 typename base::template overlaps_box
139 envelope_strategy_type,
140 disjoint_box_box_strategy_type
141 >(envelope_strategy, disjoint_strategy));
143 if (item_visitor.items_overlap)
145 return visitor.template apply<failure_intersecting_interiors>();
149 return visitor.template apply<no_failure>();
155 class has_multi_index
158 has_multi_index(signed_size_type multi_index)
159 : m_multi_index(multi_index)
162 template <typename Turn>
163 inline bool operator()(Turn const& turn) const
165 return turn.operations[0].seg_id.multi_index == m_multi_index
166 && turn.operations[1].seg_id.multi_index == m_multi_index;
170 signed_size_type const m_multi_index;
175 template <typename Predicate>
176 struct has_property_per_polygon
180 typename PolygonIterator,
181 typename TurnIterator,
182 typename VisitPolicy,
185 static inline bool apply(PolygonIterator polygons_first,
186 PolygonIterator polygons_beyond,
187 TurnIterator turns_first,
188 TurnIterator turns_beyond,
189 VisitPolicy& visitor,
190 Strategy const& strategy)
192 signed_size_type multi_index = 0;
193 for (PolygonIterator it = polygons_first; it != polygons_beyond;
196 has_multi_index index_predicate(multi_index);
198 typedef boost::filter_iterator
200 has_multi_index, TurnIterator
201 > filtered_turn_iterator;
203 filtered_turn_iterator filtered_turns_first(index_predicate,
207 filtered_turn_iterator filtered_turns_beyond(index_predicate,
211 if (! Predicate::apply(*it,
212 filtered_turns_first,
213 filtered_turns_beyond,
228 typename PolygonIterator,
229 typename TurnIterator,
230 typename VisitPolicy,
233 static inline bool have_holes_inside(PolygonIterator polygons_first,
234 PolygonIterator polygons_beyond,
235 TurnIterator turns_first,
236 TurnIterator turns_beyond,
237 VisitPolicy& visitor,
238 Strategy const& strategy)
240 return has_property_per_polygon
242 typename base::has_holes_inside
243 >::apply(polygons_first, polygons_beyond,
244 turns_first, turns_beyond, visitor, strategy);
251 typename PolygonIterator,
252 typename TurnIterator,
253 typename VisitPolicy,
256 static inline bool have_connected_interior(PolygonIterator polygons_first,
257 PolygonIterator polygons_beyond,
258 TurnIterator turns_first,
259 TurnIterator turns_beyond,
260 VisitPolicy& visitor,
261 Strategy const& strategy)
263 return has_property_per_polygon
265 typename base::has_connected_interior
266 >::apply(polygons_first, polygons_beyond,
267 turns_first, turns_beyond, visitor, strategy);
271 template <typename VisitPolicy, typename Strategy>
274 per_polygon(VisitPolicy& policy, Strategy const& strategy)
276 , m_strategy(strategy)
279 template <typename Polygon>
280 inline bool apply(Polygon const& polygon) const
282 return base::apply(polygon, m_policy, m_strategy);
285 VisitPolicy& m_policy;
286 Strategy const& m_strategy;
289 template <typename VisitPolicy, typename Strategy>
290 static inline bool apply(MultiPolygon const& multipolygon,
291 VisitPolicy& visitor,
292 Strategy const& strategy)
294 typedef debug_validity_phase<MultiPolygon> debug_phase;
296 if (BOOST_GEOMETRY_CONDITION(AllowEmptyMultiGeometries)
297 && boost::empty(multipolygon))
299 return visitor.template apply<no_failure>();
302 // check validity of all polygons ring
303 debug_phase::apply(1);
305 if (! detail::check_iterator_range
307 per_polygon<VisitPolicy, Strategy>,
308 false // do not check for empty multipolygon (done above)
309 >::apply(boost::begin(multipolygon),
310 boost::end(multipolygon),
311 per_polygon<VisitPolicy, Strategy>(visitor, strategy)))
317 // compute turns and check if all are acceptable
318 debug_phase::apply(2);
320 typedef has_valid_self_turns<MultiPolygon, typename Strategy::cs_tag> has_valid_turns;
322 std::deque<typename has_valid_turns::turn_type> turns;
323 bool has_invalid_turns =
324 ! has_valid_turns::apply(multipolygon, turns, visitor, strategy);
325 debug_print_turns(turns.begin(), turns.end());
327 if (has_invalid_turns)
333 // check if each polygon's interior rings are inside the
334 // exterior and not one inside the other
335 debug_phase::apply(3);
337 if (! have_holes_inside(boost::begin(multipolygon),
338 boost::end(multipolygon),
348 // check that each polygon's interior is connected
349 debug_phase::apply(4);
351 if (! have_connected_interior(boost::begin(multipolygon),
352 boost::end(multipolygon),
362 // check if polygon interiors are disjoint
363 debug_phase::apply(5);
364 return are_polygon_interiors_disjoint(boost::begin(multipolygon),
365 boost::end(multipolygon),
373 }} // namespace detail::is_valid
374 #endif // DOXYGEN_NO_DETAIL
378 #ifndef DOXYGEN_NO_DISPATCH
383 // Not clear what the definition is.
384 // Right now we check that each element is simple (in fact valid), and
385 // that the MultiPolygon is also valid.
387 // Reference (for validity of MultiPolygons): OGC 06-103r4 (6.1.14)
388 template <typename MultiPolygon, bool AllowEmptyMultiGeometries>
391 MultiPolygon, multi_polygon_tag, AllowEmptyMultiGeometries
392 > : detail::is_valid::is_valid_multipolygon
394 MultiPolygon, AllowEmptyMultiGeometries
399 } // namespace dispatch
400 #endif // DOXYGEN_NO_DISPATCH
403 }} // namespace boost::geometry
405 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP