gnss-sim/3rdparty/boost/geometry/algorithms/detail/is_valid/polygon.hpp

523 lines
17 KiB
C++

// 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