123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- // Boost.Geometry (aka GGL, Generic Geometry Library)
- // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
- // Copyright (c) 2014-2023, Oracle and/or its affiliates.
- // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
- // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Licensed under the Boost Software License version 1.0.
- // http://www.boost.org/users/license.html
- #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
- #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
- #include <cstddef>
- #ifdef BOOST_GEOMETRY_TEST_DEBUG
- #include <iostream>
- #endif // BOOST_GEOMETRY_TEST_DEBUG
- #include <algorithm>
- #include <deque>
- #include <iterator>
- #include <set>
- #include <vector>
- #include <boost/core/ignore_unused.hpp>
- #include <boost/range/begin.hpp>
- #include <boost/range/end.hpp>
- #include <boost/geometry/core/assert.hpp>
- #include <boost/geometry/core/exterior_ring.hpp>
- #include <boost/geometry/core/interior_rings.hpp>
- #include <boost/geometry/core/ring_type.hpp>
- #include <boost/geometry/core/tags.hpp>
- #include <boost/geometry/util/constexpr.hpp>
- #include <boost/geometry/util/range.hpp>
- #include <boost/geometry/util/sequence.hpp>
- #include <boost/geometry/geometries/box.hpp>
- #include <boost/geometry/iterators/point_iterator.hpp>
- #include <boost/geometry/algorithms/covered_by.hpp>
- #include <boost/geometry/algorithms/disjoint.hpp>
- #include <boost/geometry/algorithms/expand.hpp>
- #include <boost/geometry/algorithms/num_interior_rings.hpp>
- #include <boost/geometry/algorithms/validity_failure_type.hpp>
- #include <boost/geometry/algorithms/detail/point_on_border.hpp>
- #include <boost/geometry/algorithms/within.hpp>
- #include <boost/geometry/algorithms/detail/partition.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/complement_graph.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/ring.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp>
- #include <boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp>
- #include <boost/geometry/algorithms/dispatch/is_valid.hpp>
- // TEMP
- #include <boost/geometry/strategies/envelope/cartesian.hpp>
- #include <boost/geometry/strategies/envelope/geographic.hpp>
- #include <boost/geometry/strategies/envelope/spherical.hpp>
- namespace boost { namespace geometry
- {
- #ifndef DOXYGEN_NO_DETAIL
- namespace detail { namespace is_valid
- {
- template <typename Polygon, bool CheckRingValidityOnly = false>
- class is_valid_polygon
- {
- protected:
- template <typename VisitPolicy, typename Strategy>
- struct is_invalid_ring
- {
- is_invalid_ring(VisitPolicy& policy, Strategy const& strategy)
- : m_policy(policy)
- , m_strategy(strategy)
- {}
- template <typename Ring>
- inline bool operator()(Ring const& ring) const
- {
- return ! detail::is_valid::is_valid_ring
- <
- Ring, false, true
- >::apply(ring, m_policy, m_strategy);
- }
- VisitPolicy& m_policy;
- Strategy const& m_strategy;
- };
- template <typename InteriorRings, typename VisitPolicy, typename Strategy>
- static bool has_valid_interior_rings(InteriorRings const& interior_rings,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- return std::none_of(boost::begin(interior_rings),
- boost::end(interior_rings),
- is_invalid_ring<VisitPolicy, Strategy>(visitor, strategy));
- }
- struct has_valid_rings
- {
- template <typename VisitPolicy, typename Strategy>
- static inline bool apply(Polygon const& polygon,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- typedef debug_validity_phase<Polygon> debug_phase;
- typedef typename ring_type<Polygon>::type ring_type;
- // check validity of exterior ring
- debug_phase::apply(1);
- if (! detail::is_valid::is_valid_ring
- <
- ring_type,
- false // do not check self intersections
- >::apply(exterior_ring(polygon), visitor, strategy))
- {
- return false;
- }
- // check validity of interior rings
- debug_phase::apply(2);
- return has_valid_interior_rings(geometry::interior_rings(polygon),
- visitor,
- strategy);
- }
- };
- // Iterator value_type is a Ring or Polygon
- template <typename Iterator, typename Box>
- struct partition_item
- {
- explicit partition_item(Iterator it)
- : m_it(it)
- , m_is_initialized(false)
- {}
- Iterator get() const
- {
- return m_it;
- }
- template <typename EnvelopeStrategy>
- Box const& get_envelope(EnvelopeStrategy const& strategy) const
- {
- if (! m_is_initialized)
- {
- m_box = geometry::return_envelope<Box>(*m_it, strategy);
- m_is_initialized = true;
- }
- return m_box;
- }
- private:
- Iterator m_it;
- mutable Box m_box;
- mutable bool m_is_initialized;
- };
- // structs for partition -- start
- template <typename Strategy>
- struct expand_box
- {
- explicit expand_box(Strategy const& strategy)
- : m_strategy(strategy)
- {}
- template <typename Box, typename Iterator>
- inline void apply(Box& total, partition_item<Iterator, Box> const& item) const
- {
- geometry::expand(total,
- item.get_envelope(m_strategy),
- m_strategy);
- }
- Strategy const& m_strategy;
- };
- template <typename Strategy>
- struct overlaps_box
- {
- explicit overlaps_box(Strategy const& strategy)
- : m_strategy(strategy)
- {}
- template <typename Box, typename Iterator>
- inline bool apply(Box const& box, partition_item<Iterator, Box> const& item) const
- {
- return ! geometry::disjoint(item.get_envelope(m_strategy),
- box,
- m_strategy);
- }
- Strategy const& m_strategy;
- };
- template <typename Strategy>
- struct item_visitor_type
- {
- bool items_overlap;
- Strategy const& m_strategy;
- explicit item_visitor_type(Strategy const& strategy)
- : items_overlap(false)
- , m_strategy(strategy)
- {}
- template <typename Iterator, typename Box>
- inline bool apply(partition_item<Iterator, Box> const& item1,
- partition_item<Iterator, Box> const& item2)
- {
- typedef util::type_sequence
- <
- geometry::de9im::static_mask<'T'>,
- geometry::de9im::static_mask<'*', 'T'>,
- geometry::de9im::static_mask<'*', '*', '*', 'T'>
- > relate_mask_t;
- if ( ! items_overlap
- && geometry::relate(*item1.get(), *item2.get(), relate_mask_t(), m_strategy) )
- {
- items_overlap = true;
- return false; // interrupt
- }
- return true;
- }
- };
- // structs for partition -- end
- template
- <
- typename RingIterator,
- typename ExteriorRing,
- typename TurnIterator,
- typename VisitPolicy,
- typename Strategy
- >
- static inline bool are_holes_inside(RingIterator rings_first,
- RingIterator rings_beyond,
- ExteriorRing const& exterior_ring,
- TurnIterator turns_first,
- TurnIterator turns_beyond,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- boost::ignore_unused(visitor);
- // collect the interior ring indices that have turns with the
- // exterior ring
- std::set<signed_size_type> ring_indices;
- for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
- {
- if (tit->operations[0].seg_id.ring_index == -1)
- {
- BOOST_GEOMETRY_ASSERT(tit->operations[1].seg_id.ring_index != -1);
- ring_indices.insert(tit->operations[1].seg_id.ring_index);
- }
- else if (tit->operations[1].seg_id.ring_index == -1)
- {
- BOOST_GEOMETRY_ASSERT(tit->operations[0].seg_id.ring_index != -1);
- ring_indices.insert(tit->operations[0].seg_id.ring_index);
- }
- }
- signed_size_type ring_index = 0;
- for (RingIterator it = rings_first; it != rings_beyond;
- ++it, ++ring_index)
- {
- // do not examine interior rings that have turns with the
- // exterior ring
- if (ring_indices.find(ring_index) == ring_indices.end()
- && ! geometry::covered_by(range::front(*it), exterior_ring, strategy))
- {
- return visitor.template apply<failure_interior_rings_outside>();
- }
- }
- // collect all rings (exterior and/or interior) that have turns
- for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit)
- {
- ring_indices.insert(tit->operations[0].seg_id.ring_index);
- ring_indices.insert(tit->operations[1].seg_id.ring_index);
- }
- typedef geometry::model::box<typename point_type<Polygon>::type> box_type;
- typedef partition_item<RingIterator, box_type> item_type;
- // put iterators for interior rings without turns in a vector
- std::vector<item_type> ring_iterators;
- ring_index = 0;
- for (RingIterator it = rings_first; it != rings_beyond;
- ++it, ++ring_index)
- {
- if (ring_indices.find(ring_index) == ring_indices.end())
- {
- ring_iterators.push_back(item_type(it));
- }
- }
- // call partition to check if interior rings are disjoint from
- // each other
- item_visitor_type<Strategy> item_visitor(strategy);
- geometry::partition
- <
- box_type
- >::apply(ring_iterators, item_visitor,
- expand_box<Strategy>(strategy),
- overlaps_box<Strategy>(strategy));
- if (item_visitor.items_overlap)
- {
- return visitor.template apply<failure_nested_interior_rings>();
- }
- else
- {
- return visitor.template apply<no_failure>();
- }
- }
- template
- <
- typename InteriorRings,
- typename ExteriorRing,
- typename TurnIterator,
- typename VisitPolicy,
- typename Strategy
- >
- static inline bool are_holes_inside(InteriorRings const& interior_rings,
- ExteriorRing const& exterior_ring,
- TurnIterator first,
- TurnIterator beyond,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- return are_holes_inside(boost::begin(interior_rings),
- boost::end(interior_rings),
- exterior_ring,
- first,
- beyond,
- visitor,
- strategy);
- }
- struct has_holes_inside
- {
- template <typename TurnIterator, typename VisitPolicy, typename Strategy>
- static inline bool apply(Polygon const& polygon,
- TurnIterator first,
- TurnIterator beyond,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- return are_holes_inside(geometry::interior_rings(polygon),
- geometry::exterior_ring(polygon),
- first,
- beyond,
- visitor,
- strategy);
- }
- };
- struct has_connected_interior
- {
- template <typename TurnIterator, typename VisitPolicy, typename Strategy>
- static inline bool apply(Polygon const& polygon,
- TurnIterator first,
- TurnIterator beyond,
- VisitPolicy& visitor,
- Strategy const& )
- {
- boost::ignore_unused(visitor);
- typedef typename std::iterator_traits
- <
- TurnIterator
- >::value_type turn_type;
- typedef complement_graph
- <
- typename turn_type::point_type,
- Strategy
- > graph;
- graph g(geometry::num_interior_rings(polygon) + 1);
- for (TurnIterator tit = first; tit != beyond; ++tit)
- {
- typename graph::vertex_handle v1 = g.add_vertex
- ( tit->operations[0].seg_id.ring_index + 1 );
- typename graph::vertex_handle v2 = g.add_vertex
- ( tit->operations[1].seg_id.ring_index + 1 );
- typename graph::vertex_handle vip = g.add_vertex(tit->point);
- g.add_edge(v1, vip);
- g.add_edge(v2, vip);
- }
- #ifdef BOOST_GEOMETRY_TEST_DEBUG
- debug_print_complement_graph(std::cout, g);
- #endif // BOOST_GEOMETRY_TEST_DEBUG
- if (g.has_cycles())
- {
- return visitor.template apply<failure_disconnected_interior>();
- }
- else
- {
- return visitor.template apply<no_failure>();
- }
- }
- };
- public:
- template <typename VisitPolicy, typename Strategy>
- static inline bool apply(Polygon const& polygon,
- VisitPolicy& visitor,
- Strategy const& strategy)
- {
- if (! has_valid_rings::apply(polygon, visitor, strategy))
- {
- return false;
- }
- if BOOST_GEOMETRY_CONSTEXPR (CheckRingValidityOnly)
- {
- return true;
- }
- else // else prevents unreachable code warning
- {
- // compute turns and check if all are acceptable
- typedef debug_validity_phase<Polygon> debug_phase;
- debug_phase::apply(3);
- typedef has_valid_self_turns<Polygon, typename Strategy::cs_tag> has_valid_turns;
- std::deque<typename has_valid_turns::turn_type> turns;
- bool has_invalid_turns
- = ! has_valid_turns::apply(polygon, turns, visitor, strategy);
- debug_print_turns(turns.begin(), turns.end());
- if (has_invalid_turns)
- {
- return false;
- }
- // check if all interior rings are inside the exterior ring
- debug_phase::apply(4);
- if (! has_holes_inside::apply(polygon,
- turns.begin(), turns.end(),
- visitor,
- strategy))
- {
- return false;
- }
- // check whether the interior of the polygon is a connected set
- debug_phase::apply(5);
- return has_connected_interior::apply(polygon,
- turns.begin(),
- turns.end(),
- visitor,
- strategy);
- }
- }
- };
- }} // namespace detail::is_valid
- #endif // DOXYGEN_NO_DETAIL
- #ifndef DOXYGEN_NO_DISPATCH
- namespace dispatch
- {
- // A Polygon is always a simple geometric object provided that it is valid.
- //
- // Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1)
- template <typename Polygon, bool AllowEmptyMultiGeometries>
- struct is_valid
- <
- Polygon, polygon_tag, AllowEmptyMultiGeometries
- > : detail::is_valid::is_valid_polygon<Polygon>
- {};
- } // namespace dispatch
- #endif // DOXYGEN_NO_DISPATCH
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP
|