add boost on mac
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/geometry/core/closure.hpp>
|
||||
#include <boost/geometry/core/exception.hpp>
|
||||
#include <boost/geometry/algorithms/area.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/convert_ring.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template
|
||||
<
|
||||
typename GeometryOut,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename RingCollection
|
||||
>
|
||||
inline void convert_and_add(GeometryOut& result,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RingCollection const& collection,
|
||||
ring_identifier id,
|
||||
bool reversed, bool append)
|
||||
{
|
||||
typedef typename geometry::tag<Geometry1>::type tag1;
|
||||
typedef typename geometry::tag<Geometry2>::type tag2;
|
||||
typedef typename geometry::tag<GeometryOut>::type tag_out;
|
||||
|
||||
if (id.source_index == 0)
|
||||
{
|
||||
convert_ring<tag_out>::apply(result,
|
||||
get_ring<tag1>::apply(id, geometry1),
|
||||
append, reversed);
|
||||
}
|
||||
else if (id.source_index == 1)
|
||||
{
|
||||
convert_ring<tag_out>::apply(result,
|
||||
get_ring<tag2>::apply(id, geometry2),
|
||||
append, reversed);
|
||||
}
|
||||
else if (id.source_index == 2)
|
||||
{
|
||||
convert_ring<tag_out>::apply(result,
|
||||
get_ring<void>::apply(id, collection),
|
||||
append, reversed);
|
||||
}
|
||||
}
|
||||
|
||||
enum add_rings_error_handling
|
||||
{
|
||||
add_rings_ignore_unordered,
|
||||
add_rings_add_unordered,
|
||||
add_rings_throw_if_reversed
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename GeometryOut,
|
||||
typename SelectionMap,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename RingCollection,
|
||||
typename OutputIterator,
|
||||
typename AreaStrategy
|
||||
>
|
||||
inline OutputIterator add_rings(SelectionMap const& map,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RingCollection const& collection,
|
||||
OutputIterator out,
|
||||
AreaStrategy const& area_strategy,
|
||||
add_rings_error_handling error_handling = add_rings_ignore_unordered)
|
||||
{
|
||||
typedef typename SelectionMap::const_iterator iterator;
|
||||
typedef typename AreaStrategy::template result_type
|
||||
<
|
||||
GeometryOut
|
||||
>::type area_type;
|
||||
|
||||
area_type const zero = 0;
|
||||
std::size_t const min_num_points = core_detail::closure::minimum_ring_size
|
||||
<
|
||||
geometry::closure
|
||||
<
|
||||
typename boost::range_value
|
||||
<
|
||||
RingCollection const
|
||||
>::type
|
||||
>::value
|
||||
>::value;
|
||||
|
||||
|
||||
for (iterator it = boost::begin(map);
|
||||
it != boost::end(map);
|
||||
++it)
|
||||
{
|
||||
if (! it->second.discarded
|
||||
&& it->second.parent.source_index == -1)
|
||||
{
|
||||
GeometryOut result;
|
||||
convert_and_add(result, geometry1, geometry2, collection,
|
||||
it->first, it->second.reversed, false);
|
||||
|
||||
// Add children
|
||||
for (typename std::vector<ring_identifier>::const_iterator child_it
|
||||
= it->second.children.begin();
|
||||
child_it != it->second.children.end();
|
||||
++child_it)
|
||||
{
|
||||
iterator mit = map.find(*child_it);
|
||||
if (mit != map.end()
|
||||
&& ! mit->second.discarded)
|
||||
{
|
||||
convert_and_add(result, geometry1, geometry2, collection,
|
||||
*child_it, mit->second.reversed, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Only add rings if they satisfy minimal requirements.
|
||||
// This cannot be done earlier (during traversal), not
|
||||
// everything is figured out yet (sum of positive/negative rings)
|
||||
if (geometry::num_points(result) >= min_num_points)
|
||||
{
|
||||
area_type const area = geometry::area(result, area_strategy);
|
||||
// Ignore if area is 0
|
||||
if (! math::equals(area, zero))
|
||||
{
|
||||
if (error_handling == add_rings_add_unordered
|
||||
|| area > zero)
|
||||
{
|
||||
*out++ = result;
|
||||
}
|
||||
else if (error_handling == add_rings_throw_if_reversed)
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(invalid_output_exception());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename GeometryOut,
|
||||
typename SelectionMap,
|
||||
typename Geometry,
|
||||
typename RingCollection,
|
||||
typename OutputIterator,
|
||||
typename AreaStrategy
|
||||
>
|
||||
inline OutputIterator add_rings(SelectionMap const& map,
|
||||
Geometry const& geometry,
|
||||
RingCollection const& collection,
|
||||
OutputIterator out,
|
||||
AreaStrategy const& area_strategy)
|
||||
{
|
||||
Geometry empty;
|
||||
return add_rings<GeometryOut>(map, geometry, empty, collection, out, area_strategy);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP
|
||||
@@ -0,0 +1,70 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2018.
|
||||
// Modifications copyright (c) 2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/append.hpp>
|
||||
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <typename Range, typename Point>
|
||||
inline void append_with_duplicates(Range& range, Point const& point)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION
|
||||
std::cout << " add: ("
|
||||
<< geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")"
|
||||
<< std::endl;
|
||||
#endif
|
||||
geometry::append(range, point);
|
||||
}
|
||||
|
||||
template <typename Range, typename Point, typename EqPPStrategy>
|
||||
inline void append_no_duplicates(Range& range, Point const& point,
|
||||
EqPPStrategy const& strategy)
|
||||
{
|
||||
if ( boost::empty(range)
|
||||
|| ! geometry::detail::equals::equals_point_point(geometry::range::back(range),
|
||||
point,
|
||||
strategy) )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION
|
||||
std::cout << " add: ("
|
||||
<< geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")"
|
||||
<< std::endl;
|
||||
#endif
|
||||
geometry::append(range, point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP
|
||||
@@ -0,0 +1,216 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2014, 2017, 2018.
|
||||
// Modifications copyright (c) 2014-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/append.hpp>
|
||||
#include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp>
|
||||
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
||||
|
||||
#include <boost/geometry/core/closure.hpp>
|
||||
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
// TODO: move this / rename this
|
||||
template <typename Point1, typename Point2, typename EqualsStrategy, typename RobustPolicy>
|
||||
inline bool points_equal_or_close(Point1 const& point1,
|
||||
Point2 const& point2,
|
||||
EqualsStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
{
|
||||
if (detail::equals::equals_point_point(point1, point2, strategy))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (BOOST_GEOMETRY_CONDITION(! RobustPolicy::enabled))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try using specified robust policy
|
||||
typedef typename geometry::robust_point_type
|
||||
<
|
||||
Point1,
|
||||
RobustPolicy
|
||||
>::type robust_point_type;
|
||||
|
||||
robust_point_type point1_rob, point2_rob;
|
||||
geometry::recalculate(point1_rob, point1, robust_policy);
|
||||
geometry::recalculate(point2_rob, point2, robust_policy);
|
||||
|
||||
// Only if this is the case the same strategy can be used.
|
||||
BOOST_STATIC_ASSERT((boost::is_same
|
||||
<
|
||||
typename geometry::cs_tag<Point1>::type,
|
||||
typename geometry::cs_tag<robust_point_type>::type
|
||||
>::value));
|
||||
|
||||
return detail::equals::equals_point_point(point1_rob, point2_rob, strategy);
|
||||
}
|
||||
|
||||
|
||||
template <typename Range, typename Point, typename SideStrategy, typename RobustPolicy>
|
||||
inline void append_no_dups_or_spikes(Range& range, Point const& point,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION
|
||||
std::cout << " add: ("
|
||||
<< geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")"
|
||||
<< std::endl;
|
||||
#endif
|
||||
// The code below this condition checks all spikes/dups
|
||||
// for geometries >= 3 points.
|
||||
// So we have to check the first potential duplicate differently
|
||||
if ( boost::size(range) == 1
|
||||
&& points_equal_or_close(*(boost::begin(range)), point,
|
||||
strategy.get_equals_point_point_strategy(),
|
||||
robust_policy) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
traits::push_back<Range>::apply(range, point);
|
||||
|
||||
// If a point is equal, or forming a spike, remove the pen-ultimate point
|
||||
// because this one caused the spike.
|
||||
// If so, the now-new-pen-ultimate point can again cause a spike
|
||||
// (possibly at a corner). So keep doing this.
|
||||
// Besides spikes it will also avoid adding duplicates.
|
||||
while(boost::size(range) >= 3
|
||||
&& point_is_spike_or_equal(point,
|
||||
*(boost::end(range) - 3),
|
||||
*(boost::end(range) - 2),
|
||||
strategy,
|
||||
robust_policy))
|
||||
{
|
||||
// Use the Concept/traits, so resize and append again
|
||||
traits::resize<Range>::apply(range, boost::size(range) - 2);
|
||||
traits::push_back<Range>::apply(range, point);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Range, typename Point, typename SideStrategy, typename RobustPolicy>
|
||||
inline void append_no_collinear(Range& range, Point const& point,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
{
|
||||
// Stricter version, not allowing any point in a linear row
|
||||
// (spike, continuation or same point)
|
||||
|
||||
// The code below this condition checks all spikes/dups
|
||||
// for geometries >= 3 points.
|
||||
// So we have to check the first potential duplicate differently
|
||||
if ( boost::size(range) == 1
|
||||
&& points_equal_or_close(*(boost::begin(range)), point,
|
||||
strategy.get_equals_point_point_strategy(),
|
||||
robust_policy) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
traits::push_back<Range>::apply(range, point);
|
||||
|
||||
// If a point is equal, or forming a spike, remove the pen-ultimate point
|
||||
// because this one caused the spike.
|
||||
// If so, the now-new-pen-ultimate point can again cause a spike
|
||||
// (possibly at a corner). So keep doing this.
|
||||
// Besides spikes it will also avoid adding duplicates.
|
||||
while(boost::size(range) >= 3
|
||||
&& point_is_collinear(point,
|
||||
*(boost::end(range) - 3),
|
||||
*(boost::end(range) - 2),
|
||||
strategy,
|
||||
robust_policy))
|
||||
{
|
||||
// Use the Concept/traits, so resize and append again
|
||||
traits::resize<Range>::apply(range, boost::size(range) - 2);
|
||||
traits::push_back<Range>::apply(range, point);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Range, typename SideStrategy, typename RobustPolicy>
|
||||
inline void clean_closing_dups_and_spikes(Range& range,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
{
|
||||
std::size_t const minsize
|
||||
= core_detail::closure::minimum_ring_size
|
||||
<
|
||||
geometry::closure<Range>::value
|
||||
>::value;
|
||||
|
||||
if (boost::size(range) <= minsize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
typedef typename boost::range_iterator<Range>::type iterator_type;
|
||||
static bool const closed = geometry::closure<Range>::value == geometry::closed;
|
||||
|
||||
// TODO: the following algorithm could be rewritten to first look for spikes
|
||||
// and then erase some number of points from the beginning of the Range
|
||||
|
||||
bool found = false;
|
||||
do
|
||||
{
|
||||
found = false;
|
||||
iterator_type first = boost::begin(range);
|
||||
iterator_type second = first + 1;
|
||||
iterator_type ultimate = boost::end(range) - 1;
|
||||
if (BOOST_GEOMETRY_CONDITION(closed))
|
||||
{
|
||||
ultimate--;
|
||||
}
|
||||
|
||||
// Check if closing point is a spike (this is so if the second point is
|
||||
// considered as collinear w.r.t. the last segment)
|
||||
if (point_is_collinear(*second, *ultimate, *first, strategy, robust_policy))
|
||||
{
|
||||
range::erase(range, first);
|
||||
if (BOOST_GEOMETRY_CONDITION(closed))
|
||||
{
|
||||
// Remove closing last point
|
||||
range::resize(range, boost::size(range) - 1);
|
||||
// Add new closing point
|
||||
range::push_back(range, range::front(range));
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
} while(found && boost::size(range) > minsize);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP
|
||||
@@ -0,0 +1,426 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017, 2018.
|
||||
// Modifications copyright (c) 2017-2018 Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/envelope.hpp>
|
||||
#include <boost/geometry/algorithms/expand.hpp>
|
||||
#include <boost/geometry/algorithms/detail/partition.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp>
|
||||
#include <boost/geometry/algorithms/covered_by.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Item,
|
||||
typename InnerGeometry,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RingCollection,
|
||||
typename Strategy
|
||||
>
|
||||
static inline bool within_selected_input(Item const& item2,
|
||||
InnerGeometry const& inner_geometry,
|
||||
ring_identifier const& outer_id,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RingCollection const& collection,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::tag<Geometry1>::type tag1;
|
||||
typedef typename geometry::tag<Geometry2>::type tag2;
|
||||
|
||||
// NOTE: range_in_geometry first checks the item2.point and then
|
||||
// if this point is on boundary it checks points of inner_geometry
|
||||
// ring until a point inside/outside other geometry ring is found
|
||||
switch (outer_id.source_index)
|
||||
{
|
||||
// covered_by
|
||||
case 0 :
|
||||
return range_in_geometry(item2.point, inner_geometry,
|
||||
get_ring<tag1>::apply(outer_id, geometry1), strategy) >= 0;
|
||||
case 1 :
|
||||
return range_in_geometry(item2.point, inner_geometry,
|
||||
get_ring<tag2>::apply(outer_id, geometry2), strategy) >= 0;
|
||||
case 2 :
|
||||
return range_in_geometry(item2.point, inner_geometry,
|
||||
get_ring<void>::apply(outer_id, collection), strategy) >= 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Item,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RingCollection,
|
||||
typename Strategy
|
||||
>
|
||||
static inline bool within_selected_input(Item const& item2,
|
||||
ring_identifier const& inner_id, ring_identifier const& outer_id,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RingCollection const& collection,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::tag<Geometry1>::type tag1;
|
||||
typedef typename geometry::tag<Geometry2>::type tag2;
|
||||
|
||||
switch (inner_id.source_index)
|
||||
{
|
||||
case 0 :
|
||||
return within_selected_input(item2,
|
||||
get_ring<tag1>::apply(inner_id, geometry1),
|
||||
outer_id, geometry1, geometry2, collection, strategy);
|
||||
case 1 :
|
||||
return within_selected_input(item2,
|
||||
get_ring<tag2>::apply(inner_id, geometry2),
|
||||
outer_id, geometry1, geometry2, collection, strategy);
|
||||
case 2 :
|
||||
return within_selected_input(item2,
|
||||
get_ring<void>::apply(inner_id, collection),
|
||||
outer_id, geometry1, geometry2, collection, strategy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename Point, typename AreaType>
|
||||
struct ring_info_helper
|
||||
{
|
||||
ring_identifier id;
|
||||
AreaType real_area;
|
||||
AreaType abs_area;
|
||||
model::box<Point> envelope;
|
||||
|
||||
inline ring_info_helper()
|
||||
: real_area(0), abs_area(0)
|
||||
{}
|
||||
|
||||
inline ring_info_helper(ring_identifier i, AreaType const& a)
|
||||
: id(i), real_area(a), abs_area(geometry::math::abs(a))
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct ring_info_helper_get_box
|
||||
{
|
||||
template <typename Box, typename InputItem>
|
||||
static inline void apply(Box& total, InputItem const& item)
|
||||
{
|
||||
geometry::expand(total, item.envelope);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DisjointBoxBoxStrategy>
|
||||
struct ring_info_helper_ovelaps_box
|
||||
{
|
||||
template <typename Box, typename InputItem>
|
||||
static inline bool apply(Box const& box, InputItem const& item)
|
||||
{
|
||||
return ! geometry::detail::disjoint::disjoint_box_box(
|
||||
box, item.envelope, DisjointBoxBoxStrategy());
|
||||
}
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename Collection,
|
||||
typename RingMap,
|
||||
typename Strategy
|
||||
>
|
||||
struct assign_visitor
|
||||
{
|
||||
typedef typename RingMap::mapped_type ring_info_type;
|
||||
|
||||
Geometry1 const& m_geometry1;
|
||||
Geometry2 const& m_geometry2;
|
||||
Collection const& m_collection;
|
||||
RingMap& m_ring_map;
|
||||
Strategy const& m_strategy;
|
||||
bool m_check_for_orientation;
|
||||
|
||||
inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c,
|
||||
RingMap& map, Strategy const& strategy, bool check)
|
||||
: m_geometry1(g1)
|
||||
, m_geometry2(g2)
|
||||
, m_collection(c)
|
||||
, m_ring_map(map)
|
||||
, m_strategy(strategy)
|
||||
, m_check_for_orientation(check)
|
||||
{}
|
||||
|
||||
template <typename Item>
|
||||
inline bool apply(Item const& outer, Item const& inner, bool first = true)
|
||||
{
|
||||
if (first && outer.abs_area < inner.abs_area)
|
||||
{
|
||||
// Apply with reversed arguments
|
||||
apply(inner, outer, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_check_for_orientation
|
||||
|| (math::larger(outer.real_area, 0)
|
||||
&& math::smaller(inner.real_area, 0)))
|
||||
{
|
||||
ring_info_type& inner_in_map = m_ring_map[inner.id];
|
||||
|
||||
if (geometry::covered_by(inner_in_map.point, outer.envelope)
|
||||
&& within_selected_input(inner_in_map, inner.id, outer.id,
|
||||
m_geometry1, m_geometry2, m_collection,
|
||||
m_strategy)
|
||||
)
|
||||
{
|
||||
// Assign a parent if there was no earlier parent, or the newly
|
||||
// found parent is smaller than the previous one
|
||||
if (inner_in_map.parent.source_index == -1
|
||||
|| outer.abs_area < inner_in_map.parent_area)
|
||||
{
|
||||
inner_in_map.parent = outer.id;
|
||||
inner_in_map.parent_area = outer.abs_area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RingCollection,
|
||||
typename RingMap,
|
||||
typename Strategy
|
||||
>
|
||||
inline void assign_parents(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
RingCollection const& collection,
|
||||
RingMap& ring_map,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
static bool const is_difference = OverlayType == overlay_difference;
|
||||
static bool const is_buffer = OverlayType == overlay_buffer;
|
||||
static bool const is_dissolve = OverlayType == overlay_dissolve;
|
||||
static bool const check_for_orientation = is_buffer || is_dissolve;
|
||||
|
||||
typedef typename geometry::tag<Geometry1>::type tag1;
|
||||
typedef typename geometry::tag<Geometry2>::type tag2;
|
||||
|
||||
typedef typename RingMap::mapped_type ring_info_type;
|
||||
typedef typename ring_info_type::point_type point_type;
|
||||
typedef model::box<point_type> box_type;
|
||||
typedef typename Strategy::template area_strategy
|
||||
<
|
||||
point_type
|
||||
>::type::template result_type<point_type>::type area_result_type;
|
||||
|
||||
typedef typename RingMap::iterator map_iterator_type;
|
||||
|
||||
{
|
||||
typedef ring_info_helper<point_type, area_result_type> helper;
|
||||
typedef std::vector<helper> vector_type;
|
||||
typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type;
|
||||
|
||||
std::size_t count_total = ring_map.size();
|
||||
std::size_t count_positive = 0;
|
||||
std::size_t index_positive = 0; // only used if count_positive>0
|
||||
std::size_t index = 0;
|
||||
|
||||
// Copy to vector (with new approach this might be obsolete as well, using the map directly)
|
||||
vector_type vector(count_total);
|
||||
|
||||
for (map_iterator_type it = boost::begin(ring_map);
|
||||
it != boost::end(ring_map); ++it, ++index)
|
||||
{
|
||||
vector[index] = helper(it->first, it->second.get_area());
|
||||
helper& item = vector[index];
|
||||
switch(it->first.source_index)
|
||||
{
|
||||
case 0 :
|
||||
geometry::envelope(get_ring<tag1>::apply(it->first, geometry1),
|
||||
item.envelope, strategy.get_envelope_strategy());
|
||||
break;
|
||||
case 1 :
|
||||
geometry::envelope(get_ring<tag2>::apply(it->first, geometry2),
|
||||
item.envelope, strategy.get_envelope_strategy());
|
||||
break;
|
||||
case 2 :
|
||||
geometry::envelope(get_ring<void>::apply(it->first, collection),
|
||||
item.envelope, strategy.get_envelope_strategy());
|
||||
break;
|
||||
}
|
||||
|
||||
// Expand envelope slightly
|
||||
expand_by_epsilon(item.envelope);
|
||||
|
||||
if (item.real_area > 0)
|
||||
{
|
||||
count_positive++;
|
||||
index_positive = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (! check_for_orientation)
|
||||
{
|
||||
if (count_positive == count_total)
|
||||
{
|
||||
// Optimization for only positive rings
|
||||
// -> no assignment of parents or reversal necessary, ready here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (count_positive == 1 && ! is_difference && ! is_dissolve)
|
||||
{
|
||||
// Optimization for one outer ring
|
||||
// -> assign this as parent to all others (all interior rings)
|
||||
// In unions, this is probably the most occuring case and gives
|
||||
// a dramatic improvement (factor 5 for star_comb testcase)
|
||||
// In difference or other cases where interior rings might be
|
||||
// located outside the outer ring, this cannot be done
|
||||
ring_identifier id_of_positive = vector[index_positive].id;
|
||||
ring_info_type& outer = ring_map[id_of_positive];
|
||||
index = 0;
|
||||
for (vector_iterator_type it = boost::begin(vector);
|
||||
it != boost::end(vector); ++it, ++index)
|
||||
{
|
||||
if (index != index_positive)
|
||||
{
|
||||
ring_info_type& inner = ring_map[it->id];
|
||||
inner.parent = id_of_positive;
|
||||
outer.children.push_back(it->id);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assign_visitor
|
||||
<
|
||||
Geometry1, Geometry2,
|
||||
RingCollection, RingMap,
|
||||
Strategy
|
||||
> visitor(geometry1, geometry2, collection, ring_map, strategy, check_for_orientation);
|
||||
|
||||
typedef ring_info_helper_ovelaps_box
|
||||
<
|
||||
typename Strategy::disjoint_box_box_strategy_type
|
||||
> overlaps_box_type;
|
||||
|
||||
geometry::partition
|
||||
<
|
||||
box_type
|
||||
>::apply(vector, visitor, ring_info_helper_get_box(),
|
||||
overlaps_box_type());
|
||||
}
|
||||
|
||||
if (check_for_orientation)
|
||||
{
|
||||
for (map_iterator_type it = boost::begin(ring_map);
|
||||
it != boost::end(ring_map); ++it)
|
||||
{
|
||||
ring_info_type& info = it->second;
|
||||
if (geometry::math::equals(info.get_area(), 0))
|
||||
{
|
||||
info.discarded = true;
|
||||
}
|
||||
else if (info.parent.source_index >= 0)
|
||||
{
|
||||
const ring_info_type& parent = ring_map[info.parent];
|
||||
bool const pos = math::larger(info.get_area(), 0);
|
||||
bool const parent_pos = math::larger(parent.area, 0);
|
||||
|
||||
bool const double_neg = is_dissolve && ! pos && ! parent_pos;
|
||||
|
||||
if ((pos && parent_pos) || double_neg)
|
||||
{
|
||||
// Discard positive inner ring with positive parent
|
||||
// Also, for some cases (dissolve), negative inner ring
|
||||
// with negative parent should be discarded
|
||||
info.discarded = true;
|
||||
}
|
||||
|
||||
if (pos || info.discarded)
|
||||
{
|
||||
// Remove parent ID from any positive or discarded inner rings
|
||||
info.parent.source_index = -1;
|
||||
}
|
||||
}
|
||||
else if (info.parent.source_index < 0
|
||||
&& math::smaller(info.get_area(), 0))
|
||||
{
|
||||
// Reverse negative ring without parent
|
||||
info.reversed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign childlist
|
||||
for (map_iterator_type it = boost::begin(ring_map);
|
||||
it != boost::end(ring_map); ++it)
|
||||
{
|
||||
if (it->second.parent.source_index >= 0)
|
||||
{
|
||||
ring_map[it->second.parent].children.push_back(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Version for one geometry (called by buffer/dissolve)
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Geometry,
|
||||
typename RingCollection,
|
||||
typename RingMap,
|
||||
typename Strategy
|
||||
>
|
||||
inline void assign_parents(Geometry const& geometry,
|
||||
RingCollection const& collection,
|
||||
RingMap& ring_map,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
// Call it with an empty geometry as second geometry (source_id == 1)
|
||||
// (ring_map should be empty for source_id==1)
|
||||
Geometry empty;
|
||||
assign_parents<OverlayType>(geometry, empty,
|
||||
collection, ring_map, strategy);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
|
||||
@@ -0,0 +1,215 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017, Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/has_self_intersections.hpp>
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT)
|
||||
# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
||||
# include <boost/geometry/io/wkt/wkt.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <typename Turns>
|
||||
inline void clear_visit_info(Turns& turns)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type tp_type;
|
||||
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
for (typename boost::range_iterator
|
||||
<
|
||||
typename tp_type::container_type
|
||||
>::type op_it = boost::begin(it->operations);
|
||||
op_it != boost::end(it->operations);
|
||||
++op_it)
|
||||
{
|
||||
op_it->visited.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct backtrack_state
|
||||
{
|
||||
bool m_good;
|
||||
|
||||
inline backtrack_state() : m_good(true) {}
|
||||
inline void reset() { m_good = true; }
|
||||
inline bool good() const { return m_good; }
|
||||
};
|
||||
|
||||
|
||||
enum traverse_error_type
|
||||
{
|
||||
traverse_error_none,
|
||||
traverse_error_no_next_ip_at_start,
|
||||
traverse_error_no_next_ip,
|
||||
traverse_error_dead_end_at_start,
|
||||
traverse_error_dead_end,
|
||||
traverse_error_visit_again,
|
||||
traverse_error_endless_loop
|
||||
};
|
||||
|
||||
inline std::string traverse_error_string(traverse_error_type error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case traverse_error_none : return "";
|
||||
case traverse_error_no_next_ip_at_start : return "No next IP at start";
|
||||
case traverse_error_no_next_ip : return "No next IP";
|
||||
case traverse_error_dead_end_at_start : return "Dead end at start";
|
||||
case traverse_error_dead_end : return "Dead end";
|
||||
case traverse_error_visit_again : return "Visit again";
|
||||
case traverse_error_endless_loop : return "Endless loop";
|
||||
default : return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry1,
|
||||
typename Geometry2
|
||||
>
|
||||
class backtrack_check_self_intersections
|
||||
{
|
||||
struct state : public backtrack_state
|
||||
{
|
||||
bool m_checked;
|
||||
inline state()
|
||||
: m_checked(true)
|
||||
{}
|
||||
};
|
||||
public :
|
||||
typedef state state_type;
|
||||
|
||||
template
|
||||
<
|
||||
typename Operation,
|
||||
typename Rings, typename Ring, typename Turns,
|
||||
typename Strategy,
|
||||
typename RobustPolicy,
|
||||
typename Visitor
|
||||
>
|
||||
static inline void apply(std::size_t size_at_start,
|
||||
Rings& rings,
|
||||
Ring& ring,
|
||||
Turns& turns,
|
||||
typename boost::range_value<Turns>::type const& turn,
|
||||
Operation& operation,
|
||||
traverse_error_type traverse_error,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
state_type& state,
|
||||
Visitor& visitor)
|
||||
{
|
||||
visitor.visit_traverse_reject(turns, turn, operation, traverse_error);
|
||||
|
||||
state.m_good = false;
|
||||
|
||||
// Check self-intersections and throw exception if appropriate
|
||||
if (! state.m_checked)
|
||||
{
|
||||
state.m_checked = true;
|
||||
has_self_intersections(geometry1, strategy, robust_policy);
|
||||
has_self_intersections(geometry2, strategy, robust_policy);
|
||||
}
|
||||
|
||||
// Make bad output clean
|
||||
rings.resize(size_at_start);
|
||||
geometry::traits::clear<typename boost::range_value<Rings>::type>::apply(ring);
|
||||
|
||||
// Reject this as a starting point
|
||||
operation.visited.set_rejected();
|
||||
|
||||
// And clear all visit info
|
||||
clear_visit_info(turns);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT
|
||||
template
|
||||
<
|
||||
typename Geometry1,
|
||||
typename Geometry2
|
||||
>
|
||||
class backtrack_debug
|
||||
{
|
||||
public :
|
||||
typedef backtrack_state state_type;
|
||||
|
||||
template <typename Operation, typename Rings, typename Turns>
|
||||
static inline void apply(std::size_t size_at_start,
|
||||
Rings& rings, typename boost::range_value<Rings>::type& ring,
|
||||
Turns& turns, Operation& operation,
|
||||
std::string const& reason,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
state_type& state
|
||||
)
|
||||
{
|
||||
std::cout << " REJECT " << reason << std::endl;
|
||||
|
||||
state.m_good = false;
|
||||
|
||||
rings.resize(size_at_start);
|
||||
ring.clear();
|
||||
operation.visited.set_rejected();
|
||||
clear_visit_info(turns);
|
||||
|
||||
int c = 0;
|
||||
for (int i = 0; i < turns.size(); i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
if (turns[i].operations[j].visited.rejected())
|
||||
{
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "BACKTRACK (" << reason << " )"
|
||||
<< " " << c << " of " << turns.size() << " rejected"
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< geometry::wkt(geometry1) << std::endl
|
||||
<< geometry::wkt(geometry2) << std::endl;
|
||||
}
|
||||
};
|
||||
#endif // BOOST_GEOMETRY_OVERLAY_REPORT_WKT
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP
|
||||
@@ -0,0 +1,180 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2018.
|
||||
// Modifications copyright (c) 2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
#include <iostream>
|
||||
#endif // BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/begin.hpp>
|
||||
#include <boost/range/end.hpp>
|
||||
#include <boost/range/value_type.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template<typename Turn>
|
||||
struct meta_turn
|
||||
{
|
||||
int index;
|
||||
Turn const* turn;
|
||||
bool handled[2];
|
||||
|
||||
inline meta_turn(int i, Turn const& t)
|
||||
: index(i), turn(&t)
|
||||
{
|
||||
handled[0] = false;
|
||||
handled[1] = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename MetaTurn>
|
||||
inline void display(MetaTurn const& meta_turn, const char* reason = "")
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
std::cout << meta_turn.index
|
||||
<< "\tMethods: " << method_char(meta_turn.turn->method)
|
||||
<< " operations: " << operation_char(meta_turn.turn->operations[0].operation)
|
||||
<< operation_char(meta_turn.turn->operations[1].operation)
|
||||
<< " travels to " << meta_turn.turn->operations[0].enriched.travels_to_ip_index
|
||||
<< " and " << meta_turn.turn->operations[1].enriched.travels_to_ip_index
|
||||
//<< " -> " << op_index
|
||||
<< " " << reason
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template <typename MetaTurns, typename MetaTurn>
|
||||
inline void check_detailed(MetaTurns& meta_turns, MetaTurn const& meta_turn,
|
||||
int op_index, int cycle, int start, operation_type for_operation,
|
||||
bool& error)
|
||||
{
|
||||
display(meta_turn);
|
||||
int const ip_index = meta_turn.turn->operations[op_index].enriched.travels_to_ip_index;
|
||||
if (ip_index >= 0)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (ip_index == start)
|
||||
{
|
||||
display(meta_turns[ip_index], " FINISH");
|
||||
return;
|
||||
}
|
||||
|
||||
// check on continuing, or on same-operation-on-same-geometry
|
||||
if (! meta_turns[ip_index].handled[op_index]
|
||||
&& (meta_turns[ip_index].turn->operations[op_index].operation == operation_continue
|
||||
|| meta_turns[ip_index].turn->operations[op_index].operation == for_operation)
|
||||
)
|
||||
{
|
||||
meta_turns[ip_index].handled[op_index] = true;
|
||||
check_detailed(meta_turns, meta_turns[ip_index], op_index, cycle, start, for_operation, error);
|
||||
found = true;
|
||||
}
|
||||
// check on other geometry
|
||||
if (! found)
|
||||
{
|
||||
int const other_index = 1 - op_index;
|
||||
if (! meta_turns[ip_index].handled[other_index]
|
||||
&& meta_turns[ip_index].turn->operations[other_index].operation == for_operation)
|
||||
{
|
||||
meta_turns[ip_index].handled[other_index] = true;
|
||||
check_detailed(meta_turns, meta_turns[ip_index], other_index, cycle, start, for_operation, error);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! found)
|
||||
{
|
||||
display(meta_turns[ip_index], " STOP");
|
||||
error = true;
|
||||
#ifndef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
//std::cout << " STOP";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename TurnPoints>
|
||||
inline bool check_graph(TurnPoints& turn_points, operation_type for_operation)
|
||||
{
|
||||
typedef typename boost::range_value<TurnPoints>::type turn_point_type;
|
||||
|
||||
bool error = false;
|
||||
int index = 0;
|
||||
|
||||
std::vector<meta_turn<turn_point_type> > meta_turns;
|
||||
for (typename boost::range_iterator<TurnPoints const>::type
|
||||
it = boost::begin(turn_points);
|
||||
it != boost::end(turn_points);
|
||||
++it, ++index)
|
||||
{
|
||||
meta_turns.push_back(meta_turn<turn_point_type>(index, *it));
|
||||
}
|
||||
|
||||
int cycle = 0;
|
||||
for (typename boost::range_iterator<std::vector<meta_turn<turn_point_type> > > ::type
|
||||
it = boost::begin(meta_turns);
|
||||
it != boost::end(meta_turns);
|
||||
++it)
|
||||
{
|
||||
if (! (it->turn->blocked() || it->turn->discarded))
|
||||
{
|
||||
for (int i = 0 ; i < 2; i++)
|
||||
{
|
||||
if (! it->handled[i]
|
||||
&& it->turn->operations[i].operation == for_operation)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
std::cout << "CYCLE " << cycle << std::endl;
|
||||
#endif
|
||||
it->handled[i] = true;
|
||||
check_detailed(meta_turns, *it, i, cycle++, it->index, for_operation, error);
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
std::cout <<" END CYCLE " << it->index << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP
|
||||
@@ -0,0 +1,263 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2015, 2018.
|
||||
// Modifications copyright (c) 2015-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/clear.hpp>
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp>
|
||||
|
||||
#include <boost/geometry/util/select_coordinate_type.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
|
||||
#include <boost/geometry/strategies/cartesian/point_in_point.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
namespace strategy { namespace intersection
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Strategy: line clipping algorithm after Liang Barsky
|
||||
\ingroup overlay
|
||||
\details The Liang-Barsky line clipping algorithm clips a line with a clipping box.
|
||||
It is slightly adapted in the sense that it returns which points are clipped
|
||||
\tparam B input box type of clipping box
|
||||
\tparam P input/output point-type of segments to be clipped
|
||||
\note The algorithm is currently only implemented for 2D Cartesian points
|
||||
\note Though it is implemented in namespace strategy, and theoretically another
|
||||
strategy could be used, it is not (yet) updated to the general strategy concepts,
|
||||
and not (yet) splitted into a file in folder strategies
|
||||
\author Barend Gehrels, and the following recourses
|
||||
- A tutorial: http://www.skytopia.com/project/articles/compsci/clipping.html
|
||||
- a German applet (link broken): http://ls7-www.cs.uni-dortmund.de/students/projectgroups/acit/lineclip.shtml
|
||||
*/
|
||||
template<typename Box, typename Point>
|
||||
class liang_barsky
|
||||
{
|
||||
private:
|
||||
typedef model::referring_segment<Point> segment_type;
|
||||
|
||||
template <typename CoordinateType, typename CalcType>
|
||||
inline bool check_edge(CoordinateType const& p, CoordinateType const& q, CalcType& t1, CalcType& t2) const
|
||||
{
|
||||
bool visible = true;
|
||||
|
||||
if(p < 0)
|
||||
{
|
||||
CalcType const r = static_cast<CalcType>(q) / p;
|
||||
if (r > t2)
|
||||
visible = false;
|
||||
else if (r > t1)
|
||||
t1 = r;
|
||||
}
|
||||
else if(p > 0)
|
||||
{
|
||||
CalcType const r = static_cast<CalcType>(q) / p;
|
||||
if (r < t1)
|
||||
visible = false;
|
||||
else if (r < t2)
|
||||
t2 = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (q < 0)
|
||||
visible = false;
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// TODO: Temporary, this strategy should be moved, it is cartesian-only
|
||||
|
||||
typedef strategy::within::cartesian_point_point equals_point_point_strategy_type;
|
||||
|
||||
static inline equals_point_point_strategy_type get_equals_point_point_strategy()
|
||||
{
|
||||
return equals_point_point_strategy_type();
|
||||
}
|
||||
|
||||
inline bool clip_segment(Box const& b, segment_type& s, bool& sp1_clipped, bool& sp2_clipped) const
|
||||
{
|
||||
typedef typename select_coordinate_type<Box, Point>::type coordinate_type;
|
||||
typedef typename select_most_precise<coordinate_type, double>::type calc_type;
|
||||
|
||||
calc_type t1 = 0;
|
||||
calc_type t2 = 1;
|
||||
|
||||
coordinate_type const dx = get<1, 0>(s) - get<0, 0>(s);
|
||||
coordinate_type const dy = get<1, 1>(s) - get<0, 1>(s);
|
||||
|
||||
coordinate_type const p1 = -dx;
|
||||
coordinate_type const p2 = dx;
|
||||
coordinate_type const p3 = -dy;
|
||||
coordinate_type const p4 = dy;
|
||||
|
||||
coordinate_type const q1 = get<0, 0>(s) - get<min_corner, 0>(b);
|
||||
coordinate_type const q2 = get<max_corner, 0>(b) - get<0, 0>(s);
|
||||
coordinate_type const q3 = get<0, 1>(s) - get<min_corner, 1>(b);
|
||||
coordinate_type const q4 = get<max_corner, 1>(b) - get<0, 1>(s);
|
||||
|
||||
if (check_edge(p1, q1, t1, t2) // left
|
||||
&& check_edge(p2, q2, t1, t2) // right
|
||||
&& check_edge(p3, q3, t1, t2) // bottom
|
||||
&& check_edge(p4, q4, t1, t2)) // top
|
||||
{
|
||||
sp1_clipped = t1 > 0;
|
||||
sp2_clipped = t2 < 1;
|
||||
|
||||
if (sp2_clipped)
|
||||
{
|
||||
set<1, 0>(s, get<0, 0>(s) + t2 * dx);
|
||||
set<1, 1>(s, get<0, 1>(s) + t2 * dy);
|
||||
}
|
||||
|
||||
if(sp1_clipped)
|
||||
{
|
||||
set<0, 0>(s, get<0, 0>(s) + t1 * dx);
|
||||
set<0, 1>(s, get<0, 1>(s) + t1 * dy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Linestring, typename OutputIterator>
|
||||
inline void apply(Linestring& line_out, OutputIterator out) const
|
||||
{
|
||||
if (!boost::empty(line_out))
|
||||
{
|
||||
*out = line_out;
|
||||
++out;
|
||||
geometry::clear(line_out);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace strategy::intersection
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace intersection
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Clips a linestring with a box
|
||||
\details A linestring is intersected (clipped) by the specified box
|
||||
and the resulting linestring, or pieces of linestrings, are sent to the specified output operator.
|
||||
\tparam OutputLinestring type of the output linestrings
|
||||
\tparam OutputIterator an output iterator which outputs linestrings
|
||||
\tparam Linestring linestring-type, for example a vector of points, matching the output-iterator type,
|
||||
the points should also match the input-iterator type
|
||||
\tparam Box box type
|
||||
\tparam Strategy strategy, a clipping strategy which should implement the methods "clip_segment" and "apply"
|
||||
*/
|
||||
template
|
||||
<
|
||||
typename OutputLinestring,
|
||||
typename OutputIterator,
|
||||
typename Range,
|
||||
typename RobustPolicy,
|
||||
typename Box,
|
||||
typename Strategy
|
||||
>
|
||||
OutputIterator clip_range_with_box(Box const& b, Range const& range,
|
||||
RobustPolicy const&,
|
||||
OutputIterator out, Strategy const& strategy)
|
||||
{
|
||||
if (boost::begin(range) == boost::end(range))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
typedef typename point_type<OutputLinestring>::type point_type;
|
||||
|
||||
OutputLinestring line_out;
|
||||
|
||||
typedef typename boost::range_iterator<Range const>::type iterator_type;
|
||||
iterator_type vertex = boost::begin(range);
|
||||
for(iterator_type previous = vertex++;
|
||||
vertex != boost::end(range);
|
||||
++previous, ++vertex)
|
||||
{
|
||||
point_type p1, p2;
|
||||
geometry::convert(*previous, p1);
|
||||
geometry::convert(*vertex, p2);
|
||||
|
||||
// Clip the segment. Five situations:
|
||||
// 1. Segment is invisible, finish line if any (shouldn't occur)
|
||||
// 2. Segment is completely visible. Add (p1)-p2 to line
|
||||
// 3. Point 1 is invisible (clipped), point 2 is visible. Start new line from p1-p2...
|
||||
// 4. Point 1 is visible, point 2 is invisible (clipped). End the line with ...p2
|
||||
// 5. Point 1 and point 2 are both invisible (clipped). Start/finish an independant line p1-p2
|
||||
//
|
||||
// This results in:
|
||||
// a. if p1 is clipped, start new line
|
||||
// b. if segment is partly or completely visible, add the segment
|
||||
// c. if p2 is clipped, end the line
|
||||
|
||||
bool c1 = false;
|
||||
bool c2 = false;
|
||||
model::referring_segment<point_type> s(p1, p2);
|
||||
|
||||
if (!strategy.clip_segment(b, s, c1, c2))
|
||||
{
|
||||
strategy.apply(line_out, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
// a. If necessary, finish the line and add a start a new one
|
||||
if (c1)
|
||||
{
|
||||
strategy.apply(line_out, out);
|
||||
}
|
||||
|
||||
// b. Add p1 only if it is the first point, then add p2
|
||||
if (boost::empty(line_out))
|
||||
{
|
||||
detail::overlay::append_with_duplicates(line_out, p1);
|
||||
}
|
||||
detail::overlay::append_no_duplicates(line_out, p2,
|
||||
strategy.get_equals_point_point_strategy());
|
||||
|
||||
// c. If c2 is clipped, finish the line
|
||||
if (c2)
|
||||
{
|
||||
strategy.apply(line_out, out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add last part
|
||||
strategy.apply(line_out, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
}} // namespace detail::intersection
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP
|
||||
@@ -0,0 +1,46 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2016 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLUSTER_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLUSTER_INFO_HPP
|
||||
|
||||
|
||||
#include <set>
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
struct cluster_info
|
||||
{
|
||||
std::set<signed_size_type> turn_indices;
|
||||
|
||||
//! Number of open spaces (e.g. 2 for touch)
|
||||
std::size_t open_count;
|
||||
|
||||
inline cluster_info()
|
||||
: open_count(0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLUSTER_INFO_HPP
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2018.
|
||||
// Modifications copyright (c) 2018, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP
|
||||
|
||||
#include <boost/mpl/assert.hpp>
|
||||
#include <boost/range/algorithm/reverse.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/num_points.hpp>
|
||||
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
#include <boost/geometry/core/exterior_ring.hpp>
|
||||
#include <boost/geometry/core/interior_rings.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template<typename Tag>
|
||||
struct convert_ring
|
||||
{
|
||||
BOOST_MPL_ASSERT_MSG
|
||||
(
|
||||
false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TAG
|
||||
, (types<Tag>)
|
||||
);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert_ring<ring_tag>
|
||||
{
|
||||
template<typename Destination, typename Source>
|
||||
static inline void apply(Destination& destination, Source const& source,
|
||||
bool append, bool reverse)
|
||||
{
|
||||
if (! append)
|
||||
{
|
||||
geometry::convert(source, destination);
|
||||
if (reverse)
|
||||
{
|
||||
boost::reverse(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct convert_ring<polygon_tag>
|
||||
{
|
||||
template<typename Destination, typename Source>
|
||||
static inline void apply(Destination& destination, Source const& source,
|
||||
bool append, bool reverse)
|
||||
{
|
||||
if (! append)
|
||||
{
|
||||
geometry::convert(source, exterior_ring(destination));
|
||||
if (reverse)
|
||||
{
|
||||
boost::reverse(exterior_ring(destination));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid adding interior rings which are invalid
|
||||
// because of its number of points:
|
||||
std::size_t const min_num_points
|
||||
= core_detail::closure::minimum_ring_size
|
||||
<
|
||||
geometry::closure<Destination>::value
|
||||
>::value;
|
||||
|
||||
if (geometry::num_points(source) >= min_num_points)
|
||||
{
|
||||
interior_rings(destination).resize(
|
||||
interior_rings(destination).size() + 1);
|
||||
geometry::convert(source, interior_rings(destination).back());
|
||||
if (reverse)
|
||||
{
|
||||
boost::reverse(interior_rings(destination).back());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP
|
||||
@@ -0,0 +1,400 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP
|
||||
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/mpl/assert.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/core/ring_type.hpp>
|
||||
#include <boost/geometry/core/exterior_ring.hpp>
|
||||
#include <boost/geometry/core/interior_rings.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
#include <boost/geometry/geometries/concepts/check.hpp>
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
#include <boost/geometry/iterators/ever_circling_iterator.hpp>
|
||||
#include <boost/geometry/views/closeable_view.hpp>
|
||||
#include <boost/geometry/views/reversible_view.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace copy_segments
|
||||
{
|
||||
|
||||
|
||||
template <typename Range, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point_range
|
||||
{
|
||||
static inline bool apply(Range const& range,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point)
|
||||
{
|
||||
typedef typename closeable_view
|
||||
<
|
||||
Range const,
|
||||
closure<Range>::value
|
||||
>::type cview_type;
|
||||
|
||||
typedef typename reversible_view
|
||||
<
|
||||
cview_type const,
|
||||
Reverse ? iterate_reverse : iterate_forward
|
||||
>::type rview_type;
|
||||
|
||||
cview_type cview(range);
|
||||
rview_type view(cview);
|
||||
|
||||
typedef typename boost::range_iterator<rview_type>::type iterator;
|
||||
geometry::ever_circling_iterator<iterator> it(boost::begin(view), boost::end(view),
|
||||
boost::begin(view) + seg_id.segment_index, true);
|
||||
|
||||
for (signed_size_type i = 0; i < offset; ++i, ++it)
|
||||
{
|
||||
}
|
||||
|
||||
geometry::convert(*it, point);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point_polygon
|
||||
{
|
||||
static inline bool apply(Polygon const& polygon,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point)
|
||||
{
|
||||
// Call ring-version with the right ring
|
||||
return copy_segment_point_range
|
||||
<
|
||||
typename geometry::ring_type<Polygon>::type,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>::apply
|
||||
(
|
||||
seg_id.ring_index < 0
|
||||
? geometry::exterior_ring(polygon)
|
||||
: range::at(geometry::interior_rings(polygon), seg_id.ring_index),
|
||||
seg_id, offset,
|
||||
point
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point_box
|
||||
{
|
||||
static inline bool apply(Box const& box,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point)
|
||||
{
|
||||
signed_size_type index = seg_id.segment_index;
|
||||
for (int i = 0; i < offset; i++)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
boost::array<typename point_type<Box>::type, 4> bp;
|
||||
assign_box_corners_oriented<Reverse>(box, bp);
|
||||
point = bp[index % 4];
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiGeometry,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut,
|
||||
typename Policy
|
||||
>
|
||||
struct copy_segment_point_multi
|
||||
{
|
||||
static inline bool apply(MultiGeometry const& multi,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point)
|
||||
{
|
||||
|
||||
BOOST_GEOMETRY_ASSERT
|
||||
(
|
||||
seg_id.multi_index >= 0
|
||||
&& seg_id.multi_index < int(boost::size(multi))
|
||||
);
|
||||
|
||||
// Call the single-version
|
||||
return Policy::apply(range::at(multi, seg_id.multi_index), seg_id, offset, point);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::copy_segments
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace dispatch
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Tag,
|
||||
typename GeometryIn,
|
||||
bool Reverse,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
struct copy_segment_point
|
||||
{
|
||||
BOOST_MPL_ASSERT_MSG
|
||||
(
|
||||
false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE
|
||||
, (types<GeometryIn>)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
template <typename LineString, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point<linestring_tag, LineString, Reverse, SegmentIdentifier, PointOut>
|
||||
: detail::copy_segments::copy_segment_point_range
|
||||
<
|
||||
LineString, Reverse, SegmentIdentifier, PointOut
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template <typename Ring, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point<ring_tag, Ring, Reverse, SegmentIdentifier, PointOut>
|
||||
: detail::copy_segments::copy_segment_point_range
|
||||
<
|
||||
Ring, Reverse, SegmentIdentifier, PointOut
|
||||
>
|
||||
{};
|
||||
|
||||
template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point<polygon_tag, Polygon, Reverse, SegmentIdentifier, PointOut>
|
||||
: detail::copy_segments::copy_segment_point_polygon
|
||||
<
|
||||
Polygon, Reverse, SegmentIdentifier, PointOut
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut>
|
||||
struct copy_segment_point<box_tag, Box, Reverse, SegmentIdentifier, PointOut>
|
||||
: detail::copy_segments::copy_segment_point_box
|
||||
<
|
||||
Box, Reverse, SegmentIdentifier, PointOut
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiGeometry,
|
||||
bool Reverse,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
struct copy_segment_point
|
||||
<
|
||||
multi_polygon_tag,
|
||||
MultiGeometry,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>
|
||||
: detail::copy_segments::copy_segment_point_multi
|
||||
<
|
||||
MultiGeometry,
|
||||
SegmentIdentifier,
|
||||
PointOut,
|
||||
detail::copy_segments::copy_segment_point_polygon
|
||||
<
|
||||
typename boost::range_value<MultiGeometry>::type,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>
|
||||
>
|
||||
{};
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiGeometry,
|
||||
bool Reverse,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
struct copy_segment_point
|
||||
<
|
||||
multi_linestring_tag,
|
||||
MultiGeometry,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>
|
||||
: detail::copy_segments::copy_segment_point_multi
|
||||
<
|
||||
MultiGeometry,
|
||||
SegmentIdentifier,
|
||||
PointOut,
|
||||
detail::copy_segments::copy_segment_point_range
|
||||
<
|
||||
typename boost::range_value<MultiGeometry>::type,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
} // namespace dispatch
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief Helper function, copies a point from a segment
|
||||
\ingroup overlay
|
||||
*/
|
||||
template<bool Reverse, typename Geometry, typename SegmentIdentifier, typename PointOut>
|
||||
inline bool copy_segment_point(Geometry const& geometry,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point_out)
|
||||
{
|
||||
concepts::check<Geometry const>();
|
||||
|
||||
return dispatch::copy_segment_point
|
||||
<
|
||||
typename tag<Geometry>::type,
|
||||
Geometry,
|
||||
Reverse,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>::apply(geometry, seg_id, offset, point_out);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief Helper function, to avoid the same construct several times,
|
||||
copies a point, based on a source-index and two geometries
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
inline bool copy_segment_point(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
SegmentIdentifier const& seg_id, int offset,
|
||||
PointOut& point_out)
|
||||
{
|
||||
concepts::check<Geometry1 const>();
|
||||
concepts::check<Geometry2 const>();
|
||||
|
||||
BOOST_GEOMETRY_ASSERT(seg_id.source_index == 0 || seg_id.source_index == 1);
|
||||
|
||||
if (seg_id.source_index == 0)
|
||||
{
|
||||
return dispatch::copy_segment_point
|
||||
<
|
||||
typename tag<Geometry1>::type,
|
||||
Geometry1,
|
||||
Reverse1,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>::apply(geometry1, seg_id, offset, point_out);
|
||||
}
|
||||
else if (seg_id.source_index == 1)
|
||||
{
|
||||
return dispatch::copy_segment_point
|
||||
<
|
||||
typename tag<Geometry2>::type,
|
||||
Geometry2,
|
||||
Reverse2,
|
||||
SegmentIdentifier,
|
||||
PointOut
|
||||
>::apply(geometry2, seg_id, offset, point_out);
|
||||
}
|
||||
// Exception?
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief Helper function, to avoid the same construct several times,
|
||||
copies a point, based on a source-index and two geometries
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
inline bool copy_segment_points(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
SegmentIdentifier const& seg_id,
|
||||
PointOut& point1, PointOut& point2)
|
||||
{
|
||||
concepts::check<Geometry1 const>();
|
||||
concepts::check<Geometry2 const>();
|
||||
|
||||
return copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, 0, point1)
|
||||
&& copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, 1, point2);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Helper function, copies three points: two from the specified segment
|
||||
(from, to) and the next one
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename SegmentIdentifier,
|
||||
typename PointOut
|
||||
>
|
||||
inline bool copy_segment_points(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
SegmentIdentifier const& seg_id,
|
||||
PointOut& point1, PointOut& point2, PointOut& point3)
|
||||
{
|
||||
concepts::check<Geometry1 const>();
|
||||
concepts::check<Geometry2 const>();
|
||||
|
||||
return copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, 0, point1)
|
||||
&& copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, 1, point2)
|
||||
&& copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, 2, point3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP
|
||||
@@ -0,0 +1,387 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2014, 2017, 2018.
|
||||
// Modifications copyright (c) 2014-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/mpl/assert.hpp>
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/assign_box_corners.hpp>
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp>
|
||||
#include <boost/geometry/algorithms/not_implemented.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/geometries/concepts/check.hpp>
|
||||
|
||||
#include <boost/geometry/iterators/ever_circling_iterator.hpp>
|
||||
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
#include <boost/geometry/views/closeable_view.hpp>
|
||||
#include <boost/geometry/views/reversible_view.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace copy_segments
|
||||
{
|
||||
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments_ring
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Ring,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
static inline void apply(Ring const& ring,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& current_output)
|
||||
{
|
||||
typedef typename closeable_view
|
||||
<
|
||||
Ring const,
|
||||
closure<Ring>::value
|
||||
>::type cview_type;
|
||||
|
||||
typedef typename reversible_view
|
||||
<
|
||||
cview_type const,
|
||||
Reverse ? iterate_reverse : iterate_forward
|
||||
>::type rview_type;
|
||||
|
||||
typedef typename boost::range_iterator<rview_type const>::type iterator;
|
||||
typedef geometry::ever_circling_iterator<iterator> ec_iterator;
|
||||
|
||||
|
||||
cview_type cview(ring);
|
||||
rview_type view(cview);
|
||||
|
||||
// The problem: sometimes we want to from "3" to "2"
|
||||
// -> end = "3" -> end == begin
|
||||
// This is not convenient with iterators.
|
||||
|
||||
// So we use the ever-circling iterator and determine when to step out
|
||||
|
||||
signed_size_type const from_index = seg_id.segment_index + 1;
|
||||
|
||||
// Sanity check
|
||||
BOOST_GEOMETRY_ASSERT(from_index < static_cast<signed_size_type>(boost::size(view)));
|
||||
|
||||
ec_iterator it(boost::begin(view), boost::end(view),
|
||||
boost::begin(view) + from_index);
|
||||
|
||||
// [2..4] -> 4 - 2 + 1 = 3 -> {2,3,4} -> OK
|
||||
// [4..2],size=6 -> 6 - 4 + 2 + 1 = 5 -> {4,5,0,1,2} -> OK
|
||||
// [1..1], travel the whole ring round
|
||||
signed_size_type const count = from_index <= to_index
|
||||
? to_index - from_index + 1
|
||||
: static_cast<signed_size_type>(boost::size(view))
|
||||
- from_index + to_index + 1;
|
||||
|
||||
for (signed_size_type i = 0; i < count; ++i, ++it)
|
||||
{
|
||||
detail::overlay::append_no_dups_or_spikes(current_output, *it, strategy, robust_policy);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool Reverse, bool RemoveSpikes = true>
|
||||
class copy_segments_linestring
|
||||
{
|
||||
private:
|
||||
// remove spikes
|
||||
template <typename RangeOut, typename Point, typename SideStrategy, typename RobustPolicy>
|
||||
static inline void append_to_output(RangeOut& current_output,
|
||||
Point const& point,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
boost::true_type const&)
|
||||
{
|
||||
detail::overlay::append_no_dups_or_spikes(current_output, point,
|
||||
strategy,
|
||||
robust_policy);
|
||||
}
|
||||
|
||||
// keep spikes
|
||||
template <typename RangeOut, typename Point, typename SideStrategy, typename RobustPolicy>
|
||||
static inline void append_to_output(RangeOut& current_output,
|
||||
Point const& point,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const&,
|
||||
boost::false_type const&)
|
||||
{
|
||||
detail::overlay::append_no_duplicates(current_output, point, strategy.get_equals_point_point_strategy());
|
||||
}
|
||||
|
||||
public:
|
||||
template
|
||||
<
|
||||
typename LineString,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
static inline void apply(LineString const& ls,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& current_output)
|
||||
{
|
||||
signed_size_type const from_index = seg_id.segment_index + 1;
|
||||
|
||||
// Sanity check
|
||||
if ( from_index > to_index
|
||||
|| from_index < 0
|
||||
|| to_index >= static_cast<signed_size_type>(boost::size(ls)) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
signed_size_type const count = to_index - from_index + 1;
|
||||
|
||||
typename boost::range_iterator<LineString const>::type
|
||||
it = boost::begin(ls) + from_index;
|
||||
|
||||
for (signed_size_type i = 0; i < count; ++i, ++it)
|
||||
{
|
||||
append_to_output(current_output, *it, strategy, robust_policy,
|
||||
boost::integral_constant<bool, RemoveSpikes>());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments_polygon
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Polygon,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
static inline void apply(Polygon const& polygon,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& current_output)
|
||||
{
|
||||
// Call ring-version with the right ring
|
||||
copy_segments_ring<Reverse>::apply
|
||||
(
|
||||
seg_id.ring_index < 0
|
||||
? geometry::exterior_ring(polygon)
|
||||
: range::at(geometry::interior_rings(polygon), seg_id.ring_index),
|
||||
seg_id, to_index,
|
||||
strategy,
|
||||
robust_policy,
|
||||
current_output
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments_box
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Box,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
static inline void apply(Box const& box,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& current_output)
|
||||
{
|
||||
signed_size_type index = seg_id.segment_index + 1;
|
||||
BOOST_GEOMETRY_ASSERT(index < 5);
|
||||
|
||||
signed_size_type const count = index <= to_index
|
||||
? to_index - index + 1
|
||||
: 5 - index + to_index + 1;
|
||||
|
||||
// Create array of points, the fifth one closes it
|
||||
boost::array<typename point_type<Box>::type, 5> bp;
|
||||
assign_box_corners_oriented<Reverse>(box, bp);
|
||||
bp[4] = bp[0];
|
||||
|
||||
// (possibly cyclic) copy to output
|
||||
// (see comments in ring-version)
|
||||
for (signed_size_type i = 0; i < count; i++, index++)
|
||||
{
|
||||
detail::overlay::append_no_dups_or_spikes(current_output,
|
||||
bp[index % 5], strategy, robust_policy);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Policy>
|
||||
struct copy_segments_multi
|
||||
{
|
||||
template
|
||||
<
|
||||
typename MultiGeometry,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
static inline void apply(MultiGeometry const& multi_geometry,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& current_output)
|
||||
{
|
||||
|
||||
BOOST_GEOMETRY_ASSERT
|
||||
(
|
||||
seg_id.multi_index >= 0
|
||||
&& static_cast<std::size_t>(seg_id.multi_index) < boost::size(multi_geometry)
|
||||
);
|
||||
|
||||
// Call the single-version
|
||||
Policy::apply(range::at(multi_geometry, seg_id.multi_index),
|
||||
seg_id, to_index,
|
||||
strategy,
|
||||
robust_policy,
|
||||
current_output);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::copy_segments
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace dispatch
|
||||
{
|
||||
|
||||
template
|
||||
<
|
||||
typename Tag,
|
||||
bool Reverse
|
||||
>
|
||||
struct copy_segments : not_implemented<Tag>
|
||||
{};
|
||||
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments<ring_tag, Reverse>
|
||||
: detail::copy_segments::copy_segments_ring<Reverse>
|
||||
{};
|
||||
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments<linestring_tag, Reverse>
|
||||
: detail::copy_segments::copy_segments_linestring<Reverse>
|
||||
{};
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments<polygon_tag, Reverse>
|
||||
: detail::copy_segments::copy_segments_polygon<Reverse>
|
||||
{};
|
||||
|
||||
|
||||
template <bool Reverse>
|
||||
struct copy_segments<box_tag, Reverse>
|
||||
: detail::copy_segments::copy_segments_box<Reverse>
|
||||
{};
|
||||
|
||||
|
||||
template<bool Reverse>
|
||||
struct copy_segments<multi_polygon_tag, Reverse>
|
||||
: detail::copy_segments::copy_segments_multi
|
||||
<
|
||||
detail::copy_segments::copy_segments_polygon<Reverse>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
} // namespace dispatch
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
/*!
|
||||
\brief Copy segments from a geometry, starting with the specified segment (seg_id)
|
||||
until the specified index (to_index)
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename Geometry,
|
||||
typename SegmentIdentifier,
|
||||
typename SideStrategy,
|
||||
typename RobustPolicy,
|
||||
typename RangeOut
|
||||
>
|
||||
inline void copy_segments(Geometry const& geometry,
|
||||
SegmentIdentifier const& seg_id,
|
||||
signed_size_type to_index,
|
||||
SideStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
RangeOut& range_out)
|
||||
{
|
||||
concepts::check<Geometry const>();
|
||||
|
||||
dispatch::copy_segments
|
||||
<
|
||||
typename tag<Geometry>::type,
|
||||
Reverse
|
||||
>::apply(geometry, seg_id, to_index, strategy, robust_policy, range_out);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP
|
||||
@@ -0,0 +1,66 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
inline char method_char(detail::overlay::method_type const& method)
|
||||
{
|
||||
using namespace detail::overlay;
|
||||
switch(method)
|
||||
{
|
||||
case method_none : return '-';
|
||||
case method_disjoint : return 'd';
|
||||
case method_crosses : return 'i';
|
||||
case method_touch : return 't';
|
||||
case method_touch_interior : return 'm';
|
||||
case method_collinear : return 'c';
|
||||
case method_equal : return 'e';
|
||||
case method_error : return '!';
|
||||
default : return '?';
|
||||
}
|
||||
}
|
||||
|
||||
inline char operation_char(detail::overlay::operation_type const& operation)
|
||||
{
|
||||
using namespace detail::overlay;
|
||||
switch(operation)
|
||||
{
|
||||
case operation_none : return '-';
|
||||
case operation_union : return 'u';
|
||||
case operation_intersection : return 'i';
|
||||
case operation_blocked : return 'x';
|
||||
case operation_continue : return 'c';
|
||||
case operation_opposite : return 'o';
|
||||
default : return '?';
|
||||
}
|
||||
}
|
||||
|
||||
inline char visited_char(detail::overlay::visit_info const& v)
|
||||
{
|
||||
if (v.rejected()) return 'R';
|
||||
if (v.started()) return 's';
|
||||
if (v.visited()) return 'v';
|
||||
if (v.none()) return '-';
|
||||
if (v.finished()) return 'f';
|
||||
return '?';
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP
|
||||
|
||||
#include <boost/geometry/core/point_order.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
// Metafunction helper for intersection and union
|
||||
template <order_selector Selector, bool Reverse = false>
|
||||
struct do_reverse {};
|
||||
|
||||
template <>
|
||||
struct do_reverse<clockwise, false> : boost::false_type {};
|
||||
|
||||
template <>
|
||||
struct do_reverse<clockwise, true> : boost::true_type {};
|
||||
|
||||
template <>
|
||||
struct do_reverse<counterclockwise, false> : boost::true_type {};
|
||||
|
||||
template <>
|
||||
struct do_reverse<counterclockwise, true> : boost::false_type {};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP
|
||||
@@ -0,0 +1,553 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
# include <iostream>
|
||||
# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
||||
# include <boost/geometry/io/wkt/wkt.hpp>
|
||||
# if ! defined(BOOST_GEOMETRY_DEBUG_IDENTIFIER)
|
||||
# define BOOST_GEOMETRY_DEBUG_IDENTIFIER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/handle_colocations.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/policies/robustness/robust_type.hpp>
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
# include <boost/geometry/algorithms/detail/overlay/check_enrich.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <typename Turns>
|
||||
struct discarded_turn
|
||||
{
|
||||
discarded_turn(Turns const& turns)
|
||||
: m_turns(turns)
|
||||
{}
|
||||
|
||||
template <typename IndexedTurn>
|
||||
inline bool operator()(IndexedTurn const& indexed) const
|
||||
{
|
||||
return m_turns[indexed.turn_index].discarded;
|
||||
}
|
||||
|
||||
Turns const& m_turns;
|
||||
};
|
||||
|
||||
// Sorts IP-s of this ring on segment-identifier, and if on same segment,
|
||||
// on distance.
|
||||
// Then assigns for each IP which is the next IP on this segment,
|
||||
// plus the vertex-index to travel to, plus the next IP
|
||||
// (might be on another segment)
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Operations,
|
||||
typename Turns,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RobustPolicy,
|
||||
typename SideStrategy
|
||||
>
|
||||
inline void enrich_sort(Operations& operations,
|
||||
Turns const& turns,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
std::sort(boost::begin(operations),
|
||||
boost::end(operations),
|
||||
less_by_segment_ratio
|
||||
<
|
||||
Turns,
|
||||
typename boost::range_value<Operations>::type,
|
||||
Geometry1, Geometry2,
|
||||
RobustPolicy,
|
||||
SideStrategy,
|
||||
Reverse1, Reverse2
|
||||
>(turns, geometry1, geometry2, robust_policy, strategy));
|
||||
}
|
||||
|
||||
|
||||
template <typename Operations, typename Turns>
|
||||
inline void enrich_assign(Operations& operations, Turns& turns,
|
||||
bool check_turns)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type op_type;
|
||||
typedef typename boost::range_iterator<Operations>::type iterator_type;
|
||||
|
||||
|
||||
if (operations.size() > 0)
|
||||
{
|
||||
// Assign travel-to-vertex/ip index for each turning point.
|
||||
// Iterator "next" is circular
|
||||
|
||||
geometry::ever_circling_range_iterator<Operations const> next(operations);
|
||||
++next;
|
||||
|
||||
for (iterator_type it = boost::begin(operations);
|
||||
it != boost::end(operations); ++it)
|
||||
{
|
||||
turn_type& turn = turns[it->turn_index];
|
||||
op_type& op = turn.operations[it->operation_index];
|
||||
|
||||
if (check_turns && it->turn_index == next->turn_index)
|
||||
{
|
||||
// Normal behaviour: next points at next turn, increase next.
|
||||
// For dissolve this should not be done, turn_index is often
|
||||
// the same for two consecutive operations
|
||||
++next;
|
||||
}
|
||||
|
||||
// Cluster behaviour: next should point after cluster, unless
|
||||
// their seg_ids are not the same
|
||||
// (For dissolve, this is still to be examined - TODO)
|
||||
while (turn.is_clustered()
|
||||
&& it->turn_index != next->turn_index
|
||||
&& turn.cluster_id == turns[next->turn_index].cluster_id
|
||||
&& op.seg_id == turns[next->turn_index].operations[next->operation_index].seg_id)
|
||||
{
|
||||
++next;
|
||||
}
|
||||
|
||||
turn_type const& next_turn = turns[next->turn_index];
|
||||
op_type const& next_op = next_turn.operations[next->operation_index];
|
||||
|
||||
op.enriched.travels_to_ip_index
|
||||
= static_cast<signed_size_type>(next->turn_index);
|
||||
op.enriched.travels_to_vertex_index
|
||||
= next->subject->seg_id.segment_index;
|
||||
|
||||
if (op.seg_id.segment_index == next_op.seg_id.segment_index
|
||||
&& op.fraction < next_op.fraction)
|
||||
{
|
||||
// Next turn is located further on same segment
|
||||
// assign next_ip_index
|
||||
// (this is one not circular therefore fraction is considered)
|
||||
op.enriched.next_ip_index = static_cast<signed_size_type>(next->turn_index);
|
||||
}
|
||||
|
||||
if (! check_turns)
|
||||
{
|
||||
++next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
{
|
||||
for (iterator_type it = boost::begin(operations);
|
||||
it != boost::end(operations);
|
||||
++it)
|
||||
{
|
||||
op_type const& op = turns[it->turn_index]
|
||||
.operations[it->operation_index];
|
||||
|
||||
std::cout << it->turn_index
|
||||
<< " cl=" << turns[it->turn_index].cluster_id
|
||||
<< " meth=" << method_char(turns[it->turn_index].method)
|
||||
<< " seg=" << op.seg_id
|
||||
<< " dst=" << op.fraction // needs define
|
||||
<< " op=" << operation_char(turns[it->turn_index].operations[0].operation)
|
||||
<< operation_char(turns[it->turn_index].operations[1].operation)
|
||||
<< " (" << operation_char(op.operation) << ")"
|
||||
<< " nxt=" << op.enriched.next_ip_index
|
||||
<< " / " << op.enriched.travels_to_ip_index
|
||||
<< " [vx " << op.enriched.travels_to_vertex_index << "]"
|
||||
<< std::boolalpha << turns[it->turn_index].discarded
|
||||
<< std::endl;
|
||||
;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// END DEBUG
|
||||
|
||||
}
|
||||
|
||||
template <typename Operations, typename Turns>
|
||||
inline void enrich_adapt(Operations& operations, Turns& turns)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type op_type;
|
||||
typedef typename boost::range_value<Operations>::type indexed_turn_type;
|
||||
|
||||
if (operations.size() < 3)
|
||||
{
|
||||
// If it is empty, or contains one or two turns, it makes no sense
|
||||
return;
|
||||
}
|
||||
|
||||
// Operations is a vector of indexed_turn_operation<>
|
||||
|
||||
// Last index:
|
||||
std::size_t const x = operations.size() - 1;
|
||||
bool next_phase = false;
|
||||
|
||||
for (std::size_t i = 0; i < operations.size(); i++)
|
||||
{
|
||||
indexed_turn_type const& indexed = operations[i];
|
||||
|
||||
turn_type& turn = turns[indexed.turn_index];
|
||||
op_type& op = turn.operations[indexed.operation_index];
|
||||
|
||||
// Previous/next index
|
||||
std::size_t const p = i > 0 ? i - 1 : x;
|
||||
std::size_t const n = i < x ? i + 1 : 0;
|
||||
|
||||
turn_type const& next_turn = turns[operations[n].turn_index];
|
||||
op_type const& next_op = next_turn.operations[operations[n].operation_index];
|
||||
|
||||
if (op.seg_id.segment_index == next_op.seg_id.segment_index)
|
||||
{
|
||||
turn_type const& prev_turn = turns[operations[p].turn_index];
|
||||
op_type const& prev_op = prev_turn.operations[operations[p].operation_index];
|
||||
if (op.seg_id.segment_index == prev_op.seg_id.segment_index)
|
||||
{
|
||||
op.enriched.startable = false;
|
||||
next_phase = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! next_phase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Discard turns which are both non-startable
|
||||
next_phase = false;
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
if (! turn.operations[0].enriched.startable
|
||||
&& ! turn.operations[1].enriched.startable)
|
||||
{
|
||||
turn.discarded = true;
|
||||
next_phase = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! next_phase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove discarded turns from operations to avoid having them as next turn
|
||||
discarded_turn<Turns> const predicate(turns);
|
||||
operations.erase(std::remove_if(boost::begin(operations),
|
||||
boost::end(operations), predicate), boost::end(operations));
|
||||
}
|
||||
|
||||
template <typename Turns, typename MappedVector>
|
||||
inline void create_map(Turns const& turns, MappedVector& mapped_vector)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::container_type container_type;
|
||||
typedef typename MappedVector::mapped_type mapped_type;
|
||||
typedef typename boost::range_value<mapped_type>::type indexed_type;
|
||||
|
||||
std::size_t index = 0;
|
||||
for (typename boost::range_iterator<Turns const>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it, ++index)
|
||||
{
|
||||
// Add all (non discarded) operations on this ring
|
||||
// Blocked operations or uu on clusters (for intersection)
|
||||
// should be included, to block potential paths in clusters
|
||||
turn_type const& turn = *it;
|
||||
if (turn.discarded)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t op_index = 0;
|
||||
for (typename boost::range_iterator<container_type const>::type
|
||||
op_it = boost::begin(turn.operations);
|
||||
op_it != boost::end(turn.operations);
|
||||
++op_it, ++op_index)
|
||||
{
|
||||
ring_identifier const ring_id
|
||||
(
|
||||
op_it->seg_id.source_index,
|
||||
op_it->seg_id.multi_index,
|
||||
op_it->seg_id.ring_index
|
||||
);
|
||||
mapped_vector[ring_id].push_back
|
||||
(
|
||||
indexed_type(index, op_index, *op_it,
|
||||
it->operations[1 - op_index].seg_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Point1, typename Point2>
|
||||
inline typename geometry::coordinate_type<Point1>::type
|
||||
distance_measure(Point1 const& a, Point2 const& b)
|
||||
{
|
||||
// TODO: use comparable distance for point-point instead - but that
|
||||
// causes currently cycling include problems
|
||||
typedef typename geometry::coordinate_type<Point1>::type ctype;
|
||||
ctype const dx = get<0>(a) - get<0>(b);
|
||||
ctype const dy = get<1>(a) - get<1>(b);
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
template <typename Turns>
|
||||
inline void calculate_remaining_distance(Turns& turns)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type op_type;
|
||||
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
|
||||
op_type& op0 = turn.operations[0];
|
||||
op_type& op1 = turn.operations[1];
|
||||
|
||||
if (op0.remaining_distance != 0
|
||||
|| op1.remaining_distance != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
signed_size_type const to_index0 = op0.enriched.get_next_turn_index();
|
||||
signed_size_type const to_index1 = op1.enriched.get_next_turn_index();
|
||||
if (to_index0 >= 0
|
||||
&& to_index1 >= 0
|
||||
&& to_index0 != to_index1)
|
||||
{
|
||||
op0.remaining_distance = distance_measure(turn.point, turns[to_index0].point);
|
||||
op1.remaining_distance = distance_measure(turn.point, turns[to_index1].point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\brief All intersection points are enriched with successor information
|
||||
\ingroup overlay
|
||||
\tparam Turns type of intersection container
|
||||
(e.g. vector of "intersection/turn point"'s)
|
||||
\tparam Clusters type of cluster container
|
||||
\tparam Geometry1 \tparam_geometry
|
||||
\tparam Geometry2 \tparam_geometry
|
||||
\tparam SideStrategy side strategy type
|
||||
\param turns container containing intersection points
|
||||
\param clusters container containing clusters
|
||||
\param geometry1 \param_geometry
|
||||
\param geometry2 \param_geometry
|
||||
\param robust_policy policy to handle robustness issues
|
||||
\param strategy strategy
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RobustPolicy,
|
||||
typename SideStrategy
|
||||
>
|
||||
inline void enrich_intersection_points(Turns& turns,
|
||||
Clusters& clusters,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
static const detail::overlay::operation_type target_operation
|
||||
= detail::overlay::operation_from_overlay<OverlayType>::value;
|
||||
static const detail::overlay::operation_type opposite_operation
|
||||
= target_operation == detail::overlay::operation_union
|
||||
? detail::overlay::operation_intersection
|
||||
: detail::overlay::operation_union;
|
||||
static const bool is_dissolve = OverlayType == overlay_dissolve;
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type op_type;
|
||||
typedef detail::overlay::indexed_turn_operation
|
||||
<
|
||||
op_type
|
||||
> indexed_turn_operation;
|
||||
|
||||
typedef std::map
|
||||
<
|
||||
ring_identifier,
|
||||
std::vector<indexed_turn_operation>
|
||||
> mapped_vector_type;
|
||||
|
||||
bool has_cc = false;
|
||||
bool const has_colocations
|
||||
= detail::overlay::handle_colocations<Reverse1, Reverse2, OverlayType>(turns,
|
||||
clusters, geometry1, geometry2);
|
||||
|
||||
// Discard turns not part of target overlay
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
|
||||
if (turn.both(detail::overlay::operation_none)
|
||||
|| turn.both(opposite_operation)
|
||||
|| turn.both(detail::overlay::operation_blocked)
|
||||
|| (detail::overlay::is_self_turn<OverlayType>(turn)
|
||||
&& ! turn.is_clustered()
|
||||
&& ! turn.both(target_operation)))
|
||||
{
|
||||
// For all operations, discard xx and none/none
|
||||
// For intersections, remove uu to avoid the need to travel
|
||||
// a union (during intersection) in uu/cc clusters (e.g. #31,#32,#33)
|
||||
// The ux is necessary to indicate impossible paths
|
||||
// (especially if rescaling is removed)
|
||||
|
||||
// Similarly, for union, discard ii and ix
|
||||
|
||||
// For self-turns, only keep uu / ii
|
||||
|
||||
turn.discarded = true;
|
||||
turn.cluster_id = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! turn.discarded
|
||||
&& turn.both(detail::overlay::operation_continue))
|
||||
{
|
||||
has_cc = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_dissolve)
|
||||
{
|
||||
detail::overlay::discard_closed_turns
|
||||
<
|
||||
OverlayType,
|
||||
target_operation
|
||||
>::apply(turns, clusters, geometry1, geometry2);
|
||||
detail::overlay::discard_open_turns
|
||||
<
|
||||
OverlayType,
|
||||
target_operation
|
||||
>::apply(turns, clusters, geometry1, geometry2);
|
||||
}
|
||||
|
||||
// Create a map of vectors of indexed operation-types to be able
|
||||
// to sort intersection points PER RING
|
||||
mapped_vector_type mapped_vector;
|
||||
|
||||
detail::overlay::create_map(turns, mapped_vector);
|
||||
|
||||
// No const-iterator; contents of mapped copy is temporary,
|
||||
// and changed by enrich
|
||||
for (typename mapped_vector_type::iterator mit
|
||||
= mapped_vector.begin();
|
||||
mit != mapped_vector.end();
|
||||
++mit)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
std::cout << "ENRICH-sort Ring "
|
||||
<< mit->first << std::endl;
|
||||
#endif
|
||||
detail::overlay::enrich_sort<Reverse1, Reverse2>(
|
||||
mit->second, turns,
|
||||
geometry1, geometry2,
|
||||
robust_policy, strategy);
|
||||
}
|
||||
|
||||
for (typename mapped_vector_type::iterator mit
|
||||
= mapped_vector.begin();
|
||||
mit != mapped_vector.end();
|
||||
++mit)
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
std::cout << "ENRICH-assign Ring "
|
||||
<< mit->first << std::endl;
|
||||
#endif
|
||||
if (is_dissolve)
|
||||
{
|
||||
detail::overlay::enrich_adapt(mit->second, turns);
|
||||
}
|
||||
|
||||
detail::overlay::enrich_assign(mit->second, turns, ! is_dissolve);
|
||||
}
|
||||
|
||||
if (has_colocations)
|
||||
{
|
||||
// First gather cluster properties (using even clusters with
|
||||
// discarded turns - for open turns), then clean up clusters
|
||||
detail::overlay::gather_cluster_properties
|
||||
<
|
||||
Reverse1,
|
||||
Reverse2,
|
||||
OverlayType
|
||||
>(clusters, turns, target_operation,
|
||||
geometry1, geometry2, strategy);
|
||||
|
||||
detail::overlay::cleanup_clusters(turns, clusters);
|
||||
}
|
||||
|
||||
if (has_cc)
|
||||
{
|
||||
detail::overlay::calculate_remaining_distance(turns);
|
||||
}
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
|
||||
//detail::overlay::check_graph(turns, for_operation);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP
|
||||
@@ -0,0 +1,84 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Keeps info to enrich intersection info (per source)
|
||||
\details Class to keep information necessary for traversal phase (a phase
|
||||
of the overlay process). The information is gathered during the
|
||||
enrichment phase
|
||||
*/
|
||||
template<typename Point>
|
||||
struct enrichment_info
|
||||
{
|
||||
inline enrichment_info()
|
||||
: travels_to_vertex_index(-1)
|
||||
, travels_to_ip_index(-1)
|
||||
, next_ip_index(-1)
|
||||
, startable(true)
|
||||
, prefer_start(true)
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, rank(-1)
|
||||
, zone(-1)
|
||||
, region_id(-1)
|
||||
, isolated(false)
|
||||
{}
|
||||
|
||||
inline signed_size_type get_next_turn_index() const
|
||||
{
|
||||
return next_ip_index == -1 ? travels_to_ip_index : next_ip_index;
|
||||
}
|
||||
|
||||
// vertex to which is free travel after this IP,
|
||||
// so from "segment_index+1" to "travels_to_vertex_index", without IP-s,
|
||||
// can be -1
|
||||
signed_size_type travels_to_vertex_index;
|
||||
|
||||
// same but now IP index, so "next IP index" but not on THIS segment
|
||||
signed_size_type travels_to_ip_index;
|
||||
|
||||
// index of next IP on this segment, -1 if there is no one
|
||||
signed_size_type next_ip_index;
|
||||
|
||||
bool startable; // Can be used to start in traverse
|
||||
bool prefer_start; // Is preferred as starting point (if true)
|
||||
|
||||
// Counts if polygons left/right of this operation
|
||||
std::size_t count_left;
|
||||
std::size_t count_right;
|
||||
signed_size_type rank; // in cluster
|
||||
signed_size_type zone; // open zone, in cluster
|
||||
signed_size_type region_id;
|
||||
bool isolated;
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP
|
||||
@@ -0,0 +1,524 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2014, 2017, 2018.
|
||||
// Modifications copyright (c) 2014-2018 Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/mpl/assert.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/point_on_border.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/covered_by.hpp>
|
||||
#include <boost/geometry/algorithms/clear.hpp>
|
||||
#include <boost/geometry/algorithms/detail/relate/turns.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
namespace following
|
||||
{
|
||||
|
||||
template <typename Turn, typename Operation>
|
||||
static inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op)
|
||||
{
|
||||
// (Blocked means: blocked for polygon/polygon intersection, because
|
||||
// they are reversed. But for polygon/line it is similar to continue)
|
||||
return op.operation == operation_intersection
|
||||
|| op.operation == operation_continue
|
||||
|| op.operation == operation_blocked
|
||||
;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turn,
|
||||
typename Operation,
|
||||
typename LineString,
|
||||
typename Polygon,
|
||||
typename PtInPolyStrategy
|
||||
>
|
||||
static inline bool last_covered_by(Turn const& /*turn*/, Operation const& op,
|
||||
LineString const& linestring, Polygon const& polygon,
|
||||
PtInPolyStrategy const& strategy)
|
||||
{
|
||||
return geometry::covered_by(range::at(linestring, op.seg_id.segment_index), polygon, strategy);
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Turn,
|
||||
typename Operation,
|
||||
typename LineString,
|
||||
typename Polygon,
|
||||
typename PtInPolyStrategy
|
||||
>
|
||||
static inline bool is_leaving(Turn const& turn, Operation const& op,
|
||||
bool entered, bool first,
|
||||
LineString const& linestring, Polygon const& polygon,
|
||||
PtInPolyStrategy const& strategy)
|
||||
{
|
||||
if (op.operation == operation_union)
|
||||
{
|
||||
return entered
|
||||
|| turn.method == method_crosses
|
||||
|| (first && last_covered_by(turn, op, linestring, polygon, strategy))
|
||||
;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Turn,
|
||||
typename Operation,
|
||||
typename LineString,
|
||||
typename Polygon,
|
||||
typename PtInPolyStrategy
|
||||
>
|
||||
static inline bool is_staying_inside(Turn const& turn, Operation const& op,
|
||||
bool entered, bool first,
|
||||
LineString const& linestring, Polygon const& polygon,
|
||||
PtInPolyStrategy const& strategy)
|
||||
{
|
||||
if (turn.method == method_crosses)
|
||||
{
|
||||
// The normal case, this is completely covered with entering/leaving
|
||||
// so stay out of this time consuming "covered_by"
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_entering(turn, op))
|
||||
{
|
||||
return entered || (first && last_covered_by(turn, op, linestring, polygon, strategy));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turn,
|
||||
typename Operation,
|
||||
typename Linestring,
|
||||
typename Polygon,
|
||||
typename PtInPolyStrategy
|
||||
>
|
||||
static inline bool was_entered(Turn const& turn, Operation const& op, bool first,
|
||||
Linestring const& linestring, Polygon const& polygon,
|
||||
PtInPolyStrategy const& strategy)
|
||||
{
|
||||
if (first && (turn.method == method_collinear || turn.method == method_equal))
|
||||
{
|
||||
return last_covered_by(turn, op, linestring, polygon, strategy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Template specialization structure to call the right actions for the right type
|
||||
template <overlay_type OverlayType, bool RemoveSpikes = true>
|
||||
struct action_selector
|
||||
{
|
||||
// If you get here the overlay type is not intersection or difference
|
||||
// BOOST_MPL_ASSERT(false);
|
||||
};
|
||||
|
||||
// Specialization for intersection, containing the implementation
|
||||
template <bool RemoveSpikes>
|
||||
struct action_selector<overlay_intersection, RemoveSpikes>
|
||||
{
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation,
|
||||
typename Strategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void enter(LineStringOut& current_piece,
|
||||
LineString const& ,
|
||||
segment_identifier& segment_id,
|
||||
signed_size_type , Point const& point,
|
||||
Operation const& operation,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& ,
|
||||
OutputIterator& )
|
||||
{
|
||||
// On enter, append the intersection point and remember starting point
|
||||
// TODO: we don't check on spikes for linestrings (?). Consider this.
|
||||
detail::overlay::append_no_duplicates(current_piece, point, strategy.get_equals_point_point_strategy());
|
||||
segment_id = operation.seg_id;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation,
|
||||
typename Strategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void leave(LineStringOut& current_piece,
|
||||
LineString const& linestring,
|
||||
segment_identifier& segment_id,
|
||||
signed_size_type index, Point const& point,
|
||||
Operation const& ,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator& out)
|
||||
{
|
||||
// On leave, copy all segments from starting point, append the intersection point
|
||||
// and add the output piece
|
||||
detail::copy_segments::copy_segments_linestring
|
||||
<
|
||||
false, RemoveSpikes
|
||||
>::apply(linestring, segment_id, index, strategy, robust_policy, current_piece);
|
||||
detail::overlay::append_no_duplicates(current_piece, point, strategy.get_equals_point_point_strategy());
|
||||
if (::boost::size(current_piece) > 1)
|
||||
{
|
||||
*out++ = current_piece;
|
||||
}
|
||||
|
||||
geometry::clear(current_piece);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation
|
||||
>
|
||||
static inline void isolated_point(LineStringOut&,
|
||||
LineString const&,
|
||||
segment_identifier&,
|
||||
signed_size_type, Point const& point,
|
||||
Operation const& , OutputIterator& out)
|
||||
{
|
||||
LineStringOut isolated_point_ls;
|
||||
geometry::append(isolated_point_ls, point);
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
|
||||
geometry::append(isolated_point_ls, point);
|
||||
#endif // BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
|
||||
|
||||
*out++ = isolated_point_ls;
|
||||
}
|
||||
|
||||
static inline bool is_entered(bool entered)
|
||||
{
|
||||
return entered;
|
||||
}
|
||||
|
||||
static inline bool included(int inside_value)
|
||||
{
|
||||
return inside_value >= 0; // covered_by
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Specialization for difference, which reverses these actions
|
||||
template <bool RemoveSpikes>
|
||||
struct action_selector<overlay_difference, RemoveSpikes>
|
||||
{
|
||||
typedef action_selector<overlay_intersection, RemoveSpikes> normal_action;
|
||||
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation,
|
||||
typename Strategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void enter(LineStringOut& current_piece,
|
||||
LineString const& linestring,
|
||||
segment_identifier& segment_id,
|
||||
signed_size_type index, Point const& point,
|
||||
Operation const& operation,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator& out)
|
||||
{
|
||||
normal_action::leave(current_piece, linestring, segment_id, index,
|
||||
point, operation, strategy, robust_policy, out);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation,
|
||||
typename Strategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void leave(LineStringOut& current_piece,
|
||||
LineString const& linestring,
|
||||
segment_identifier& segment_id,
|
||||
signed_size_type index, Point const& point,
|
||||
Operation const& operation,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator& out)
|
||||
{
|
||||
normal_action::enter(current_piece, linestring, segment_id, index,
|
||||
point, operation, strategy, robust_policy, out);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename OutputIterator,
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Point,
|
||||
typename Operation
|
||||
>
|
||||
static inline void isolated_point(LineStringOut&,
|
||||
LineString const&,
|
||||
segment_identifier&,
|
||||
signed_size_type, Point const&,
|
||||
Operation const&, OutputIterator&)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool is_entered(bool entered)
|
||||
{
|
||||
return ! normal_action::is_entered(entered);
|
||||
}
|
||||
|
||||
static inline bool included(int inside_value)
|
||||
{
|
||||
return ! normal_action::included(inside_value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Follows a linestring from intersection point to intersection point, outputting which
|
||||
is inside, or outside, a ring or polygon
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
typename LineStringOut,
|
||||
typename LineString,
|
||||
typename Polygon,
|
||||
overlay_type OverlayType,
|
||||
bool RemoveSpikes = true
|
||||
>
|
||||
class follow
|
||||
{
|
||||
|
||||
#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
|
||||
template <typename Turn>
|
||||
struct sort_on_segment
|
||||
{
|
||||
// In case of turn point at the same location, we want to have continue/blocked LAST
|
||||
// because that should be followed (intersection) or skipped (difference).
|
||||
inline int operation_order(Turn const& turn) const
|
||||
{
|
||||
operation_type const& operation = turn.operations[0].operation;
|
||||
switch(operation)
|
||||
{
|
||||
case operation_opposite : return 0;
|
||||
case operation_none : return 0;
|
||||
case operation_union : return 1;
|
||||
case operation_intersection : return 2;
|
||||
case operation_blocked : return 3;
|
||||
case operation_continue : return 4;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
inline bool use_operation(Turn const& left, Turn const& right) const
|
||||
{
|
||||
// If they are the same, OK.
|
||||
return operation_order(left) < operation_order(right);
|
||||
}
|
||||
|
||||
inline bool use_distance(Turn const& left, Turn const& right) const
|
||||
{
|
||||
return left.operations[0].fraction == right.operations[0].fraction
|
||||
? use_operation(left, right)
|
||||
: left.operations[0].fraction < right.operations[0].fraction
|
||||
;
|
||||
}
|
||||
|
||||
inline bool operator()(Turn const& left, Turn const& right) const
|
||||
{
|
||||
segment_identifier const& sl = left.operations[0].seg_id;
|
||||
segment_identifier const& sr = right.operations[0].seg_id;
|
||||
|
||||
return sl == sr
|
||||
? use_distance(left, right)
|
||||
: sl < sr
|
||||
;
|
||||
|
||||
}
|
||||
};
|
||||
#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
|
||||
|
||||
|
||||
public :
|
||||
|
||||
static inline bool included(int inside_value)
|
||||
{
|
||||
return following::action_selector
|
||||
<
|
||||
OverlayType, RemoveSpikes
|
||||
>::included(inside_value);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename OutputIterator,
|
||||
typename RobustPolicy,
|
||||
typename Strategy
|
||||
>
|
||||
static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon,
|
||||
detail::overlay::operation_type , // TODO: this parameter might be redundant
|
||||
Turns& turns,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename boost::range_iterator<Turns>::type turn_iterator;
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename boost::range_iterator
|
||||
<
|
||||
typename turn_type::container_type
|
||||
>::type turn_operation_iterator_type;
|
||||
|
||||
typedef following::action_selector<OverlayType, RemoveSpikes> action;
|
||||
|
||||
typename Strategy::template point_in_geometry_strategy
|
||||
<
|
||||
LineString, Polygon
|
||||
>::type const pt_in_poly_strategy
|
||||
= strategy.template get_point_in_geometry_strategy<LineString, Polygon>();
|
||||
|
||||
// Sort intersection points on segments-along-linestring, and distance
|
||||
// (like in enrich is done for poly/poly)
|
||||
// sort turns by Linear seg_id, then by fraction, then
|
||||
// for same ring id: x, u, i, c
|
||||
// for different ring id: c, i, u, x
|
||||
#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
|
||||
std::sort(boost::begin(turns), boost::end(turns), sort_on_segment<turn_type>());
|
||||
#else
|
||||
typedef relate::turns::less<0, relate::turns::less_op_linear_areal_single<0> > turn_less;
|
||||
std::sort(boost::begin(turns), boost::end(turns), turn_less());
|
||||
#endif
|
||||
|
||||
LineStringOut current_piece;
|
||||
geometry::segment_identifier current_segment_id(0, -1, -1, -1);
|
||||
|
||||
// Iterate through all intersection points (they are ordered along the line)
|
||||
bool entered = false;
|
||||
bool first = true;
|
||||
for (turn_iterator it = boost::begin(turns); it != boost::end(turns); ++it)
|
||||
{
|
||||
turn_operation_iterator_type iit = boost::begin(it->operations);
|
||||
|
||||
if (following::was_entered(*it, *iit, first, linestring, polygon, pt_in_poly_strategy))
|
||||
{
|
||||
debug_traverse(*it, *iit, "-> Was entered");
|
||||
entered = true;
|
||||
}
|
||||
|
||||
if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy))
|
||||
{
|
||||
debug_traverse(*it, *iit, "-> Staying inside");
|
||||
|
||||
entered = true;
|
||||
}
|
||||
else if (following::is_entering(*it, *iit))
|
||||
{
|
||||
debug_traverse(*it, *iit, "-> Entering");
|
||||
|
||||
entered = true;
|
||||
action::enter(current_piece, linestring, current_segment_id,
|
||||
iit->seg_id.segment_index, it->point, *iit,
|
||||
strategy, robust_policy,
|
||||
out);
|
||||
}
|
||||
else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy))
|
||||
{
|
||||
debug_traverse(*it, *iit, "-> Leaving");
|
||||
|
||||
entered = false;
|
||||
action::leave(current_piece, linestring, current_segment_id,
|
||||
iit->seg_id.segment_index, it->point, *iit,
|
||||
strategy, robust_policy,
|
||||
out);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (action::is_entered(entered))
|
||||
{
|
||||
detail::copy_segments::copy_segments_linestring
|
||||
<
|
||||
false, RemoveSpikes
|
||||
>::apply(linestring,
|
||||
current_segment_id,
|
||||
static_cast<signed_size_type>(boost::size(linestring) - 1),
|
||||
strategy, robust_policy,
|
||||
current_piece);
|
||||
}
|
||||
|
||||
// Output the last one, if applicable
|
||||
if (::boost::size(current_piece) > 1)
|
||||
{
|
||||
*out++ = current_piece;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
|
||||
@@ -0,0 +1,557 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// Copyright (c) 2014-2017, Oracle and/or its affiliates.
|
||||
|
||||
// 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_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/follow.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/inconsistent_turns_exception.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/turns/debug_turn.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/not_implemented.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
namespace following { namespace linear
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
// follower for linear/linear geometries set operations
|
||||
|
||||
template <typename Turn, typename Operation>
|
||||
static inline bool is_entering(Turn const& turn,
|
||||
Operation const& operation)
|
||||
{
|
||||
if ( turn.method != method_touch && turn.method != method_touch_interior )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return operation.operation == operation_intersection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Turn, typename Operation>
|
||||
static inline bool is_staying_inside(Turn const& turn,
|
||||
Operation const& operation,
|
||||
bool entered)
|
||||
{
|
||||
if ( !entered )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( turn.method != method_equal && turn.method != method_collinear )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return operation.operation == operation_continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Turn, typename Operation>
|
||||
static inline bool is_leaving(Turn const& turn,
|
||||
Operation const& operation,
|
||||
bool entered)
|
||||
{
|
||||
if ( !entered )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( turn.method != method_touch
|
||||
&& turn.method != method_touch_interior
|
||||
&& turn.method != method_equal
|
||||
&& turn.method != method_collinear )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( operation.operation == operation_blocked )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( operation.operation != operation_union )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operation.is_collinear;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Turn, typename Operation>
|
||||
static inline bool is_isolated_point(Turn const& turn,
|
||||
Operation const& operation,
|
||||
bool entered)
|
||||
{
|
||||
if ( entered )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( turn.method == method_none )
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT( operation.operation == operation_continue );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( turn.method == method_crosses )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( turn.method != method_touch && turn.method != method_touch_interior )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( operation.operation == operation_blocked )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( operation.operation != operation_union )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !operation.is_collinear;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LinestringOut,
|
||||
typename Linestring,
|
||||
typename Linear,
|
||||
overlay_type OverlayType,
|
||||
bool FollowIsolatedPoints,
|
||||
bool FollowContinueTurns
|
||||
>
|
||||
class follow_linestring_linear_linestring
|
||||
{
|
||||
protected:
|
||||
// allow spikes (false indicates: do not remove spikes)
|
||||
typedef following::action_selector<OverlayType, false> action;
|
||||
|
||||
template
|
||||
<
|
||||
typename TurnIterator,
|
||||
typename TurnOperationIterator,
|
||||
typename SegmentIdentifier,
|
||||
typename OutputIterator,
|
||||
typename SideStrategy
|
||||
>
|
||||
static inline OutputIterator
|
||||
process_turn(TurnIterator it,
|
||||
TurnOperationIterator op_it,
|
||||
bool& entered,
|
||||
std::size_t& enter_count,
|
||||
Linestring const& linestring,
|
||||
LinestringOut& current_piece,
|
||||
SegmentIdentifier& current_segment_id,
|
||||
OutputIterator oit,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
// We don't rescale linear/linear
|
||||
detail::no_rescale_policy robust_policy;
|
||||
|
||||
if ( is_entering(*it, *op_it) )
|
||||
{
|
||||
detail::turns::debug_turn(*it, *op_it, "-> Entering");
|
||||
|
||||
entered = true;
|
||||
if ( enter_count == 0 )
|
||||
{
|
||||
action::enter(current_piece, linestring,
|
||||
current_segment_id,
|
||||
op_it->seg_id.segment_index,
|
||||
it->point, *op_it, strategy, robust_policy, oit);
|
||||
}
|
||||
++enter_count;
|
||||
}
|
||||
else if ( is_leaving(*it, *op_it, entered) )
|
||||
{
|
||||
detail::turns::debug_turn(*it, *op_it, "-> Leaving");
|
||||
|
||||
--enter_count;
|
||||
if ( enter_count == 0 )
|
||||
{
|
||||
entered = false;
|
||||
action::leave(current_piece, linestring,
|
||||
current_segment_id,
|
||||
op_it->seg_id.segment_index,
|
||||
it->point, *op_it, strategy, robust_policy, oit);
|
||||
}
|
||||
}
|
||||
else if ( FollowIsolatedPoints
|
||||
&& is_isolated_point(*it, *op_it, entered) )
|
||||
{
|
||||
detail::turns::debug_turn(*it, *op_it, "-> Isolated point");
|
||||
|
||||
action::isolated_point(current_piece, linestring,
|
||||
current_segment_id,
|
||||
op_it->seg_id.segment_index,
|
||||
it->point, *op_it, oit);
|
||||
}
|
||||
else if ( FollowContinueTurns
|
||||
&& is_staying_inside(*it, *op_it, entered) )
|
||||
{
|
||||
detail::turns::debug_turn(*it, *op_it, "-> Staying inside");
|
||||
|
||||
entered = true;
|
||||
}
|
||||
return oit;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename SegmentIdentifier,
|
||||
typename OutputIterator,
|
||||
typename SideStrategy
|
||||
>
|
||||
static inline OutputIterator
|
||||
process_end(bool entered,
|
||||
Linestring const& linestring,
|
||||
SegmentIdentifier const& current_segment_id,
|
||||
LinestringOut& current_piece,
|
||||
OutputIterator oit,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
if ( action::is_entered(entered) )
|
||||
{
|
||||
// We don't rescale linear/linear
|
||||
detail::no_rescale_policy robust_policy;
|
||||
|
||||
detail::copy_segments::copy_segments_linestring
|
||||
<
|
||||
false, false // do not reverse; do not remove spikes
|
||||
>::apply(linestring,
|
||||
current_segment_id,
|
||||
static_cast<signed_size_type>(boost::size(linestring) - 1),
|
||||
strategy,
|
||||
robust_policy,
|
||||
current_piece);
|
||||
}
|
||||
|
||||
// Output the last one, if applicable
|
||||
if (::boost::size(current_piece) > 1)
|
||||
{
|
||||
*oit++ = current_piece;
|
||||
}
|
||||
|
||||
return oit;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename TurnIterator, typename OutputIterator, typename SideStrategy>
|
||||
static inline OutputIterator
|
||||
apply(Linestring const& linestring, Linear const&,
|
||||
TurnIterator first, TurnIterator beyond,
|
||||
OutputIterator oit,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
// Iterate through all intersection points (they are
|
||||
// ordered along the each line)
|
||||
|
||||
LinestringOut current_piece;
|
||||
geometry::segment_identifier current_segment_id(0, -1, -1, -1);
|
||||
|
||||
bool entered = false;
|
||||
std::size_t enter_count = 0;
|
||||
|
||||
for (TurnIterator it = first; it != beyond; ++it)
|
||||
{
|
||||
oit = process_turn(it, boost::begin(it->operations),
|
||||
entered, enter_count,
|
||||
linestring,
|
||||
current_piece, current_segment_id,
|
||||
oit,
|
||||
strategy);
|
||||
}
|
||||
|
||||
#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
|
||||
if (enter_count != 0)
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(inconsistent_turns_exception());
|
||||
}
|
||||
#else
|
||||
BOOST_GEOMETRY_ASSERT(enter_count == 0);
|
||||
#endif
|
||||
|
||||
return process_end(entered, linestring,
|
||||
current_segment_id, current_piece,
|
||||
oit,
|
||||
strategy);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LinestringOut,
|
||||
typename MultiLinestring,
|
||||
typename Linear,
|
||||
overlay_type OverlayType,
|
||||
bool FollowIsolatedPoints,
|
||||
bool FollowContinueTurns
|
||||
>
|
||||
class follow_multilinestring_linear_linestring
|
||||
: follow_linestring_linear_linestring
|
||||
<
|
||||
LinestringOut,
|
||||
typename boost::range_value<MultiLinestring>::type,
|
||||
Linear,
|
||||
OverlayType,
|
||||
FollowIsolatedPoints,
|
||||
FollowContinueTurns
|
||||
>
|
||||
{
|
||||
protected:
|
||||
typedef typename boost::range_value<MultiLinestring>::type Linestring;
|
||||
|
||||
typedef follow_linestring_linear_linestring
|
||||
<
|
||||
LinestringOut, Linestring, Linear,
|
||||
OverlayType, FollowIsolatedPoints, FollowContinueTurns
|
||||
> Base;
|
||||
|
||||
typedef following::action_selector<OverlayType> action;
|
||||
|
||||
typedef typename boost::range_iterator
|
||||
<
|
||||
MultiLinestring const
|
||||
>::type linestring_iterator;
|
||||
|
||||
|
||||
template <typename OutputIt, overlay_type OT>
|
||||
struct copy_linestrings_in_range
|
||||
{
|
||||
static inline OutputIt
|
||||
apply(linestring_iterator, linestring_iterator, OutputIt oit)
|
||||
{
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt>
|
||||
struct copy_linestrings_in_range<OutputIt, overlay_difference>
|
||||
{
|
||||
static inline OutputIt
|
||||
apply(linestring_iterator first, linestring_iterator beyond,
|
||||
OutputIt oit)
|
||||
{
|
||||
for (linestring_iterator ls_it = first; ls_it != beyond; ++ls_it)
|
||||
{
|
||||
LinestringOut line_out;
|
||||
geometry::convert(*ls_it, line_out);
|
||||
*oit++ = line_out;
|
||||
}
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TurnIterator>
|
||||
static inline signed_size_type get_multi_index(TurnIterator it)
|
||||
{
|
||||
return boost::begin(it->operations)->seg_id.multi_index;
|
||||
}
|
||||
|
||||
class has_other_multi_id
|
||||
{
|
||||
private:
|
||||
signed_size_type m_multi_id;
|
||||
|
||||
public:
|
||||
has_other_multi_id(signed_size_type multi_id)
|
||||
: m_multi_id(multi_id) {}
|
||||
|
||||
template <typename Turn>
|
||||
bool operator()(Turn const& turn) const
|
||||
{
|
||||
return boost::begin(turn.operations)->seg_id.multi_index
|
||||
!= m_multi_id;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename TurnIterator, typename OutputIterator, typename SideStrategy>
|
||||
static inline OutputIterator
|
||||
apply(MultiLinestring const& multilinestring, Linear const& linear,
|
||||
TurnIterator first, TurnIterator beyond,
|
||||
OutputIterator oit,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT( first != beyond );
|
||||
|
||||
typedef copy_linestrings_in_range
|
||||
<
|
||||
OutputIterator, OverlayType
|
||||
> copy_linestrings;
|
||||
|
||||
linestring_iterator ls_first = boost::begin(multilinestring);
|
||||
linestring_iterator ls_beyond = boost::end(multilinestring);
|
||||
|
||||
// Iterate through all intersection points (they are
|
||||
// ordered along the each linestring)
|
||||
|
||||
signed_size_type current_multi_id = get_multi_index(first);
|
||||
|
||||
oit = copy_linestrings::apply(ls_first,
|
||||
ls_first + current_multi_id,
|
||||
oit);
|
||||
|
||||
TurnIterator per_ls_next = first;
|
||||
do {
|
||||
TurnIterator per_ls_current = per_ls_next;
|
||||
|
||||
// find turn with different multi-index
|
||||
per_ls_next = std::find_if(per_ls_current, beyond,
|
||||
has_other_multi_id(current_multi_id));
|
||||
|
||||
oit = Base::apply(*(ls_first + current_multi_id),
|
||||
linear, per_ls_current, per_ls_next, oit, strategy);
|
||||
|
||||
signed_size_type next_multi_id = -1;
|
||||
linestring_iterator ls_next = ls_beyond;
|
||||
if ( per_ls_next != beyond )
|
||||
{
|
||||
next_multi_id = get_multi_index(per_ls_next);
|
||||
ls_next = ls_first + next_multi_id;
|
||||
}
|
||||
oit = copy_linestrings::apply(ls_first + current_multi_id + 1,
|
||||
ls_next,
|
||||
oit);
|
||||
|
||||
current_multi_id = next_multi_id;
|
||||
}
|
||||
while ( per_ls_next != beyond );
|
||||
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LinestringOut,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
overlay_type OverlayType,
|
||||
bool FollowIsolatedPoints,
|
||||
bool FollowContinueTurns,
|
||||
typename TagOut = typename tag<LinestringOut>::type,
|
||||
typename TagIn1 = typename tag<Geometry1>::type
|
||||
>
|
||||
struct follow
|
||||
: not_implemented<LinestringOut, Geometry1>
|
||||
{};
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LinestringOut,
|
||||
typename Linestring,
|
||||
typename Linear,
|
||||
overlay_type OverlayType,
|
||||
bool FollowIsolatedPoints,
|
||||
bool FollowContinueTurns
|
||||
>
|
||||
struct follow
|
||||
<
|
||||
LinestringOut, Linestring, Linear,
|
||||
OverlayType, FollowIsolatedPoints, FollowContinueTurns,
|
||||
linestring_tag, linestring_tag
|
||||
> : follow_linestring_linear_linestring
|
||||
<
|
||||
LinestringOut, Linestring, Linear,
|
||||
OverlayType, FollowIsolatedPoints, FollowContinueTurns
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LinestringOut,
|
||||
typename MultiLinestring,
|
||||
typename Linear,
|
||||
overlay_type OverlayType,
|
||||
bool FollowIsolatedPoints,
|
||||
bool FollowContinueTurns
|
||||
>
|
||||
struct follow
|
||||
<
|
||||
LinestringOut, MultiLinestring, Linear,
|
||||
OverlayType, FollowIsolatedPoints, FollowContinueTurns,
|
||||
linestring_tag, multi_linestring_tag
|
||||
> : follow_multilinestring_linear_linestring
|
||||
<
|
||||
LinestringOut, MultiLinestring, Linear,
|
||||
OverlayType, FollowIsolatedPoints, FollowContinueTurns
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
|
||||
}} // namespace following::linear
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP
|
||||
@@ -0,0 +1,173 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/mpl/if.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
|
||||
#include <boost/geometry/policies/robustness/robust_point_type.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace get_intersection_points
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Point1,
|
||||
typename Point2,
|
||||
typename TurnInfo
|
||||
>
|
||||
struct get_turn_without_info
|
||||
{
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename Strategy,
|
||||
typename RobustPolicy,
|
||||
typename OutputIterator
|
||||
>
|
||||
static inline OutputIterator apply(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
TurnInfo const& ,
|
||||
Strategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out)
|
||||
{
|
||||
typedef typename TurnInfo::point_type turn_point_type;
|
||||
|
||||
typedef policies::relate::segments_intersection_points
|
||||
<
|
||||
segment_intersection_points
|
||||
<
|
||||
turn_point_type,
|
||||
typename geometry::segment_ratio_type
|
||||
<
|
||||
turn_point_type, RobustPolicy
|
||||
>::type
|
||||
>
|
||||
> policy_type;
|
||||
|
||||
typedef model::referring_segment<Point1 const> segment_type1;
|
||||
typedef model::referring_segment<Point2 const> segment_type2;
|
||||
Point1 const& pi = range_p.at(0);
|
||||
Point1 const& pj = range_p.at(1);
|
||||
Point2 const& qi = range_q.at(0);
|
||||
Point2 const& qj = range_q.at(1);
|
||||
segment_type1 p1(pi, pj);
|
||||
segment_type2 q1(qi, qj);
|
||||
|
||||
typedef typename geometry::robust_point_type
|
||||
<
|
||||
Point1, RobustPolicy
|
||||
>::type robust_point_type;
|
||||
|
||||
robust_point_type pi_rob, pj_rob, qi_rob, qj_rob;
|
||||
geometry::recalculate(pi_rob, pi, robust_policy);
|
||||
geometry::recalculate(pj_rob, pj, robust_policy);
|
||||
geometry::recalculate(qi_rob, qi, robust_policy);
|
||||
geometry::recalculate(qj_rob, qj, robust_policy);
|
||||
typename policy_type::return_type result
|
||||
= strategy.apply(p1, q1, policy_type(), robust_policy,
|
||||
pi_rob, pj_rob, qi_rob, qj_rob);
|
||||
|
||||
for (std::size_t i = 0; i < result.count; i++)
|
||||
{
|
||||
TurnInfo tp;
|
||||
geometry::convert(result.intersections[i], tp.point);
|
||||
*out++ = tp;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::get_intersection_points
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename RobustPolicy,
|
||||
typename Turns,
|
||||
typename Strategy
|
||||
>
|
||||
inline void get_intersection_points(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
Turns& turns,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
concepts::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>();
|
||||
|
||||
typedef detail::get_intersection_points::get_turn_without_info
|
||||
<
|
||||
typename point_type<Geometry1>::type,
|
||||
typename point_type<Geometry2>::type,
|
||||
typename boost::range_value<Turns>::type
|
||||
> TurnPolicy;
|
||||
|
||||
detail::get_turns::no_interrupt_policy interrupt_policy;
|
||||
|
||||
boost::mpl::if_c
|
||||
<
|
||||
reverse_dispatch<Geometry1, Geometry2>::type::value,
|
||||
dispatch::get_turns_reversed
|
||||
<
|
||||
typename tag<Geometry1>::type,
|
||||
typename tag<Geometry2>::type,
|
||||
Geometry1, Geometry2,
|
||||
false, false,
|
||||
TurnPolicy
|
||||
>,
|
||||
dispatch::get_turns
|
||||
<
|
||||
typename tag<Geometry1>::type,
|
||||
typename tag<Geometry2>::type,
|
||||
Geometry1, Geometry2,
|
||||
false, false,
|
||||
TurnPolicy
|
||||
>
|
||||
>::type::apply(
|
||||
0, geometry1,
|
||||
1, geometry2,
|
||||
strategy,
|
||||
robust_policy,
|
||||
turns, interrupt_policy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP
|
||||
@@ -0,0 +1,103 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/strategies/intersection_strategies.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Get relative order
|
||||
\details Can indicate which of two segments R and S,
|
||||
both crossing a common segment P, comes first.
|
||||
If the two segments cross P very close (e.g. in a spike),
|
||||
the distance between the intersection points can be zero,
|
||||
but we still need to know which comes first.
|
||||
Therefore, it is useful that using sides we are able to discover this.
|
||||
*/
|
||||
struct get_relative_order
|
||||
{
|
||||
template <typename Point, typename SideStrategy>
|
||||
static inline int value_via_product(Point const& ti, Point const& tj,
|
||||
Point const& ui, Point const& uj, int factor,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
int const side_ti_u = strategy.apply(ti, tj, ui);
|
||||
int const side_tj_u = strategy.apply(ti, tj, uj);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER
|
||||
std::cout << (factor == 1 ? " r//s " : " s//r ")
|
||||
<< side_ti_u << " / " << side_tj_u;
|
||||
#endif
|
||||
|
||||
return side_ti_u * side_tj_u >= 0
|
||||
? factor * (side_ti_u != 0 ? side_ti_u : side_tj_u)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename Point1, typename SideStrategy>
|
||||
static inline int apply(
|
||||
Point1 const& pi, Point1 const& pj,
|
||||
Point1 const& ri, Point1 const& rj,
|
||||
Point1 const& si, Point1 const& sj,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
int const side_ri_p = strategy.apply(pi, pj, ri);
|
||||
int const side_si_p = strategy.apply(pi, pj, si);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER
|
||||
int const side_rj_p = strategy::apply(pi, pj, rj);
|
||||
int const side_sj_p = strategy::apply(pi, pj, sj);
|
||||
std::cout << "r//p: " << side_ri_p << " / " << side_rj_p;
|
||||
std::cout << " s//p: " << side_si_p << " / " << side_sj_p;
|
||||
#endif
|
||||
|
||||
int value = value_via_product(si, sj, ri, rj, 1, strategy);
|
||||
if (value == 0)
|
||||
{
|
||||
value = value_via_product(ri, rj, si, sj, -1, strategy);
|
||||
}
|
||||
|
||||
int const order = side_ri_p * side_ri_p * side_si_p * value;
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER
|
||||
std::cout
|
||||
<< " o: " << order
|
||||
<< std::endl << std::endl;
|
||||
#endif
|
||||
|
||||
return order;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP
|
||||
@@ -0,0 +1,123 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
|
||||
|
||||
|
||||
#include <boost/range.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/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/geometries/concepts/check.hpp>
|
||||
#include <boost/geometry/util/range.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template<typename Tag>
|
||||
struct get_ring
|
||||
{};
|
||||
|
||||
// A range of rings (multi-ring but that does not exist)
|
||||
// gets the "void" tag and is dispatched here.
|
||||
template<>
|
||||
struct get_ring<void>
|
||||
{
|
||||
template<typename Range>
|
||||
static inline typename boost::range_value<Range>::type const&
|
||||
apply(ring_identifier const& id, Range const& container)
|
||||
{
|
||||
return range::at(container, id.multi_index);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct get_ring<ring_tag>
|
||||
{
|
||||
template<typename Ring>
|
||||
static inline Ring const& apply(ring_identifier const& , Ring const& ring)
|
||||
{
|
||||
return ring;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct get_ring<box_tag>
|
||||
{
|
||||
template<typename Box>
|
||||
static inline Box const& apply(ring_identifier const& ,
|
||||
Box const& box)
|
||||
{
|
||||
return box;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct get_ring<polygon_tag>
|
||||
{
|
||||
template<typename Polygon>
|
||||
static inline typename ring_return_type<Polygon const>::type const apply(
|
||||
ring_identifier const& id,
|
||||
Polygon const& polygon)
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT
|
||||
(
|
||||
id.ring_index >= -1
|
||||
&& id.ring_index < int(boost::size(interior_rings(polygon)))
|
||||
);
|
||||
return id.ring_index < 0
|
||||
? exterior_ring(polygon)
|
||||
: range::at(interior_rings(polygon), id.ring_index);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct get_ring<multi_polygon_tag>
|
||||
{
|
||||
template<typename MultiPolygon>
|
||||
static inline typename ring_type<MultiPolygon>::type const& apply(
|
||||
ring_identifier const& id,
|
||||
MultiPolygon const& multi_polygon)
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT
|
||||
(
|
||||
id.multi_index >= 0
|
||||
&& id.multi_index < int(boost::size(multi_polygon))
|
||||
);
|
||||
return get_ring<polygon_tag>::apply(id,
|
||||
range::at(multi_polygon, id.multi_index));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,646 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2013, 2014, 2017, 2018.
|
||||
// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
|
||||
|
||||
namespace boost { namespace geometry {
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay {
|
||||
|
||||
// SEGMENT_INTERSECTION RESULT
|
||||
|
||||
// C H0 H1 A0 A1 O IP1 IP2
|
||||
|
||||
// D0 and D1 == 0
|
||||
|
||||
// |--------> 2 0 0 0 0 F i/i x/x
|
||||
// |-------->
|
||||
//
|
||||
// |--------> 2 0 0 0 0 T i/x x/i
|
||||
// <--------|
|
||||
//
|
||||
// |-----> 1 0 0 0 0 T x/x
|
||||
// <-----|
|
||||
//
|
||||
|
||||
// |---------> 2 0 0 0 1 T i/x x/i
|
||||
// <----|
|
||||
//
|
||||
// |---------> 2 0 0 0 0 F i/i x/x
|
||||
// |---->
|
||||
//
|
||||
// |---------> 2 0 0 -1 1 F i/i u/x
|
||||
// |---->
|
||||
//
|
||||
// |---------> 2 0 0 -1 0 T i/x u/i
|
||||
// <----|
|
||||
|
||||
// |-------> 2 0 0 1 -1 F and i/i x/u
|
||||
// |-------> 2 0 0 -1 1 F symetric i/i u/x
|
||||
// |------->
|
||||
//
|
||||
// |-------> 2 0 0 -1 -1 T i/u u/i
|
||||
// <-------|
|
||||
//
|
||||
// |-------> 2 0 0 1 1 T i/x x/i
|
||||
// <-------|
|
||||
//
|
||||
// |--------> 2 0 0 -1 1 F i/i u/x
|
||||
// |---->
|
||||
//
|
||||
// |--------> 2 0 0 -1 1 T i/x u/i
|
||||
// <----|
|
||||
|
||||
// |-----> 1 -1 -1 -1 -1 T u/u
|
||||
// <-----|
|
||||
//
|
||||
// |-----> 1 -1 0 -1 0 F and u/x
|
||||
// |-----> 1 0 -1 0 -1 F symetric x/u
|
||||
// |----->
|
||||
|
||||
// D0 or D1 != 0
|
||||
|
||||
// ^
|
||||
// |
|
||||
// + 1 -1 1 -1 1 F and u/x (P is vertical)
|
||||
// |--------> 1 1 -1 1 -1 F symetric x/u (P is horizontal)
|
||||
// ^
|
||||
// |
|
||||
// +
|
||||
//
|
||||
// +
|
||||
// |
|
||||
// v
|
||||
// |--------> 1 1 1 1 1 F x/x (P is vertical)
|
||||
//
|
||||
// ^
|
||||
// |
|
||||
// +
|
||||
// |--------> 1 -1 -1 -1 -1 F u/u (P is vertical)
|
||||
//
|
||||
// ^
|
||||
// |
|
||||
// +
|
||||
// |--------> 1 0 -1 0 -1 F u/u (P is vertical)
|
||||
//
|
||||
// +
|
||||
// |
|
||||
// v
|
||||
// |--------> 1 0 1 0 1 F u/x (P is vertical)
|
||||
//
|
||||
|
||||
class linear_intersections
|
||||
{
|
||||
public:
|
||||
template <typename Point1, typename Point2, typename IntersectionResult, typename EqPPStrategy>
|
||||
linear_intersections(Point1 const& pi,
|
||||
Point2 const& qi,
|
||||
IntersectionResult const& result,
|
||||
bool is_p_last, bool is_q_last,
|
||||
EqPPStrategy const& strategy)
|
||||
{
|
||||
int arrival_a = result.template get<1>().arrival[0];
|
||||
int arrival_b = result.template get<1>().arrival[1];
|
||||
bool same_dirs = result.template get<1>().dir_a == 0
|
||||
&& result.template get<1>().dir_b == 0;
|
||||
|
||||
if ( same_dirs )
|
||||
{
|
||||
if ( result.template get<0>().count == 2 )
|
||||
{
|
||||
if ( ! result.template get<1>().opposite )
|
||||
{
|
||||
ips[0].p_operation = operation_intersection;
|
||||
ips[0].q_operation = operation_intersection;
|
||||
ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
|
||||
ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
|
||||
|
||||
ips[0].is_pi
|
||||
= equals::equals_point_point(
|
||||
pi, result.template get<0>().intersections[0], strategy);
|
||||
ips[0].is_qi
|
||||
= equals::equals_point_point(
|
||||
qi, result.template get<0>().intersections[0], strategy);
|
||||
ips[1].is_pj = arrival_a != -1;
|
||||
ips[1].is_qj = arrival_b != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ips[0].p_operation = operation_intersection;
|
||||
ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
|
||||
ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
|
||||
ips[1].q_operation = operation_intersection;
|
||||
|
||||
ips[0].is_pi = arrival_b != 1;
|
||||
ips[0].is_qj = arrival_b != -1;
|
||||
ips[1].is_pj = arrival_a != -1;
|
||||
ips[1].is_qi = arrival_a != 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT(result.template get<0>().count == 1);
|
||||
ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
|
||||
ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
|
||||
|
||||
ips[0].is_pi = arrival_a == -1;
|
||||
ips[0].is_qi = arrival_b == -1;
|
||||
ips[0].is_pj = arrival_a == 0;
|
||||
ips[0].is_qj = arrival_b == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last);
|
||||
ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last);
|
||||
|
||||
ips[0].is_pi = arrival_a == -1;
|
||||
ips[0].is_qi = arrival_b == -1;
|
||||
ips[0].is_pj = arrival_a == 1;
|
||||
ips[0].is_qj = arrival_b == 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct ip_info
|
||||
{
|
||||
inline ip_info()
|
||||
: p_operation(operation_none), q_operation(operation_none)
|
||||
, is_pi(false), is_pj(false), is_qi(false), is_qj(false)
|
||||
{}
|
||||
|
||||
operation_type p_operation, q_operation;
|
||||
bool is_pi, is_pj, is_qi, is_qj;
|
||||
};
|
||||
|
||||
template <std::size_t I>
|
||||
ip_info const& get() const
|
||||
{
|
||||
BOOST_STATIC_ASSERT(I < 2);
|
||||
return ips[I];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// only if collinear (same_dirs)
|
||||
static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last)
|
||||
{
|
||||
if ( arrival == 1 )
|
||||
return operation_blocked;
|
||||
else if ( arrival == -1 )
|
||||
return operation_union;
|
||||
else
|
||||
return is_last ? operation_blocked : operation_union;
|
||||
//return operation_blocked;
|
||||
}
|
||||
|
||||
// only if not collinear (!same_dirs)
|
||||
static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last)
|
||||
{
|
||||
if ( arrival == 1 )
|
||||
//return operation_blocked;
|
||||
return is_last ? operation_blocked : operation_union;
|
||||
else
|
||||
return operation_union;
|
||||
}
|
||||
|
||||
ip_info ips[2];
|
||||
};
|
||||
|
||||
template <bool EnableFirst, bool EnableLast>
|
||||
struct get_turn_info_for_endpoint
|
||||
{
|
||||
typedef std::pair<operation_type, operation_type> operations_pair;
|
||||
|
||||
BOOST_STATIC_ASSERT(EnableFirst || EnableLast);
|
||||
|
||||
template<typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutputIterator,
|
||||
typename EqPPStrategy
|
||||
>
|
||||
static inline bool apply(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionInfo const& inters,
|
||||
method_type /*method*/,
|
||||
OutputIterator out,
|
||||
EqPPStrategy const& strategy)
|
||||
{
|
||||
std::size_t ip_count = inters.i_info().count;
|
||||
// no intersection points
|
||||
if (ip_count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! range_p.is_first_segment()
|
||||
&& ! range_q.is_first_segment()
|
||||
&& ! range_p.is_last_segment()
|
||||
&& ! range_q.is_last_segment())
|
||||
{
|
||||
// Not an end-point from segment p or q
|
||||
return false;
|
||||
}
|
||||
|
||||
linear_intersections intersections(range_p.at(0),
|
||||
range_q.at(0),
|
||||
inters.result(),
|
||||
range_p.is_last_segment(),
|
||||
range_q.is_last_segment(),
|
||||
strategy);
|
||||
|
||||
bool append0_last
|
||||
= analyse_segment_and_assign_ip(range_p, range_q,
|
||||
intersections.template get<0>(),
|
||||
tp_model, inters, 0, out);
|
||||
|
||||
// NOTE: opposite && ip_count == 1 may be true!
|
||||
bool opposite = inters.d_info().opposite;
|
||||
|
||||
// don't ignore only for collinear opposite
|
||||
bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite );
|
||||
|
||||
if ( intersections.template get<1>().p_operation == operation_none )
|
||||
return result_ignore_ip0;
|
||||
|
||||
bool append1_last
|
||||
= analyse_segment_and_assign_ip(range_p, range_q,
|
||||
intersections.template get<1>(),
|
||||
tp_model, inters, 1, out);
|
||||
|
||||
// don't ignore only for collinear opposite
|
||||
bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/;
|
||||
|
||||
return result_ignore_ip0 || result_ignore_ip1;
|
||||
}
|
||||
|
||||
template <typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutputIterator>
|
||||
static inline
|
||||
bool analyse_segment_and_assign_ip(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
linear_intersections::ip_info const& ip_info,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionInfo const& inters,
|
||||
unsigned int ip_index,
|
||||
OutputIterator out)
|
||||
{
|
||||
// TODO - calculate first/last only if needed
|
||||
bool is_p_first_ip = range_p.is_first_segment() && ip_info.is_pi;
|
||||
bool is_p_last_ip = range_p.is_last_segment() && ip_info.is_pj;
|
||||
bool is_q_first_ip = range_q.is_first_segment() && ip_info.is_qi;
|
||||
bool is_q_last_ip = range_q.is_last_segment() && ip_info.is_qj;
|
||||
bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip);
|
||||
bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip);
|
||||
|
||||
operation_type p_operation = ip_info.p_operation;
|
||||
operation_type q_operation = ip_info.q_operation;
|
||||
|
||||
if ( append_first || append_last )
|
||||
{
|
||||
bool handled = handle_internal<0>(range_p, range_q,
|
||||
is_p_first_ip, is_p_last_ip,
|
||||
is_q_first_ip, is_q_last_ip,
|
||||
ip_info.is_qi, ip_info.is_qj,
|
||||
tp_model, inters, ip_index,
|
||||
p_operation, q_operation);
|
||||
if ( !handled )
|
||||
{
|
||||
// Reverse p/q
|
||||
handle_internal<1>(range_q, range_p,
|
||||
is_q_first_ip, is_q_last_ip,
|
||||
is_p_first_ip, is_p_last_ip,
|
||||
ip_info.is_pi, ip_info.is_pj,
|
||||
tp_model, inters, ip_index,
|
||||
q_operation, p_operation);
|
||||
}
|
||||
|
||||
if ( p_operation != operation_none )
|
||||
{
|
||||
method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj,
|
||||
ip_info.is_qi, ip_info.is_qj);
|
||||
turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip);
|
||||
turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip);
|
||||
|
||||
// handle spikes
|
||||
|
||||
// P is spike and should be handled
|
||||
if (ip_info.is_pj // this check is redundant (also in is_spike_p) but faster
|
||||
&& inters.i_info().count == 2
|
||||
&& inters.is_spike_p() )
|
||||
{
|
||||
assign(inters.result(), ip_index, method, operation_blocked, q_operation,
|
||||
p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
|
||||
assign(inters.result(), ip_index, method, operation_intersection, q_operation,
|
||||
p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
|
||||
}
|
||||
// Q is spike and should be handled
|
||||
else if (ip_info.is_qj // this check is redundant (also in is_spike_q) but faster
|
||||
&& inters.i_info().count == 2
|
||||
&& inters.is_spike_q() )
|
||||
{
|
||||
assign(inters.result(), ip_index, method, p_operation, operation_blocked,
|
||||
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
|
||||
assign(inters.result(), ip_index, method, p_operation, operation_intersection,
|
||||
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
|
||||
}
|
||||
// no spikes
|
||||
else
|
||||
{
|
||||
assign(inters.result(), ip_index, method, p_operation, q_operation,
|
||||
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return append_last;
|
||||
}
|
||||
|
||||
// TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment
|
||||
// however now it's lazily calculated and then it would be always calculated
|
||||
|
||||
template<std::size_t G1Index,
|
||||
typename UniqueRange1,
|
||||
typename UniqueRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo
|
||||
>
|
||||
static inline bool handle_internal(UniqueRange1 const& range1,
|
||||
UniqueRange2 const& range2,
|
||||
bool first1, bool last1, bool first2, bool last2,
|
||||
bool ip_i2, bool ip_j2, TurnInfo const& tp_model,
|
||||
IntersectionInfo const& inters, unsigned int ip_index,
|
||||
operation_type & op1, operation_type & op2)
|
||||
{
|
||||
boost::ignore_unused(ip_index, tp_model);
|
||||
|
||||
typename IntersectionInfo::side_strategy_type const& sides
|
||||
= inters.get_side_strategy();
|
||||
|
||||
if ( !first2 && !last2 )
|
||||
{
|
||||
if ( first1 )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
|
||||
// may this give false positives for INTs?
|
||||
typename IntersectionResult::point_type const&
|
||||
inters_pt = inters.i_info().intersections[ip_index];
|
||||
BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
|
||||
BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
|
||||
#endif
|
||||
if ( ip_i2 )
|
||||
{
|
||||
// don't output this IP - for the first point of other geometry segment
|
||||
op1 = operation_none;
|
||||
op2 = operation_none;
|
||||
return true;
|
||||
}
|
||||
else if ( ip_j2 )
|
||||
{
|
||||
int const side_pj_q2 = sides.apply(range2.at(1), range2.at(2), range1.at(1));
|
||||
int const side_pj_q1 = sides.apply(range2.at(0), range2.at(1), range1.at(1));
|
||||
int const side_qk_q1 = sides.apply(range2.at(0), range2.at(1), range2.at(2));
|
||||
|
||||
operations_pair operations = operations_of_equal(side_pj_q2, side_pj_q1, side_qk_q1);
|
||||
|
||||
// TODO: must the above be calculated?
|
||||
// wouldn't it be enough to check if segments are collinear?
|
||||
|
||||
if ( operations_both(operations, operation_continue) )
|
||||
{
|
||||
if ( op1 != operation_union
|
||||
|| op2 != operation_union
|
||||
|| ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
|
||||
{
|
||||
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
|
||||
bool opposite = inters.d_info().opposite;
|
||||
|
||||
op1 = operation_intersection;
|
||||
op2 = opposite ? operation_union : operation_intersection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
|
||||
//op1 = operation_union;
|
||||
//op2 = operation_union;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// else do nothing - shouldn't be handled this way
|
||||
}
|
||||
else if ( last1 )
|
||||
{
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
|
||||
// may this give false positives for INTs?
|
||||
typename IntersectionResult::point_type const&
|
||||
inters_pt = inters.i_info().intersections[ip_index];
|
||||
BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
|
||||
BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
|
||||
#endif
|
||||
if ( ip_i2 )
|
||||
{
|
||||
// don't output this IP - for the first point of other geometry segment
|
||||
op1 = operation_none;
|
||||
op2 = operation_none;
|
||||
return true;
|
||||
}
|
||||
else if ( ip_j2 )
|
||||
{
|
||||
int const side_pi_q2 = sides.apply(range2.at(1), range2.at(2), range1.at(0));
|
||||
int const side_pi_q1 = sides.apply(range2.at(0), range2.at(1), range1.at(0));
|
||||
int const side_qk_q1 = sides.apply(range2.at(0), range2.at(1), range2.at(2));
|
||||
|
||||
operations_pair operations = operations_of_equal(side_pi_q2, side_pi_q1, side_qk_q1);
|
||||
|
||||
// TODO: must the above be calculated?
|
||||
// wouldn't it be enough to check if segments are collinear?
|
||||
|
||||
if ( operations_both(operations, operation_continue) )
|
||||
{
|
||||
if ( op1 != operation_blocked
|
||||
|| op2 != operation_union
|
||||
|| ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
|
||||
{
|
||||
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
|
||||
bool second_going_out = inters.i_info().count > 1;
|
||||
|
||||
op1 = operation_blocked;
|
||||
op2 = second_going_out ? operation_union : operation_intersection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
|
||||
//op1 = operation_blocked;
|
||||
//op2 = operation_union;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// else do nothing - shouldn't be handled this way
|
||||
}
|
||||
// else do nothing - shouldn't be handled this way
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj)
|
||||
{
|
||||
if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) )
|
||||
return method_touch;
|
||||
else
|
||||
return method_touch_interior;
|
||||
}
|
||||
|
||||
static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j)
|
||||
{
|
||||
return is_ip_first_i ? position_front :
|
||||
( is_ip_last_j ? position_back : position_middle );
|
||||
}
|
||||
|
||||
template <typename IntersectionResult,
|
||||
typename TurnInfo,
|
||||
typename OutputIterator>
|
||||
static inline void assign(IntersectionResult const& result,
|
||||
unsigned int ip_index,
|
||||
method_type method,
|
||||
operation_type op0, operation_type op1,
|
||||
turn_position pos0, turn_position pos1,
|
||||
bool is_p_first_ip, bool is_q_first_ip,
|
||||
bool is_p_spike, bool is_q_spike,
|
||||
TurnInfo const& tp_model,
|
||||
OutputIterator out)
|
||||
{
|
||||
TurnInfo tp = tp_model;
|
||||
|
||||
//geometry::convert(ip, tp.point);
|
||||
//tp.method = method;
|
||||
base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index);
|
||||
|
||||
tp.operations[0].operation = op0;
|
||||
tp.operations[1].operation = op1;
|
||||
tp.operations[0].position = pos0;
|
||||
tp.operations[1].position = pos1;
|
||||
|
||||
if ( result.template get<0>().count > 1 )
|
||||
{
|
||||
// NOTE: is_collinear is NOT set for the first endpoint
|
||||
// for which there is no preceding segment
|
||||
|
||||
//BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
|
||||
if ( ! is_p_first_ip )
|
||||
{
|
||||
tp.operations[0].is_collinear = op0 != operation_intersection
|
||||
|| is_p_spike;
|
||||
}
|
||||
|
||||
if ( ! is_q_first_ip )
|
||||
{
|
||||
tp.operations[1].is_collinear = op1 != operation_intersection
|
||||
|| is_q_spike;
|
||||
}
|
||||
}
|
||||
else //if ( result.template get<0>().count == 1 )
|
||||
{
|
||||
if ( op0 == operation_blocked && op1 == operation_intersection )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
else if ( op0 == operation_intersection && op1 == operation_blocked )
|
||||
{
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
}
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
|
||||
static inline operations_pair operations_of_equal(int side_px_q2,
|
||||
int side_px_q1,
|
||||
int side_qk_q1)
|
||||
{
|
||||
// If px (pi or pj) is collinear with qj-qk (q2), they continue collinearly.
|
||||
// This can be on either side of q1, or collinear
|
||||
// The second condition checks if they do not continue
|
||||
// oppositely
|
||||
if (side_px_q2 == 0 && side_px_q1 == side_qk_q1)
|
||||
{
|
||||
return std::make_pair(operation_continue, operation_continue);
|
||||
}
|
||||
|
||||
// If they turn to same side (not opposite sides)
|
||||
if ( ! base_turn_handler::opposite(side_px_q1, side_qk_q1) )
|
||||
{
|
||||
// If px is left of q2 or collinear: p: union, q: intersection
|
||||
if (side_px_q2 != -1 )
|
||||
{
|
||||
return std::make_pair(operation_union, operation_intersection);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(operation_intersection, operation_union);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// They turn opposite sides. If p turns left (or collinear),
|
||||
// p: union, q: intersection
|
||||
if (side_px_q1 != -1 )
|
||||
{
|
||||
return std::make_pair(operation_union, operation_intersection);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(operation_intersection, operation_union);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool operations_both(operations_pair const& operations,
|
||||
operation_type const op)
|
||||
{
|
||||
return operations.first == op && operations.second == op;
|
||||
}
|
||||
|
||||
static inline bool operations_combination(operations_pair const& operations,
|
||||
operation_type const op1,
|
||||
operation_type const op2)
|
||||
{
|
||||
return ( operations.first == op1 && operations.second == op2 )
|
||||
|| ( operations.first == op2 && operations.second == op1 );
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
|
||||
@@ -0,0 +1,535 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
|
||||
// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/detail/direction_code.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/recalculate.hpp>
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp> // referring_segment
|
||||
#include <boost/geometry/policies/relate/direction.hpp>
|
||||
#include <boost/geometry/policies/relate/intersection_points.hpp>
|
||||
#include <boost/geometry/policies/relate/tupled.hpp>
|
||||
#include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
|
||||
#include <boost/geometry/strategies/intersection_result.hpp>
|
||||
|
||||
namespace boost { namespace geometry {
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay {
|
||||
|
||||
enum turn_position { position_middle, position_front, position_back };
|
||||
|
||||
template <typename Point, typename SegmentRatio>
|
||||
struct turn_operation_linear
|
||||
: public turn_operation<Point, SegmentRatio>
|
||||
{
|
||||
turn_operation_linear()
|
||||
: position(position_middle)
|
||||
, is_collinear(false)
|
||||
{}
|
||||
|
||||
turn_position position;
|
||||
bool is_collinear; // valid only for Linear geometry
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename TurnPointCSTag,
|
||||
typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename SideStrategy
|
||||
>
|
||||
struct side_calculator
|
||||
{
|
||||
typedef typename UniqueSubRange1::point_type point1_type;
|
||||
typedef typename UniqueSubRange2::point_type point2_type;
|
||||
|
||||
inline side_calculator(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
SideStrategy const& side_strategy)
|
||||
: m_side_strategy(side_strategy)
|
||||
, m_range_p(range_p)
|
||||
, m_range_q(range_q)
|
||||
{}
|
||||
|
||||
inline int pk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_pk()); }
|
||||
inline int pk_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pk()); }
|
||||
inline int qk_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qk()); }
|
||||
inline int qk_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_qk()); }
|
||||
|
||||
inline int pk_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pk()); }
|
||||
inline int qk_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qk()); }
|
||||
|
||||
// Necessary when rescaling turns off:
|
||||
inline int qj_wrt_p1() const { return m_side_strategy.apply(get_pi(), get_pj(), get_qj()); }
|
||||
inline int qj_wrt_p2() const { return m_side_strategy.apply(get_pj(), get_pk(), get_qj()); }
|
||||
inline int pj_wrt_q1() const { return m_side_strategy.apply(get_qi(), get_qj(), get_pj()); }
|
||||
inline int pj_wrt_q2() const { return m_side_strategy.apply(get_qj(), get_qk(), get_pj()); }
|
||||
|
||||
inline point1_type const& get_pi() const { return m_range_p.at(0); }
|
||||
inline point1_type const& get_pj() const { return m_range_p.at(1); }
|
||||
inline point1_type const& get_pk() const { return m_range_p.at(2); }
|
||||
|
||||
inline point2_type const& get_qi() const { return m_range_q.at(0); }
|
||||
inline point2_type const& get_qj() const { return m_range_q.at(1); }
|
||||
inline point2_type const& get_qk() const { return m_range_q.at(2); }
|
||||
|
||||
// Used side-strategy, owned by the calculator,
|
||||
// created from .get_side_strategy()
|
||||
SideStrategy m_side_strategy;
|
||||
|
||||
// Used ranges - owned by get_turns or (for robust points) by intersection_info_base
|
||||
UniqueSubRange1 const& m_range_p;
|
||||
UniqueSubRange2 const& m_range_q;
|
||||
};
|
||||
|
||||
template<typename Point, typename UniqueSubRange, typename RobustPolicy>
|
||||
struct robust_subrange_adapter
|
||||
{
|
||||
typedef Point point_type;
|
||||
|
||||
robust_subrange_adapter(UniqueSubRange const& unique_sub_range,
|
||||
Point const& robust_point_i, Point const& robust_point_j,
|
||||
RobustPolicy const& robust_policy)
|
||||
|
||||
: m_unique_sub_range(unique_sub_range)
|
||||
, m_robust_policy(robust_policy)
|
||||
, m_robust_point_i(robust_point_i)
|
||||
, m_robust_point_j(robust_point_j)
|
||||
, m_k_retrieved(false)
|
||||
{}
|
||||
|
||||
std::size_t size() const { return m_unique_sub_range.size(); }
|
||||
|
||||
//! Get precalculated point
|
||||
Point const& at(std::size_t index) const
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT(index < size());
|
||||
switch (index)
|
||||
{
|
||||
case 0 : return m_robust_point_i;
|
||||
case 1 : return m_robust_point_j;
|
||||
case 2 : return get_point_k();
|
||||
default : return m_robust_point_i;
|
||||
}
|
||||
}
|
||||
|
||||
private :
|
||||
Point const& get_point_k() const
|
||||
{
|
||||
if (! m_k_retrieved)
|
||||
{
|
||||
geometry::recalculate(m_robust_point_k, m_unique_sub_range.at(2), m_robust_policy);
|
||||
m_k_retrieved = true;
|
||||
}
|
||||
return m_robust_point_k;
|
||||
}
|
||||
|
||||
UniqueSubRange const& m_unique_sub_range;
|
||||
RobustPolicy const& m_robust_policy;
|
||||
|
||||
Point const& m_robust_point_i;
|
||||
Point const& m_robust_point_j;
|
||||
mutable Point m_robust_point_k;
|
||||
|
||||
mutable bool m_k_retrieved;
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1, typename UniqueSubRange2,
|
||||
typename RobustPolicy
|
||||
>
|
||||
struct robust_points
|
||||
{
|
||||
typedef typename geometry::robust_point_type
|
||||
<
|
||||
typename UniqueSubRange1::point_type, RobustPolicy
|
||||
>::type robust_point1_type;
|
||||
|
||||
typedef robust_point1_type robust_point2_type;
|
||||
|
||||
inline robust_points(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
RobustPolicy const& robust_policy)
|
||||
: m_robust_policy(robust_policy)
|
||||
, m_range_p(range_p)
|
||||
, m_range_q(range_q)
|
||||
, m_pk_retrieved(false)
|
||||
, m_qk_retrieved(false)
|
||||
{
|
||||
// Calculate pi,pj and qi,qj which are almost always necessary
|
||||
// But don't calculate pk/qk yet, which is retrieved (taking
|
||||
// more time) and not always necessary.
|
||||
geometry::recalculate(m_rpi, range_p.at(0), robust_policy);
|
||||
geometry::recalculate(m_rpj, range_p.at(1), robust_policy);
|
||||
geometry::recalculate(m_rqi, range_q.at(0), robust_policy);
|
||||
geometry::recalculate(m_rqj, range_q.at(1), robust_policy);
|
||||
}
|
||||
|
||||
inline robust_point1_type const& get_rpk() const
|
||||
{
|
||||
if (! m_pk_retrieved)
|
||||
{
|
||||
geometry::recalculate(m_rpk, m_range_p.at(2), m_robust_policy);
|
||||
m_pk_retrieved = true;
|
||||
}
|
||||
return m_rpk;
|
||||
}
|
||||
inline robust_point2_type const& get_rqk() const
|
||||
{
|
||||
if (! m_qk_retrieved)
|
||||
{
|
||||
geometry::recalculate(m_rqk, m_range_q.at(2), m_robust_policy);
|
||||
m_qk_retrieved = true;
|
||||
}
|
||||
return m_rqk;
|
||||
}
|
||||
|
||||
robust_point1_type m_rpi, m_rpj;
|
||||
robust_point2_type m_rqi, m_rqj;
|
||||
|
||||
private :
|
||||
RobustPolicy const& m_robust_policy;
|
||||
UniqueSubRange1 const& m_range_p;
|
||||
UniqueSubRange2 const& m_range_q;
|
||||
|
||||
// On retrieval
|
||||
mutable robust_point1_type m_rpk;
|
||||
mutable robust_point2_type m_rqk;
|
||||
mutable bool m_pk_retrieved;
|
||||
mutable bool m_qk_retrieved;
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1, typename UniqueSubRange2,
|
||||
typename TurnPoint, typename IntersectionStrategy, typename RobustPolicy>
|
||||
class intersection_info_base
|
||||
: private robust_points<UniqueSubRange1, UniqueSubRange2, RobustPolicy>
|
||||
{
|
||||
typedef robust_points<UniqueSubRange1, UniqueSubRange2, RobustPolicy> base;
|
||||
|
||||
public:
|
||||
typedef typename base::robust_point1_type robust_point1_type;
|
||||
typedef typename base::robust_point2_type robust_point2_type;
|
||||
|
||||
typedef robust_subrange_adapter<robust_point1_type, UniqueSubRange1, RobustPolicy> robust_subrange1;
|
||||
typedef robust_subrange_adapter<robust_point2_type, UniqueSubRange2, RobustPolicy> robust_subrange2;
|
||||
|
||||
typedef typename cs_tag<TurnPoint>::type cs_tag;
|
||||
|
||||
typedef typename IntersectionStrategy::side_strategy_type side_strategy_type;
|
||||
typedef side_calculator<cs_tag, robust_subrange1, robust_subrange2,
|
||||
side_strategy_type> side_calculator_type;
|
||||
|
||||
typedef side_calculator
|
||||
<
|
||||
cs_tag, robust_subrange2, robust_subrange1,
|
||||
side_strategy_type
|
||||
> robust_swapped_side_calculator_type;
|
||||
|
||||
intersection_info_base(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
: base(range_p, range_q, robust_policy)
|
||||
, m_range_p(range_p)
|
||||
, m_range_q(range_q)
|
||||
, m_robust_range_p(range_p, base::m_rpi, base::m_rpj, robust_policy)
|
||||
, m_robust_range_q(range_q, base::m_rqi, base::m_rqj, robust_policy)
|
||||
, m_side_calc(m_robust_range_p, m_robust_range_q,
|
||||
intersection_strategy.get_side_strategy())
|
||||
{}
|
||||
|
||||
inline typename UniqueSubRange1::point_type const& pi() const { return m_range_p.at(0); }
|
||||
inline typename UniqueSubRange2::point_type const& qi() const { return m_range_q.at(0); }
|
||||
|
||||
inline robust_point1_type const& rpi() const { return base::m_rpi; }
|
||||
inline robust_point1_type const& rpj() const { return base::m_rpj; }
|
||||
inline robust_point1_type const& rpk() const { return base::get_rpk(); }
|
||||
|
||||
inline robust_point2_type const& rqi() const { return base::m_rqi; }
|
||||
inline robust_point2_type const& rqj() const { return base::m_rqj; }
|
||||
inline robust_point2_type const& rqk() const { return base::get_rqk(); }
|
||||
|
||||
inline side_calculator_type const& sides() const { return m_side_calc; }
|
||||
|
||||
robust_swapped_side_calculator_type get_swapped_sides() const
|
||||
{
|
||||
robust_swapped_side_calculator_type result(
|
||||
m_robust_range_q, m_robust_range_p,
|
||||
m_side_calc.m_side_strategy);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Owned by get_turns
|
||||
UniqueSubRange1 const& m_range_p;
|
||||
UniqueSubRange2 const& m_range_q;
|
||||
private :
|
||||
// Owned by this class
|
||||
robust_subrange1 m_robust_range_p;
|
||||
robust_subrange2 m_robust_range_q;
|
||||
side_calculator_type m_side_calc;
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1, typename UniqueSubRange2,
|
||||
typename TurnPoint, typename IntersectionStrategy
|
||||
>
|
||||
class intersection_info_base<UniqueSubRange1, UniqueSubRange2,
|
||||
TurnPoint, IntersectionStrategy, detail::no_rescale_policy>
|
||||
{
|
||||
public:
|
||||
typedef typename UniqueSubRange1::point_type point1_type;
|
||||
typedef typename UniqueSubRange2::point_type point2_type;
|
||||
|
||||
typedef typename UniqueSubRange1::point_type robust_point1_type;
|
||||
typedef typename UniqueSubRange2::point_type robust_point2_type;
|
||||
|
||||
typedef typename cs_tag<TurnPoint>::type cs_tag;
|
||||
|
||||
typedef typename IntersectionStrategy::side_strategy_type side_strategy_type;
|
||||
typedef side_calculator<cs_tag, UniqueSubRange1, UniqueSubRange2, side_strategy_type> side_calculator_type;
|
||||
|
||||
typedef side_calculator
|
||||
<
|
||||
cs_tag, UniqueSubRange2, UniqueSubRange1,
|
||||
side_strategy_type
|
||||
> swapped_side_calculator_type;
|
||||
|
||||
intersection_info_base(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
no_rescale_policy const& /*robust_policy*/)
|
||||
: m_range_p(range_p)
|
||||
, m_range_q(range_q)
|
||||
, m_side_calc(range_p, range_q,
|
||||
intersection_strategy.get_side_strategy())
|
||||
{}
|
||||
|
||||
inline point1_type const& rpi() const { return m_side_calc.get_pi(); }
|
||||
inline point1_type const& rpj() const { return m_side_calc.get_pj(); }
|
||||
inline point1_type const& rpk() const { return m_side_calc.get_pk(); }
|
||||
|
||||
inline point2_type const& rqi() const { return m_side_calc.get_qi(); }
|
||||
inline point2_type const& rqj() const { return m_side_calc.get_qj(); }
|
||||
inline point2_type const& rqk() const { return m_side_calc.get_qk(); }
|
||||
|
||||
inline side_calculator_type const& sides() const { return m_side_calc; }
|
||||
|
||||
swapped_side_calculator_type get_swapped_sides() const
|
||||
{
|
||||
swapped_side_calculator_type result(
|
||||
m_range_q, m_range_p,
|
||||
m_side_calc.m_side_strategy);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected :
|
||||
// Owned by get_turns
|
||||
UniqueSubRange1 const& m_range_p;
|
||||
UniqueSubRange2 const& m_range_q;
|
||||
private :
|
||||
// Owned here, passed by .get_side_strategy()
|
||||
side_calculator_type m_side_calc;
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1, typename UniqueSubRange2,
|
||||
typename TurnPoint,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
class intersection_info
|
||||
: public intersection_info_base<UniqueSubRange1, UniqueSubRange2,
|
||||
TurnPoint, IntersectionStrategy, RobustPolicy>
|
||||
{
|
||||
typedef intersection_info_base<UniqueSubRange1, UniqueSubRange2,
|
||||
TurnPoint, IntersectionStrategy, RobustPolicy> base;
|
||||
|
||||
public:
|
||||
typedef segment_intersection_points
|
||||
<
|
||||
TurnPoint,
|
||||
typename geometry::segment_ratio_type
|
||||
<
|
||||
TurnPoint, RobustPolicy
|
||||
>::type
|
||||
> intersection_point_type;
|
||||
|
||||
typedef typename UniqueSubRange1::point_type point1_type;
|
||||
typedef typename UniqueSubRange2::point_type point2_type;
|
||||
|
||||
// NOTE: formerly defined in intersection_strategies
|
||||
typedef policies::relate::segments_tupled
|
||||
<
|
||||
policies::relate::segments_intersection_points
|
||||
<
|
||||
intersection_point_type
|
||||
>,
|
||||
policies::relate::segments_direction
|
||||
> intersection_policy_type;
|
||||
|
||||
typedef IntersectionStrategy intersection_strategy_type;
|
||||
typedef typename IntersectionStrategy::side_strategy_type side_strategy_type;
|
||||
|
||||
typedef model::referring_segment<point1_type const> segment_type1;
|
||||
typedef model::referring_segment<point2_type const> segment_type2;
|
||||
typedef typename base::side_calculator_type side_calculator_type;
|
||||
|
||||
typedef typename intersection_policy_type::return_type result_type;
|
||||
typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info
|
||||
typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info
|
||||
|
||||
intersection_info(UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
: base(range_p, range_q,
|
||||
intersection_strategy, robust_policy)
|
||||
, m_result(intersection_strategy.apply(
|
||||
segment_type1(range_p.at(0), range_p.at(1)),
|
||||
segment_type2(range_q.at(0), range_q.at(1)),
|
||||
intersection_policy_type(),
|
||||
robust_policy,
|
||||
base::rpi(), base::rpj(),
|
||||
base::rqi(), base::rqj()))
|
||||
, m_intersection_strategy(intersection_strategy)
|
||||
, m_robust_policy(robust_policy)
|
||||
{}
|
||||
|
||||
inline result_type const& result() const { return m_result; }
|
||||
inline i_info_type const& i_info() const { return m_result.template get<0>(); }
|
||||
inline d_info_type const& d_info() const { return m_result.template get<1>(); }
|
||||
|
||||
inline side_strategy_type get_side_strategy() const
|
||||
{
|
||||
return m_intersection_strategy.get_side_strategy();
|
||||
}
|
||||
|
||||
// TODO: it's more like is_spike_ip_p
|
||||
inline bool is_spike_p() const
|
||||
{
|
||||
if (base::m_range_p.is_last_segment())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (base::sides().pk_wrt_p1() == 0)
|
||||
{
|
||||
// p: pi--------pj--------pk
|
||||
// or: pi----pk==pj
|
||||
|
||||
if (! is_ip_j<0>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: why is q used to determine spike property in p?
|
||||
bool const has_qk = ! base::m_range_q.is_last_segment();
|
||||
int const qk_p1 = has_qk ? base::sides().qk_wrt_p1() : 0;
|
||||
int const qk_p2 = has_qk ? base::sides().qk_wrt_p2() : 0;
|
||||
|
||||
if (qk_p1 == -qk_p2)
|
||||
{
|
||||
if (qk_p1 == 0)
|
||||
{
|
||||
// qk is collinear with both p1 and p2,
|
||||
// verify if pk goes backwards w.r.t. pi/pj
|
||||
return direction_code(base::rpi(), base::rpj(), base::rpk()) == -1;
|
||||
}
|
||||
|
||||
// qk is at opposite side of p1/p2, therefore
|
||||
// p1/p2 (collinear) are opposite and form a spike
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_spike_q() const
|
||||
{
|
||||
if (base::m_range_q.is_last_segment())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// See comments at is_spike_p
|
||||
if (base::sides().qk_wrt_q1() == 0)
|
||||
{
|
||||
if (! is_ip_j<1>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: why is p used to determine spike property in q?
|
||||
bool const has_pk = ! base::m_range_p.is_last_segment();
|
||||
int const pk_q1 = has_pk ? base::sides().pk_wrt_q1() : 0;
|
||||
int const pk_q2 = has_pk ? base::sides().pk_wrt_q2() : 0;
|
||||
|
||||
if (pk_q1 == -pk_q2)
|
||||
{
|
||||
if (pk_q1 == 0)
|
||||
{
|
||||
return direction_code(base::rqi(), base::rqj(), base::rqk()) == -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t OpId>
|
||||
bool is_ip_j() const
|
||||
{
|
||||
int arrival = d_info().arrival[OpId];
|
||||
bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0;
|
||||
|
||||
if (same_dirs)
|
||||
{
|
||||
if (i_info().count == 2)
|
||||
{
|
||||
return arrival != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return arrival == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return arrival == 1;
|
||||
}
|
||||
}
|
||||
|
||||
result_type m_result;
|
||||
IntersectionStrategy const& m_intersection_strategy;
|
||||
RobustPolicy const& m_robust_policy;
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP
|
||||
@@ -0,0 +1,881 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
|
||||
// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
|
||||
|
||||
// TEMP, for spikes detector
|
||||
//#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp>
|
||||
|
||||
namespace boost { namespace geometry {
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay {
|
||||
|
||||
template<typename AssignPolicy>
|
||||
struct get_turn_info_linear_areal
|
||||
{
|
||||
// Currently only Linear spikes are handled
|
||||
// Areal spikes are ignored
|
||||
static const bool handle_spikes = true;
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename OutputIterator
|
||||
>
|
||||
static inline OutputIterator apply(
|
||||
UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out)
|
||||
{
|
||||
typedef intersection_info
|
||||
<
|
||||
UniqueSubRange1, UniqueSubRange2,
|
||||
typename TurnInfo::point_type,
|
||||
IntersectionStrategy,
|
||||
RobustPolicy
|
||||
> inters_info;
|
||||
|
||||
inters_info inters(range_p, range_q, strategy, robust_policy);
|
||||
|
||||
char const method = inters.d_info().how;
|
||||
|
||||
// Copy, to copy possibly extended fields
|
||||
TurnInfo tp = tp_model;
|
||||
|
||||
// Select method and apply
|
||||
switch(method)
|
||||
{
|
||||
case 'a' : // collinear, "at"
|
||||
case 'f' : // collinear, "from"
|
||||
case 's' : // starts from the middle
|
||||
get_turn_info_for_endpoint<true, true>(range_p, range_q,
|
||||
tp_model, inters, method_none, out,
|
||||
strategy.get_point_in_point_strategy());
|
||||
break;
|
||||
|
||||
case 'd' : // disjoint: never do anything
|
||||
break;
|
||||
|
||||
case 'm' :
|
||||
{
|
||||
if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
|
||||
tp_model, inters, method_touch_interior, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef touch_interior
|
||||
<
|
||||
TurnInfo
|
||||
> policy;
|
||||
|
||||
// If Q (1) arrives (1)
|
||||
if ( inters.d_info().arrival[1] == 1 )
|
||||
{
|
||||
policy::template apply<0>(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(),
|
||||
inters.sides());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swap p/q
|
||||
policy::template apply<1>(range_q, range_p,
|
||||
tp, inters.i_info(), inters.d_info(),
|
||||
inters.get_swapped_sides());
|
||||
}
|
||||
|
||||
if ( tp.operations[1].operation == operation_blocked )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
|
||||
replace_method_and_operations_tm(tp.method,
|
||||
tp.operations[0].operation,
|
||||
tp.operations[1].operation);
|
||||
|
||||
// this function assumes that 'u' must be set for a spike
|
||||
calculate_spike_operation(tp.operations[0].operation,
|
||||
inters,
|
||||
strategy.get_point_in_point_strategy());
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'i' :
|
||||
{
|
||||
crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
|
||||
|
||||
replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
break;
|
||||
case 't' :
|
||||
{
|
||||
// Both touch (both arrive there)
|
||||
if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
|
||||
tp_model, inters, method_touch, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
touch<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
if ( tp.operations[1].operation == operation_blocked )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
|
||||
// workarounds for touch<> not taking spikes into account starts here
|
||||
// those was discovered empirically
|
||||
// touch<> is not symmetrical!
|
||||
// P spikes and Q spikes may produce various operations!
|
||||
// Only P spikes are valid for L/A
|
||||
// TODO: this is not optimal solution - think about rewriting touch<>
|
||||
|
||||
if ( tp.operations[0].operation == operation_blocked )
|
||||
{
|
||||
// a spike on P on the same line with Q1
|
||||
if ( inters.is_spike_p() )
|
||||
{
|
||||
if ( inters.sides().qk_wrt_p1() == 0 )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[0].operation == operation_continue
|
||||
&& tp.operations[1].operation == operation_continue )
|
||||
{
|
||||
// P spike on the same line with Q2 (opposite)
|
||||
if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
|
||||
&& inters.is_spike_p() )
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_union;
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[0].operation == operation_none
|
||||
&& tp.operations[1].operation == operation_none )
|
||||
{
|
||||
// spike not handled by touch<>
|
||||
if ( inters.is_spike_p() )
|
||||
{
|
||||
tp.operations[0].operation = operation_intersection;
|
||||
tp.operations[1].operation = operation_union;
|
||||
|
||||
if ( inters.sides().pk_wrt_q2() == 0 )
|
||||
{
|
||||
tp.operations[0].operation = operation_continue; // will be converted to i
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// workarounds for touch<> not taking spikes into account ends here
|
||||
|
||||
replace_method_and_operations_tm(tp.method,
|
||||
tp.operations[0].operation,
|
||||
tp.operations[1].operation);
|
||||
|
||||
bool ignore_spike
|
||||
= calculate_spike_operation(tp.operations[0].operation,
|
||||
inters,
|
||||
strategy.get_point_in_point_strategy());
|
||||
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ignore_spike
|
||||
|| ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i???
|
||||
tp, inters, out) )
|
||||
{
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
{
|
||||
if ( get_turn_info_for_endpoint<true, true>(range_p, range_q,
|
||||
tp_model, inters, method_equal, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
|
||||
if ( ! inters.d_info().opposite )
|
||||
{
|
||||
// Both equal
|
||||
// or collinear-and-ending at intersection point
|
||||
equal<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
turn_transformer_ec<false> transformer(method_touch);
|
||||
transformer(tp);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ! append_collinear_spikes(tp, inters,
|
||||
method_touch, append_equal, out) )
|
||||
{
|
||||
*out++ = tp; // no spikes
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
equal_opposite
|
||||
<
|
||||
TurnInfo,
|
||||
AssignPolicy
|
||||
>::apply(range_p, range_q,
|
||||
tp, out, inters);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c' :
|
||||
{
|
||||
// Collinear
|
||||
if ( get_turn_info_for_endpoint<true, true>(
|
||||
range_p, range_q,
|
||||
tp_model, inters, method_collinear, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
|
||||
if ( ! inters.d_info().opposite )
|
||||
{
|
||||
method_type method_replace = method_touch_interior;
|
||||
append_version_c version = append_collinear;
|
||||
|
||||
if ( inters.d_info().arrival[0] == 0 )
|
||||
{
|
||||
// Collinear, but similar thus handled as equal
|
||||
equal<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
method_replace = method_touch;
|
||||
version = append_equal;
|
||||
}
|
||||
else
|
||||
{
|
||||
collinear<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
//method_replace = method_touch_interior;
|
||||
//version = append_collinear;
|
||||
}
|
||||
|
||||
turn_transformer_ec<false> transformer(method_replace);
|
||||
transformer(tp);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ! append_collinear_spikes(tp, inters,
|
||||
method_replace, version, out) )
|
||||
{
|
||||
// no spikes
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is this always 'm' ?
|
||||
turn_transformer_ec<false> transformer(method_touch_interior);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
|
||||
{
|
||||
append_opposite_spikes<append_collinear_opposite>(
|
||||
tp, inters, out);
|
||||
}
|
||||
|
||||
// TODO: ignore for spikes?
|
||||
// E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
|
||||
// the same with is_q_valid
|
||||
|
||||
collinear_opposite
|
||||
<
|
||||
TurnInfo,
|
||||
AssignPolicy
|
||||
>::apply(range_p, range_q,
|
||||
tp, out, inters,
|
||||
inters.sides(), transformer);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '0' :
|
||||
{
|
||||
// degenerate points
|
||||
if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
|
||||
{
|
||||
only_convert::apply(tp, inters.i_info());
|
||||
|
||||
if ( range_p.is_first_segment()
|
||||
&& equals::equals_point_point(range_p.at(0), tp.point,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
tp.operations[0].position = position_front;
|
||||
}
|
||||
else if ( range_p.is_last_segment()
|
||||
&& equals::equals_point_point(range_p.at(1), tp.point,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
tp.operations[0].position = position_back;
|
||||
}
|
||||
// tp.operations[1].position = position_middle;
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default :
|
||||
{
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
|
||||
std::cout << "TURN: Unknown method: " << method << std::endl;
|
||||
#endif
|
||||
#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
|
||||
BOOST_THROW_EXCEPTION(turn_info_exception(method));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Operation,
|
||||
typename IntersectionInfo,
|
||||
typename EqPPStrategy>
|
||||
static inline bool calculate_spike_operation(Operation & op,
|
||||
IntersectionInfo const& inters,
|
||||
EqPPStrategy const& strategy)
|
||||
{
|
||||
bool is_p_spike = ( op == operation_union || op == operation_intersection )
|
||||
&& inters.is_spike_p();
|
||||
|
||||
if ( is_p_spike )
|
||||
{
|
||||
int const pk_q1 = inters.sides().pk_wrt_q1();
|
||||
|
||||
bool going_in = pk_q1 < 0; // Pk on the right
|
||||
bool going_out = pk_q1 > 0; // Pk on the left
|
||||
|
||||
int const qk_q1 = inters.sides().qk_wrt_q1();
|
||||
|
||||
// special cases
|
||||
if ( qk_q1 < 0 ) // Q turning R
|
||||
{
|
||||
// spike on the edge point
|
||||
// if it's already known that the spike is going out this musn't be checked
|
||||
if ( ! going_out
|
||||
&& detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
|
||||
{
|
||||
int const pk_q2 = inters.sides().pk_wrt_q2();
|
||||
going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both
|
||||
going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them
|
||||
}
|
||||
}
|
||||
else if ( qk_q1 > 0 ) // Q turning L
|
||||
{
|
||||
// spike on the edge point
|
||||
// if it's already known that the spike is going in this musn't be checked
|
||||
if ( ! going_in
|
||||
&& detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
|
||||
{
|
||||
int const pk_q2 = inters.sides().pk_wrt_q2();
|
||||
going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them
|
||||
going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both
|
||||
}
|
||||
}
|
||||
|
||||
if ( going_in )
|
||||
{
|
||||
op = operation_intersection;
|
||||
return true;
|
||||
}
|
||||
else if ( going_out )
|
||||
{
|
||||
op = operation_union;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum append_version_c { append_equal, append_collinear };
|
||||
|
||||
template <typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutIt>
|
||||
static inline bool append_collinear_spikes(TurnInfo & tp,
|
||||
IntersectionInfo const& inters,
|
||||
method_type method, append_version_c version,
|
||||
OutIt out)
|
||||
{
|
||||
// method == touch || touch_interior
|
||||
// both position == middle
|
||||
|
||||
bool is_p_spike = ( version == append_equal ?
|
||||
( tp.operations[0].operation == operation_union
|
||||
|| tp.operations[0].operation == operation_intersection ) :
|
||||
tp.operations[0].operation == operation_continue )
|
||||
&& inters.is_spike_p();
|
||||
|
||||
// TODO: throw an exception for spike in Areal?
|
||||
/*bool is_q_spike = tp.operations[1].operation == operation_continue
|
||||
&& inters.is_spike_q();
|
||||
|
||||
// both are collinear spikes on the same IP, we can just follow both
|
||||
if ( is_p_spike && is_q_spike )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// spike on Linear - it's turning back on the boundary of Areal
|
||||
else*/
|
||||
if ( is_p_spike )
|
||||
{
|
||||
tp.method = method;
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[1].operation = operation_union;
|
||||
*out++ = tp;
|
||||
tp.operations[0].operation = operation_continue; // boundary
|
||||
//tp.operations[1].operation = operation_union;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}
|
||||
// spike on Areal - Linear is going outside
|
||||
/*else if ( is_q_spike )
|
||||
{
|
||||
tp.method = method;
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_continue;
|
||||
*out++ = tp;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum append_version_o { append_touches, append_collinear_opposite };
|
||||
|
||||
template <append_version_o Version,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutIt>
|
||||
static inline bool append_opposite_spikes(TurnInfo & tp,
|
||||
IntersectionInfo const& inters,
|
||||
OutIt out)
|
||||
{
|
||||
static const bool is_version_touches = (Version == append_touches);
|
||||
|
||||
bool is_p_spike = ( is_version_touches ?
|
||||
( tp.operations[0].operation == operation_continue
|
||||
|| tp.operations[0].operation == operation_intersection ) : // i ???
|
||||
true )
|
||||
&& inters.is_spike_p();
|
||||
|
||||
// TODO: throw an exception for spike in Areal?
|
||||
/*bool is_q_spike = ( ( Version == append_touches
|
||||
&& tp.operations[1].operation == operation_continue )
|
||||
|| ( Version == append_collinear_opposite
|
||||
&& tp.operations[1].operation == operation_none ) )
|
||||
&& inters.is_spike_q();
|
||||
|
||||
if ( is_p_spike && is_q_spike )
|
||||
{
|
||||
// u/u or nothing?
|
||||
return false;
|
||||
}
|
||||
else*/
|
||||
if ( is_p_spike )
|
||||
{
|
||||
if ( BOOST_GEOMETRY_CONDITION(is_version_touches)
|
||||
|| inters.d_info().arrival[0] == 1 )
|
||||
{
|
||||
if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
//tp.operations[1].is_collinear = false;
|
||||
tp.method = method_touch;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
//tp.operations[1].is_collinear = false;
|
||||
|
||||
BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
|
||||
base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1);
|
||||
}
|
||||
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[1].operation = operation_continue; // boundary
|
||||
*out++ = tp;
|
||||
tp.operations[0].operation = operation_continue; // boundary
|
||||
//tp.operations[1].operation = operation_continue; // boundary
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*else if ( is_q_spike )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.method = is_version_touches ? method_touch : method_touch_interior;
|
||||
tp.operations[0].operation = operation_continue;
|
||||
tp.operations[1].operation = operation_continue; // boundary
|
||||
*out++ = tp;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void replace_method_and_operations_tm(method_type & method,
|
||||
operation_type & op0,
|
||||
operation_type & op1)
|
||||
{
|
||||
if ( op0 == operation_blocked && op1 == operation_blocked )
|
||||
{
|
||||
// NOTE: probably only if methods are WRT IPs, not segments!
|
||||
method = (method == method_touch ? method_equal : method_collinear);
|
||||
}
|
||||
|
||||
// Assuming G1 is always Linear
|
||||
if ( op0 == operation_blocked )
|
||||
{
|
||||
op0 = operation_continue;
|
||||
}
|
||||
|
||||
if ( op1 == operation_blocked )
|
||||
{
|
||||
op1 = operation_continue;
|
||||
}
|
||||
else if ( op1 == operation_intersection )
|
||||
{
|
||||
op1 = operation_union;
|
||||
}
|
||||
|
||||
// spikes in 'm'
|
||||
if ( method == method_error )
|
||||
{
|
||||
method = method_touch_interior;
|
||||
op0 = operation_union;
|
||||
op1 = operation_union;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool IsFront>
|
||||
class turn_transformer_ec
|
||||
{
|
||||
public:
|
||||
explicit turn_transformer_ec(method_type method_t_or_m)
|
||||
: m_method(method_t_or_m)
|
||||
{}
|
||||
|
||||
template <typename Turn>
|
||||
void operator()(Turn & turn) const
|
||||
{
|
||||
operation_type & op0 = turn.operations[0].operation;
|
||||
operation_type & op1 = turn.operations[1].operation;
|
||||
|
||||
// NOTE: probably only if methods are WRT IPs, not segments!
|
||||
if ( BOOST_GEOMETRY_CONDITION(IsFront)
|
||||
|| op0 == operation_intersection || op0 == operation_union
|
||||
|| op1 == operation_intersection || op1 == operation_union )
|
||||
{
|
||||
turn.method = m_method;
|
||||
}
|
||||
|
||||
turn.operations[0].is_collinear = op0 != operation_blocked;
|
||||
|
||||
// Assuming G1 is always Linear
|
||||
if ( op0 == operation_blocked )
|
||||
{
|
||||
op0 = operation_continue;
|
||||
}
|
||||
|
||||
if ( op1 == operation_blocked )
|
||||
{
|
||||
op1 = operation_continue;
|
||||
}
|
||||
else if ( op1 == operation_intersection )
|
||||
{
|
||||
op1 = operation_union;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
method_type m_method;
|
||||
};
|
||||
|
||||
static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1)
|
||||
{
|
||||
// assuming Linear is always the first one
|
||||
op1 = operation_union;
|
||||
}
|
||||
|
||||
// NOTE: Spikes may NOT be handled for Linear endpoints because it's not
|
||||
// possible to define a spike on an endpoint. Areal geometries must
|
||||
// NOT have spikes at all. One thing that could be done is to throw
|
||||
// an exception when spike is detected in Areal geometry.
|
||||
|
||||
template <bool EnableFirst,
|
||||
bool EnableLast,
|
||||
typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutputIterator,
|
||||
typename EqPPStrategy>
|
||||
static inline bool get_turn_info_for_endpoint(
|
||||
UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionInfo const& inters,
|
||||
method_type /*method*/,
|
||||
OutputIterator out,
|
||||
EqPPStrategy const& strategy)
|
||||
{
|
||||
namespace ov = overlay;
|
||||
typedef ov::get_turn_info_for_endpoint<EnableFirst, EnableLast> get_info_e;
|
||||
|
||||
const std::size_t ip_count = inters.i_info().count;
|
||||
// no intersection points
|
||||
if (ip_count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! range_p.is_first_segment() && ! range_p.is_last_segment())
|
||||
{
|
||||
// P sub-range has no end-points
|
||||
return false;
|
||||
}
|
||||
|
||||
typename IntersectionInfo::side_strategy_type const& sides
|
||||
= inters.get_side_strategy();
|
||||
|
||||
linear_intersections intersections(range_p.at(0),
|
||||
range_q.at(0),
|
||||
inters.result(),
|
||||
range_p.is_last_segment(),
|
||||
range_q.is_last_segment(),
|
||||
strategy);
|
||||
linear_intersections::ip_info const& ip0 = intersections.template get<0>();
|
||||
linear_intersections::ip_info const& ip1 = intersections.template get<1>();
|
||||
|
||||
const bool opposite = inters.d_info().opposite;
|
||||
|
||||
// ANALYSE AND ASSIGN FIRST
|
||||
|
||||
// IP on the first point of Linear Geometry
|
||||
bool was_first_point_handled = false;
|
||||
if ( BOOST_GEOMETRY_CONDITION(EnableFirst)
|
||||
&& range_p.is_first_segment() && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication
|
||||
{
|
||||
TurnInfo tp = tp_model;
|
||||
tp.operations[0].position = position_front;
|
||||
tp.operations[1].position = position_middle;
|
||||
|
||||
if ( opposite ) // opposite -> collinear
|
||||
{
|
||||
tp.operations[0].operation = operation_continue;
|
||||
tp.operations[1].operation = operation_union;
|
||||
tp.method = ip0.is_qj ? method_touch : method_touch_interior;
|
||||
}
|
||||
else
|
||||
{
|
||||
// pi is the intersection point at qj or in the middle of q1
|
||||
// so consider segments
|
||||
// 1. pi at qj: qi-qj-pj and qi-qj-qk
|
||||
// x: qi-qj, y: qj-qk, qz: qk
|
||||
// 2. pi in the middle of q1: qi-pi-pj and qi-pi-qj
|
||||
// x: qi-pi, y: pi-qj, qz: qj
|
||||
// qi-pi, side the same as WRT q1
|
||||
// pi-qj, side the same as WRT q1
|
||||
// qj WRT q1 is 0
|
||||
method_type replaced_method = method_none;
|
||||
int side_pj_y = 0, side_pj_x = 0, side_qz_x = 0;
|
||||
// 1. ip0 or pi at qj
|
||||
if ( ip0.is_qj )
|
||||
{
|
||||
replaced_method = method_touch;
|
||||
side_pj_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(1)); // pj wrt q2
|
||||
side_pj_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
|
||||
side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
|
||||
}
|
||||
// 2. ip0 or pi in the middle of q1
|
||||
else
|
||||
{
|
||||
replaced_method = method_touch_interior;
|
||||
side_pj_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
|
||||
side_pj_x = side_pj_y; // pj wrt q1
|
||||
side_qz_x = 0; // qj wrt q1
|
||||
}
|
||||
|
||||
std::pair<operation_type, operation_type> operations
|
||||
= get_info_e::operations_of_equal(side_pj_y, side_pj_x, side_qz_x);
|
||||
|
||||
tp.operations[0].operation = operations.first;
|
||||
tp.operations[1].operation = operations.second;
|
||||
|
||||
turn_transformer_ec<true> transformer(replaced_method);
|
||||
transformer(tp);
|
||||
}
|
||||
|
||||
// equals<> or collinear<> will assign the second point,
|
||||
// we'd like to assign the first one
|
||||
base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0);
|
||||
|
||||
// NOTE: is_collinear is not set for the first endpoint of L
|
||||
// for which there is no preceding segment
|
||||
// here is_p_first_ip == true
|
||||
tp.operations[0].is_collinear = false;
|
||||
|
||||
*out++ = tp;
|
||||
|
||||
was_first_point_handled = true;
|
||||
}
|
||||
|
||||
// ANALYSE AND ASSIGN LAST
|
||||
|
||||
// IP on the last point of Linear Geometry
|
||||
if ( BOOST_GEOMETRY_CONDITION(EnableLast)
|
||||
&& range_p.is_last_segment()
|
||||
&& ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication
|
||||
{
|
||||
TurnInfo tp = tp_model;
|
||||
|
||||
if ( inters.i_info().count > 1 )
|
||||
{
|
||||
//BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].operation = opposite ? operation_continue : operation_union;
|
||||
}
|
||||
else //if ( result.template get<0>().count == 1 )
|
||||
{
|
||||
// pj is the intersection point at qj or in the middle of q1
|
||||
// so consider segments
|
||||
// 1. pj at qj: qi-qj-pi and qi-qj-qk
|
||||
// x: qi-qj, y: qj-qk, qz: qk
|
||||
// 2. pj in the middle of q1: qi-pj-pi and qi-pj-qj
|
||||
// x: qi-pj, y: pj-qj, qz: qj
|
||||
// qi-pj, the side is the same as WRT q1
|
||||
// pj-qj, the side is the same as WRT q1
|
||||
// side of qj WRT q1 is 0
|
||||
int side_pi_y = 0, side_pi_x = 0, side_qz_x = 0;
|
||||
// 1. ip0 or pj at qj
|
||||
if ( ip0.is_qj )
|
||||
{
|
||||
side_pi_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(0)); // pi wrt q2
|
||||
side_pi_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
|
||||
side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
|
||||
}
|
||||
// 2. ip0 or pj in the middle of q1
|
||||
else
|
||||
{
|
||||
side_pi_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
|
||||
side_pi_x = side_pi_y; // pi wrt q1
|
||||
side_qz_x = 0; // qj wrt q1
|
||||
}
|
||||
|
||||
std::pair<operation_type, operation_type> operations
|
||||
= get_info_e::operations_of_equal(side_pi_y, side_pi_x, side_qz_x);
|
||||
|
||||
tp.operations[0].operation = operations.first;
|
||||
tp.operations[1].operation = operations.second;
|
||||
|
||||
turn_transformer_ec<false> transformer(method_none);
|
||||
transformer(tp);
|
||||
|
||||
tp.operations[0].is_collinear = tp.both(operation_continue);
|
||||
}
|
||||
|
||||
tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior;
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[0].position = position_back;
|
||||
tp.operations[1].position = position_middle;
|
||||
|
||||
// equals<> or collinear<> will assign the second point,
|
||||
// we'd like to assign the first one
|
||||
unsigned int ip_index = ip_count > 1 ? 1 : 0;
|
||||
base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index);
|
||||
|
||||
*out++ = tp;
|
||||
|
||||
// don't ignore the first IP if the segment is opposite
|
||||
return !( opposite && ip_count > 1 ) || was_first_point_handled;
|
||||
}
|
||||
|
||||
// don't ignore anything for now
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Point1, typename Point2, typename IntersectionStrategy>
|
||||
static inline bool equals_point_point(Point1 const& point1, Point2 const& point2,
|
||||
IntersectionStrategy const& strategy)
|
||||
{
|
||||
return detail::equals::equals_point_point(point1, point2,
|
||||
strategy.get_point_in_point_strategy());
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
|
||||
@@ -0,0 +1,721 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
|
||||
// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
|
||||
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
namespace boost { namespace geometry {
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay {
|
||||
|
||||
template<typename AssignPolicy>
|
||||
struct get_turn_info_linear_linear
|
||||
{
|
||||
static const bool handle_spikes = true;
|
||||
|
||||
template
|
||||
<
|
||||
typename UniqueSubRange1,
|
||||
typename UniqueSubRange2,
|
||||
typename TurnInfo,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename OutputIterator
|
||||
>
|
||||
static inline OutputIterator apply(
|
||||
UniqueSubRange1 const& range_p,
|
||||
UniqueSubRange2 const& range_q,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out)
|
||||
{
|
||||
typedef intersection_info
|
||||
<
|
||||
UniqueSubRange1, UniqueSubRange2,
|
||||
typename TurnInfo::point_type,
|
||||
IntersectionStrategy,
|
||||
RobustPolicy
|
||||
> inters_info;
|
||||
|
||||
inters_info inters(range_p, range_q, strategy, robust_policy);
|
||||
|
||||
char const method = inters.d_info().how;
|
||||
|
||||
// Copy, to copy possibly extended fields
|
||||
TurnInfo tp = tp_model;
|
||||
|
||||
// Select method and apply
|
||||
switch(method)
|
||||
{
|
||||
case 'a' : // collinear, "at"
|
||||
case 'f' : // collinear, "from"
|
||||
case 's' : // starts from the middle
|
||||
get_turn_info_for_endpoint<true, true>
|
||||
::apply(range_p, range_q,
|
||||
tp_model, inters, method_none, out,
|
||||
strategy.get_point_in_point_strategy());
|
||||
break;
|
||||
|
||||
case 'd' : // disjoint: never do anything
|
||||
break;
|
||||
|
||||
case 'm' :
|
||||
{
|
||||
if ( get_turn_info_for_endpoint<false, true>
|
||||
::apply(range_p, range_q,
|
||||
tp_model, inters, method_touch_interior, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef touch_interior
|
||||
<
|
||||
TurnInfo
|
||||
> policy;
|
||||
|
||||
// If Q (1) arrives (1)
|
||||
if ( inters.d_info().arrival[1] == 1)
|
||||
{
|
||||
policy::template apply<0>(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(),
|
||||
inters.sides());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swap p/q
|
||||
policy::template apply<1>(range_q, range_p, tp,
|
||||
inters.i_info(), inters.d_info(),
|
||||
inters.get_swapped_sides());
|
||||
}
|
||||
|
||||
if ( tp.operations[0].operation == operation_blocked )
|
||||
{
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
if ( tp.operations[1].operation == operation_blocked )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
|
||||
replace_method_and_operations_tm(tp.method,
|
||||
tp.operations[0].operation,
|
||||
tp.operations[1].operation);
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'i' :
|
||||
{
|
||||
crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
|
||||
|
||||
replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
break;
|
||||
case 't' :
|
||||
{
|
||||
// Both touch (both arrive there)
|
||||
if ( get_turn_info_for_endpoint<false, true>
|
||||
::apply(range_p, range_q,
|
||||
tp_model, inters, method_touch, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
touch<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
// workarounds for touch<> not taking spikes into account starts here
|
||||
// those was discovered empirically
|
||||
// touch<> is not symmetrical!
|
||||
// P spikes and Q spikes may produce various operations!
|
||||
// TODO: this is not optimal solution - think about rewriting touch<>
|
||||
|
||||
if ( tp.operations[0].operation == operation_blocked
|
||||
&& tp.operations[1].operation == operation_blocked )
|
||||
{
|
||||
// two touching spikes on the same line
|
||||
if ( inters.is_spike_p() && inters.is_spike_q() )
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_union;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[0].operation == operation_blocked )
|
||||
{
|
||||
// a spike on P on the same line with Q1
|
||||
if ( inters.is_spike_p() )
|
||||
{
|
||||
if ( inters.sides().qk_wrt_p1() == 0 )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[1].operation == operation_blocked )
|
||||
{
|
||||
// a spike on Q on the same line with P1
|
||||
if ( inters.is_spike_q() )
|
||||
{
|
||||
if ( inters.sides().pk_wrt_q1() == 0 )
|
||||
{
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[1].operation = operation_union;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[0].operation == operation_continue
|
||||
&& tp.operations[1].operation == operation_continue )
|
||||
{
|
||||
// P spike on the same line with Q2 (opposite)
|
||||
if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
|
||||
&& inters.is_spike_p() )
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_union;
|
||||
}
|
||||
}
|
||||
else if ( tp.operations[0].operation == operation_none
|
||||
&& tp.operations[1].operation == operation_none )
|
||||
{
|
||||
// spike not handled by touch<>
|
||||
bool const is_p = inters.is_spike_p();
|
||||
bool const is_q = inters.is_spike_q();
|
||||
|
||||
if ( is_p || is_q )
|
||||
{
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_union;
|
||||
|
||||
if ( inters.sides().pk_wrt_q2() == 0 )
|
||||
{
|
||||
tp.operations[0].operation = operation_continue; // will be converted to i
|
||||
if ( is_p )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( inters.sides().qk_wrt_p2() == 0 )
|
||||
{
|
||||
tp.operations[1].operation = operation_continue; // will be converted to i
|
||||
if ( is_q )
|
||||
{
|
||||
tp.operations[1].is_collinear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// workarounds for touch<> not taking spikes into account ends here
|
||||
|
||||
replace_method_and_operations_tm(tp.method,
|
||||
tp.operations[0].operation,
|
||||
tp.operations[1].operation);
|
||||
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ! append_opposite_spikes<append_touches>(tp, inters, out) )
|
||||
{
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
{
|
||||
if ( get_turn_info_for_endpoint<true, true>
|
||||
::apply(range_p, range_q,
|
||||
tp_model, inters, method_equal, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].is_collinear = true;
|
||||
|
||||
if ( ! inters.d_info().opposite )
|
||||
{
|
||||
// Both equal
|
||||
// or collinear-and-ending at intersection point
|
||||
equal<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
operation_type spike_op
|
||||
= ( tp.operations[0].operation != operation_continue
|
||||
|| tp.operations[1].operation != operation_continue ) ?
|
||||
operation_union :
|
||||
operation_continue;
|
||||
|
||||
// transform turn
|
||||
turn_transformer_ec transformer(method_touch);
|
||||
transformer(tp);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ! append_collinear_spikes(tp, inters,
|
||||
method_touch, spike_op,
|
||||
out) )
|
||||
{
|
||||
*out++ = tp; // no spikes
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: ignore for spikes or generate something else than opposite?
|
||||
|
||||
equal_opposite
|
||||
<
|
||||
TurnInfo,
|
||||
AssignPolicy
|
||||
>::apply(range_p, range_q, tp, out, inters);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c' :
|
||||
{
|
||||
// Collinear
|
||||
if ( get_turn_info_for_endpoint<true, true>
|
||||
::apply(range_p, range_q,
|
||||
tp_model, inters, method_collinear, out,
|
||||
strategy.get_point_in_point_strategy()) )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this is for spikes since those are set in the turn_transformer_ec
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].is_collinear = true;
|
||||
|
||||
if ( ! inters.d_info().opposite )
|
||||
{
|
||||
method_type method_replace = method_touch_interior;
|
||||
operation_type spike_op = operation_continue;
|
||||
|
||||
if ( inters.d_info().arrival[0] == 0 )
|
||||
{
|
||||
// Collinear, but similar thus handled as equal
|
||||
equal<TurnInfo>::apply(range_p, range_q, tp,
|
||||
inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
method_replace = method_touch;
|
||||
if ( tp.operations[0].operation != operation_continue
|
||||
|| tp.operations[1].operation != operation_continue )
|
||||
{
|
||||
spike_op = operation_union;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
collinear<TurnInfo>::apply(range_p, range_q,
|
||||
tp, inters.i_info(), inters.d_info(), inters.sides());
|
||||
|
||||
//method_replace = method_touch_interior;
|
||||
//spike_op = operation_continue;
|
||||
}
|
||||
|
||||
// transform turn
|
||||
turn_transformer_ec transformer(method_replace);
|
||||
transformer(tp);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
|
||||
|| ! append_collinear_spikes(tp, inters,
|
||||
method_replace, spike_op,
|
||||
out) )
|
||||
{
|
||||
// no spikes
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this always 'm' ?
|
||||
turn_transformer_ec transformer(method_touch_interior);
|
||||
|
||||
// conditionally handle spikes
|
||||
if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
|
||||
{
|
||||
append_opposite_spikes<append_collinear_opposite>(tp, inters, out);
|
||||
}
|
||||
|
||||
// TODO: ignore for spikes?
|
||||
// E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
|
||||
// the same with is_q_valid
|
||||
|
||||
collinear_opposite
|
||||
<
|
||||
TurnInfo,
|
||||
AssignPolicy
|
||||
>::apply(range_p, range_q,
|
||||
tp, out, inters, inters.sides(),
|
||||
transformer);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '0' :
|
||||
{
|
||||
// degenerate points
|
||||
if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
|
||||
{
|
||||
typedef typename IntersectionStrategy::point_in_point_strategy_type
|
||||
equals_strategy_type;
|
||||
|
||||
only_convert::apply(tp, inters.i_info());
|
||||
|
||||
// if any, only one of those should be true
|
||||
if ( range_p.is_first_segment()
|
||||
&& equals::equals_point_point(range_p.at(0), tp.point, equals_strategy_type()) )
|
||||
{
|
||||
tp.operations[0].position = position_front;
|
||||
}
|
||||
else if ( range_p.is_last_segment()
|
||||
&& equals::equals_point_point(range_p.at(1), tp.point, equals_strategy_type()) )
|
||||
{
|
||||
tp.operations[0].position = position_back;
|
||||
}
|
||||
else if ( range_q.is_first_segment()
|
||||
&& equals::equals_point_point(range_q.at(0), tp.point, equals_strategy_type()) )
|
||||
{
|
||||
tp.operations[1].position = position_front;
|
||||
}
|
||||
else if ( range_q.is_last_segment()
|
||||
&& equals::equals_point_point(range_q.at(1), tp.point, equals_strategy_type()) )
|
||||
{
|
||||
tp.operations[1].position = position_back;
|
||||
}
|
||||
|
||||
*out++ = tp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default :
|
||||
{
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
|
||||
std::cout << "TURN: Unknown method: " << method << std::endl;
|
||||
#endif
|
||||
#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
|
||||
BOOST_THROW_EXCEPTION(turn_info_exception(method));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutIt>
|
||||
static inline bool append_collinear_spikes(TurnInfo & tp,
|
||||
IntersectionInfo const& inters_info,
|
||||
method_type method, operation_type spike_op,
|
||||
OutIt out)
|
||||
{
|
||||
// method == touch || touch_interior
|
||||
// both position == middle
|
||||
|
||||
bool is_p_spike = tp.operations[0].operation == spike_op
|
||||
&& inters_info.is_spike_p();
|
||||
bool is_q_spike = tp.operations[1].operation == spike_op
|
||||
&& inters_info.is_spike_q();
|
||||
|
||||
if ( is_p_spike && is_q_spike )
|
||||
{
|
||||
if ( tp.method == method_equal
|
||||
&& tp.operations[0].operation == operation_continue
|
||||
&& tp.operations[1].operation == operation_continue )
|
||||
{
|
||||
// treat both non-opposite collinear spikes as no-spikes
|
||||
return false;
|
||||
}
|
||||
|
||||
tp.method = method;
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[1].operation = operation_blocked;
|
||||
*out++ = tp;
|
||||
tp.operations[0].operation = operation_intersection;
|
||||
tp.operations[1].operation = operation_intersection;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( is_p_spike )
|
||||
{
|
||||
tp.method = method;
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[1].operation = operation_union;
|
||||
*out++ = tp;
|
||||
tp.operations[0].operation = operation_intersection;
|
||||
//tp.operations[1].operation = operation_union;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( is_q_spike )
|
||||
{
|
||||
tp.method = method;
|
||||
tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_blocked;
|
||||
*out++ = tp;
|
||||
//tp.operations[0].operation = operation_union;
|
||||
tp.operations[1].operation = operation_intersection;
|
||||
*out++ = tp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum append_version { append_touches, append_collinear_opposite };
|
||||
|
||||
template <append_version Version,
|
||||
typename TurnInfo,
|
||||
typename IntersectionInfo,
|
||||
typename OutIt>
|
||||
static inline bool append_opposite_spikes(TurnInfo & tp,
|
||||
IntersectionInfo const& inters,
|
||||
OutIt out)
|
||||
{
|
||||
static const bool is_version_touches = (Version == append_touches);
|
||||
|
||||
bool is_p_spike = ( is_version_touches ?
|
||||
( tp.operations[0].operation == operation_continue
|
||||
|| tp.operations[0].operation == operation_intersection ) :
|
||||
true )
|
||||
&& inters.is_spike_p();
|
||||
bool is_q_spike = ( is_version_touches ?
|
||||
( tp.operations[1].operation == operation_continue
|
||||
|| tp.operations[1].operation == operation_intersection ) :
|
||||
true )
|
||||
&& inters.is_spike_q();
|
||||
|
||||
bool res = false;
|
||||
|
||||
if ( is_p_spike
|
||||
&& ( BOOST_GEOMETRY_CONDITION(is_version_touches)
|
||||
|| inters.d_info().arrival[0] == 1 ) )
|
||||
{
|
||||
if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].is_collinear = false;
|
||||
tp.method = method_touch;
|
||||
}
|
||||
else // Version == append_collinear_opposite
|
||||
{
|
||||
tp.operations[0].is_collinear = true;
|
||||
tp.operations[1].is_collinear = false;
|
||||
|
||||
BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
|
||||
|
||||
base_turn_handler::assign_point(tp, method_touch_interior,
|
||||
inters.i_info(), 1);
|
||||
}
|
||||
|
||||
tp.operations[0].operation = operation_blocked;
|
||||
tp.operations[1].operation = operation_intersection;
|
||||
*out++ = tp;
|
||||
tp.operations[0].operation = operation_intersection;
|
||||
//tp.operations[1].operation = operation_intersection;
|
||||
*out++ = tp;
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
||||
if ( is_q_spike
|
||||
&& ( BOOST_GEOMETRY_CONDITION(is_version_touches)
|
||||
|| inters.d_info().arrival[1] == 1 ) )
|
||||
{
|
||||
if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
|
||||
{
|
||||
tp.operations[0].is_collinear = false;
|
||||
tp.operations[1].is_collinear = true;
|
||||
tp.method = method_touch;
|
||||
}
|
||||
else // Version == append_collinear_opposite
|
||||
{
|
||||
tp.operations[0].is_collinear = false;
|
||||
tp.operations[1].is_collinear = true;
|
||||
|
||||
BOOST_GEOMETRY_ASSERT(inters.i_info().count > 0);
|
||||
|
||||
base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0);
|
||||
}
|
||||
|
||||
tp.operations[0].operation = operation_intersection;
|
||||
tp.operations[1].operation = operation_blocked;
|
||||
*out++ = tp;
|
||||
//tp.operations[0].operation = operation_intersection;
|
||||
tp.operations[1].operation = operation_intersection;
|
||||
*out++ = tp;
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void replace_method_and_operations_tm(method_type & method,
|
||||
operation_type & op0,
|
||||
operation_type & op1)
|
||||
{
|
||||
if ( op0 == operation_blocked && op1 == operation_blocked )
|
||||
{
|
||||
// NOTE: probably only if methods are WRT IPs, not segments!
|
||||
method = (method == method_touch ? method_equal : method_collinear);
|
||||
op0 = operation_continue;
|
||||
op1 = operation_continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( op0 == operation_continue || op0 == operation_blocked )
|
||||
{
|
||||
op0 = operation_intersection;
|
||||
}
|
||||
else if ( op0 == operation_intersection )
|
||||
{
|
||||
op0 = operation_union;
|
||||
}
|
||||
|
||||
if ( op1 == operation_continue || op1 == operation_blocked )
|
||||
{
|
||||
op1 = operation_intersection;
|
||||
}
|
||||
else if ( op1 == operation_intersection )
|
||||
{
|
||||
op1 = operation_union;
|
||||
}
|
||||
|
||||
// spikes in 'm'
|
||||
if ( method == method_error )
|
||||
{
|
||||
method = method_touch_interior;
|
||||
op0 = operation_union;
|
||||
op1 = operation_union;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class turn_transformer_ec
|
||||
{
|
||||
public:
|
||||
explicit turn_transformer_ec(method_type method_t_or_m)
|
||||
: m_method(method_t_or_m)
|
||||
{}
|
||||
|
||||
template <typename Turn>
|
||||
void operator()(Turn & turn) const
|
||||
{
|
||||
operation_type & op0 = turn.operations[0].operation;
|
||||
operation_type & op1 = turn.operations[1].operation;
|
||||
|
||||
BOOST_GEOMETRY_ASSERT(op0 != operation_blocked || op1 != operation_blocked );
|
||||
|
||||
if ( op0 == operation_blocked )
|
||||
{
|
||||
op0 = operation_intersection;
|
||||
}
|
||||
else if ( op0 == operation_intersection )
|
||||
{
|
||||
op0 = operation_union;
|
||||
}
|
||||
|
||||
if ( op1 == operation_blocked )
|
||||
{
|
||||
op1 = operation_intersection;
|
||||
}
|
||||
else if ( op1 == operation_intersection )
|
||||
{
|
||||
op1 = operation_union;
|
||||
}
|
||||
|
||||
if ( op0 == operation_intersection || op0 == operation_union
|
||||
|| op1 == operation_intersection || op1 == operation_union )
|
||||
{
|
||||
turn.method = m_method;
|
||||
}
|
||||
|
||||
// TODO: is this correct?
|
||||
// it's equivalent to comparing to operation_blocked at the beginning of the function
|
||||
turn.operations[0].is_collinear = op0 != operation_intersection;
|
||||
turn.operations[1].is_collinear = op1 != operation_intersection;
|
||||
}
|
||||
|
||||
private:
|
||||
method_type m_method;
|
||||
};
|
||||
|
||||
static inline void replace_operations_i(operation_type & op0, operation_type & op1)
|
||||
{
|
||||
if ( op0 == operation_intersection )
|
||||
{
|
||||
op0 = operation_union;
|
||||
}
|
||||
|
||||
if ( op1 == operation_intersection )
|
||||
{
|
||||
op1 = operation_union;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,861 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/geometry/core/point_order.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
|
||||
# include <iostream>
|
||||
# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
||||
# include <boost/geometry/io/wkt/wkt.hpp>
|
||||
# define BOOST_GEOMETRY_DEBUG_IDENTIFIER
|
||||
#endif
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <typename SegmentRatio>
|
||||
struct segment_fraction
|
||||
{
|
||||
segment_identifier seg_id;
|
||||
SegmentRatio fraction;
|
||||
|
||||
segment_fraction(segment_identifier const& id, SegmentRatio const& fr)
|
||||
: seg_id(id)
|
||||
, fraction(fr)
|
||||
{}
|
||||
|
||||
segment_fraction()
|
||||
{}
|
||||
|
||||
bool operator<(segment_fraction<SegmentRatio> const& other) const
|
||||
{
|
||||
return seg_id == other.seg_id
|
||||
? fraction < other.fraction
|
||||
: seg_id < other.seg_id;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct turn_operation_index
|
||||
{
|
||||
turn_operation_index(signed_size_type ti = -1,
|
||||
signed_size_type oi = -1)
|
||||
: turn_index(ti)
|
||||
, op_index(oi)
|
||||
{}
|
||||
|
||||
signed_size_type turn_index;
|
||||
signed_size_type op_index; // only 0,1
|
||||
};
|
||||
|
||||
|
||||
template <typename Turns>
|
||||
struct less_by_fraction_and_type
|
||||
{
|
||||
inline less_by_fraction_and_type(Turns const& turns)
|
||||
: m_turns(turns)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator()(turn_operation_index const& left,
|
||||
turn_operation_index const& right) const
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
|
||||
turn_type const& left_turn = m_turns[left.turn_index];
|
||||
turn_type const& right_turn = m_turns[right.turn_index];
|
||||
turn_operation_type const& left_op
|
||||
= left_turn.operations[left.op_index];
|
||||
|
||||
turn_operation_type const& right_op
|
||||
= right_turn.operations[right.op_index];
|
||||
|
||||
if (! (left_op.fraction == right_op.fraction))
|
||||
{
|
||||
return left_op.fraction < right_op.fraction;
|
||||
}
|
||||
|
||||
// Order xx first - used to discard any following colocated turn
|
||||
bool const left_both_xx = left_turn.both(operation_blocked);
|
||||
bool const right_both_xx = right_turn.both(operation_blocked);
|
||||
if (left_both_xx && ! right_both_xx)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (! left_both_xx && right_both_xx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool const left_both_uu = left_turn.both(operation_union);
|
||||
bool const right_both_uu = right_turn.both(operation_union);
|
||||
if (left_both_uu && ! right_both_uu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (! left_both_uu && right_both_uu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
turn_operation_type const& left_other_op
|
||||
= left_turn.operations[1 - left.op_index];
|
||||
|
||||
turn_operation_type const& right_other_op
|
||||
= right_turn.operations[1 - right.op_index];
|
||||
|
||||
// Fraction is the same, now sort on ring id, first outer ring,
|
||||
// then interior rings
|
||||
return left_other_op.seg_id < right_other_op.seg_id;
|
||||
}
|
||||
|
||||
private:
|
||||
Turns const& m_turns;
|
||||
};
|
||||
|
||||
template <typename Operation, typename ClusterPerSegment>
|
||||
inline signed_size_type get_cluster_id(Operation const& op, ClusterPerSegment const& cluster_per_segment)
|
||||
{
|
||||
typedef typename ClusterPerSegment::key_type segment_fraction_type;
|
||||
|
||||
segment_fraction_type seg_frac(op.seg_id, op.fraction);
|
||||
typename ClusterPerSegment::const_iterator it
|
||||
= cluster_per_segment.find(seg_frac);
|
||||
|
||||
if (it == cluster_per_segment.end())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template <typename Operation, typename ClusterPerSegment>
|
||||
inline void add_cluster_id(Operation const& op,
|
||||
ClusterPerSegment& cluster_per_segment, signed_size_type id)
|
||||
{
|
||||
typedef typename ClusterPerSegment::key_type segment_fraction_type;
|
||||
|
||||
segment_fraction_type seg_frac(op.seg_id, op.fraction);
|
||||
|
||||
cluster_per_segment[seg_frac] = id;
|
||||
}
|
||||
|
||||
template <typename Turn, typename ClusterPerSegment>
|
||||
inline signed_size_type add_turn_to_cluster(Turn const& turn,
|
||||
ClusterPerSegment& cluster_per_segment, signed_size_type& cluster_id)
|
||||
{
|
||||
signed_size_type cid0 = get_cluster_id(turn.operations[0], cluster_per_segment);
|
||||
signed_size_type cid1 = get_cluster_id(turn.operations[1], cluster_per_segment);
|
||||
|
||||
if (cid0 == -1 && cid1 == -1)
|
||||
{
|
||||
// Because of this, first cluster ID will be 1
|
||||
++cluster_id;
|
||||
add_cluster_id(turn.operations[0], cluster_per_segment, cluster_id);
|
||||
add_cluster_id(turn.operations[1], cluster_per_segment, cluster_id);
|
||||
return cluster_id;
|
||||
}
|
||||
else if (cid0 == -1 && cid1 != -1)
|
||||
{
|
||||
add_cluster_id(turn.operations[0], cluster_per_segment, cid1);
|
||||
return cid1;
|
||||
}
|
||||
else if (cid0 != -1 && cid1 == -1)
|
||||
{
|
||||
add_cluster_id(turn.operations[1], cluster_per_segment, cid0);
|
||||
return cid0;
|
||||
}
|
||||
else if (cid0 == cid1)
|
||||
{
|
||||
// Both already added to same cluster, no action
|
||||
return cid0;
|
||||
}
|
||||
|
||||
// Both operations.seg_id/fraction were already part of any cluster, and
|
||||
// these clusters are not the same. Merge of two clusters is necessary
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
|
||||
std::cout << " TODO: merge " << cid0 << " and " << cid1 << std::endl;
|
||||
#endif
|
||||
return cid0;
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename ClusterPerSegment,
|
||||
typename Operations,
|
||||
typename Geometry1,
|
||||
typename Geometry2
|
||||
>
|
||||
inline void handle_colocation_cluster(Turns& turns,
|
||||
signed_size_type& cluster_id,
|
||||
ClusterPerSegment& cluster_per_segment,
|
||||
Operations const& operations,
|
||||
Geometry1 const& /*geometry1*/, Geometry2 const& /*geometry2*/)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
|
||||
std::vector<turn_operation_index>::const_iterator vit = operations.begin();
|
||||
|
||||
turn_operation_index ref_toi = *vit;
|
||||
signed_size_type ref_id = -1;
|
||||
|
||||
for (++vit; vit != operations.end(); ++vit)
|
||||
{
|
||||
turn_type& ref_turn = turns[ref_toi.turn_index];
|
||||
turn_operation_type const& ref_op
|
||||
= ref_turn.operations[ref_toi.op_index];
|
||||
|
||||
turn_operation_index const& toi = *vit;
|
||||
turn_type& turn = turns[toi.turn_index];
|
||||
turn_operation_type const& op = turn.operations[toi.op_index];
|
||||
|
||||
BOOST_ASSERT(ref_op.seg_id == op.seg_id);
|
||||
|
||||
if (ref_op.fraction == op.fraction)
|
||||
{
|
||||
turn_operation_type const& other_op = turn.operations[1 - toi.op_index];
|
||||
|
||||
if (ref_id == -1)
|
||||
{
|
||||
ref_id = add_turn_to_cluster(ref_turn, cluster_per_segment, cluster_id);
|
||||
}
|
||||
BOOST_ASSERT(ref_id != -1);
|
||||
|
||||
// ref_turn (both operations) are already added to cluster,
|
||||
// so also "op" is already added to cluster,
|
||||
// We only need to add other_op
|
||||
signed_size_type id = get_cluster_id(other_op, cluster_per_segment);
|
||||
if (id != -1 && id != ref_id)
|
||||
{
|
||||
}
|
||||
else if (id == -1)
|
||||
{
|
||||
// Add to same cluster
|
||||
add_cluster_id(other_op, cluster_per_segment, ref_id);
|
||||
id = ref_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not on same fraction on this segment
|
||||
// assign for next
|
||||
ref_toi = toi;
|
||||
ref_id = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename ClusterPerSegment
|
||||
>
|
||||
inline void assign_cluster_to_turns(Turns& turns,
|
||||
Clusters& clusters,
|
||||
ClusterPerSegment const& cluster_per_segment)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
typedef typename ClusterPerSegment::key_type segment_fraction_type;
|
||||
|
||||
signed_size_type turn_index = 0;
|
||||
for (typename boost::range_iterator<Turns>::type it = turns.begin();
|
||||
it != turns.end(); ++it, ++turn_index)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
|
||||
if (turn.discarded)
|
||||
{
|
||||
// They were processed (to create proper map) but will not be added
|
||||
// This might leave a cluster with only 1 turn, which will be fixed
|
||||
// afterwards
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
turn_operation_type const& op = turn.operations[i];
|
||||
segment_fraction_type seg_frac(op.seg_id, op.fraction);
|
||||
typename ClusterPerSegment::const_iterator cit = cluster_per_segment.find(seg_frac);
|
||||
if (cit != cluster_per_segment.end())
|
||||
{
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
|
||||
if (turn.is_clustered()
|
||||
&& turn.cluster_id != cit->second)
|
||||
{
|
||||
std::cout << " CONFLICT " << std::endl;
|
||||
}
|
||||
#endif
|
||||
turn.cluster_id = cit->second;
|
||||
clusters[turn.cluster_id].turn_indices.insert(turn_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Turns, typename Clusters>
|
||||
inline void remove_clusters(Turns& turns, Clusters& clusters)
|
||||
{
|
||||
typename Clusters::iterator it = clusters.begin();
|
||||
while (it != clusters.end())
|
||||
{
|
||||
// Hold iterator and increase. We can erase cit, this keeps the
|
||||
// iterator valid (cf The standard associative-container erase idiom)
|
||||
typename Clusters::iterator current_it = it;
|
||||
++it;
|
||||
|
||||
std::set<signed_size_type> const& turn_indices
|
||||
= current_it->second.turn_indices;
|
||||
if (turn_indices.size() == 1)
|
||||
{
|
||||
signed_size_type const turn_index = *turn_indices.begin();
|
||||
turns[turn_index].cluster_id = -1;
|
||||
clusters.erase(current_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Turns, typename Clusters>
|
||||
inline void cleanup_clusters(Turns& turns, Clusters& clusters)
|
||||
{
|
||||
// Removes discarded turns from clusters
|
||||
for (typename Clusters::iterator mit = clusters.begin();
|
||||
mit != clusters.end(); ++mit)
|
||||
{
|
||||
cluster_info& cinfo = mit->second;
|
||||
std::set<signed_size_type>& ids = cinfo.turn_indices;
|
||||
for (std::set<signed_size_type>::iterator sit = ids.begin();
|
||||
sit != ids.end(); /* no increment */)
|
||||
{
|
||||
std::set<signed_size_type>::iterator current_it = sit;
|
||||
++sit;
|
||||
|
||||
signed_size_type const turn_index = *current_it;
|
||||
if (turns[turn_index].discarded)
|
||||
{
|
||||
ids.erase(current_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_clusters(turns, clusters);
|
||||
}
|
||||
|
||||
template <typename Turn, typename IdSet>
|
||||
inline void discard_ie_turn(Turn& turn, IdSet& ids, signed_size_type id)
|
||||
{
|
||||
turn.discarded = true;
|
||||
// Set cluster id to -1, but don't clear colocated flags
|
||||
turn.cluster_id = -1;
|
||||
// To remove it later from clusters
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
template <bool Reverse>
|
||||
inline bool is_interior(segment_identifier const& seg_id)
|
||||
{
|
||||
return Reverse ? seg_id.ring_index == -1 : seg_id.ring_index >= 0;
|
||||
}
|
||||
|
||||
template <bool Reverse0, bool Reverse1>
|
||||
inline bool is_ie_turn(segment_identifier const& ext_seg_0,
|
||||
segment_identifier const& ext_seg_1,
|
||||
segment_identifier const& int_seg_0,
|
||||
segment_identifier const& other_seg_1)
|
||||
{
|
||||
if (ext_seg_0.source_index == ext_seg_1.source_index)
|
||||
{
|
||||
// External turn is a self-turn, dont discard internal turn for this
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Compares two segment identifiers from two turns (external / one internal)
|
||||
|
||||
// From first turn [0], both are from same polygon (multi_index),
|
||||
// one is exterior (-1), the other is interior (>= 0),
|
||||
// and the second turn [1] handles the same ring
|
||||
|
||||
// For difference, where the rings are processed in reversal, all interior
|
||||
// rings become exterior and vice versa. But also the multi property changes:
|
||||
// rings originally from the same multi should now be considered as from
|
||||
// different multi polygons.
|
||||
// But this is not always the case, and at this point hard to figure out
|
||||
// (not yet implemented, TODO)
|
||||
|
||||
bool const same_multi0 = ! Reverse0
|
||||
&& ext_seg_0.multi_index == int_seg_0.multi_index;
|
||||
|
||||
bool const same_multi1 = ! Reverse1
|
||||
&& ext_seg_1.multi_index == other_seg_1.multi_index;
|
||||
|
||||
boost::ignore_unused(same_multi1);
|
||||
|
||||
return same_multi0
|
||||
&& same_multi1
|
||||
&& ! is_interior<Reverse0>(ext_seg_0)
|
||||
&& is_interior<Reverse0>(int_seg_0)
|
||||
&& ext_seg_1.ring_index == other_seg_1.ring_index;
|
||||
|
||||
// The other way round is tested in another call
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior
|
||||
typename Turns,
|
||||
typename Clusters
|
||||
>
|
||||
inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters)
|
||||
{
|
||||
typedef std::set<signed_size_type>::const_iterator set_iterator;
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
|
||||
std::set<signed_size_type> ids_to_remove;
|
||||
|
||||
for (typename Clusters::iterator cit = clusters.begin();
|
||||
cit != clusters.end(); ++cit)
|
||||
{
|
||||
cluster_info& cinfo = cit->second;
|
||||
std::set<signed_size_type>& ids = cinfo.turn_indices;
|
||||
|
||||
ids_to_remove.clear();
|
||||
|
||||
for (set_iterator it = ids.begin(); it != ids.end(); ++it)
|
||||
{
|
||||
turn_type& turn = turns[*it];
|
||||
segment_identifier const& seg_0 = turn.operations[0].seg_id;
|
||||
segment_identifier const& seg_1 = turn.operations[1].seg_id;
|
||||
|
||||
if (! (turn.both(operation_union)
|
||||
|| turn.combination(operation_union, operation_blocked)))
|
||||
{
|
||||
// Not a uu/ux, so cannot be colocated with a iu turn
|
||||
continue;
|
||||
}
|
||||
|
||||
for (set_iterator int_it = ids.begin(); int_it != ids.end(); ++int_it)
|
||||
{
|
||||
if (*it == *int_it)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turn with, possibly, an interior ring involved
|
||||
turn_type& int_turn = turns[*int_it];
|
||||
segment_identifier const& int_seg_0 = int_turn.operations[0].seg_id;
|
||||
segment_identifier const& int_seg_1 = int_turn.operations[1].seg_id;
|
||||
|
||||
if (is_ie_turn<Reverse0, Reverse1>(seg_0, seg_1, int_seg_0, int_seg_1))
|
||||
{
|
||||
discard_ie_turn(int_turn, ids_to_remove, *int_it);
|
||||
}
|
||||
if (is_ie_turn<Reverse1, Reverse0>(seg_1, seg_0, int_seg_1, int_seg_0))
|
||||
{
|
||||
discard_ie_turn(int_turn, ids_to_remove, *int_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Erase from the ids (which cannot be done above)
|
||||
for (set_iterator sit = ids_to_remove.begin();
|
||||
sit != ids_to_remove.end(); ++sit)
|
||||
{
|
||||
ids.erase(*sit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Turns,
|
||||
typename Clusters
|
||||
>
|
||||
inline void set_colocation(Turns& turns, Clusters const& clusters)
|
||||
{
|
||||
typedef std::set<signed_size_type>::const_iterator set_iterator;
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
|
||||
for (typename Clusters::const_iterator cit = clusters.begin();
|
||||
cit != clusters.end(); ++cit)
|
||||
{
|
||||
cluster_info const& cinfo = cit->second;
|
||||
std::set<signed_size_type> const& ids = cinfo.turn_indices;
|
||||
|
||||
bool both_target = false;
|
||||
for (set_iterator it = ids.begin(); it != ids.end(); ++it)
|
||||
{
|
||||
turn_type const& turn = turns[*it];
|
||||
if (turn.both(operation_from_overlay<OverlayType>::value))
|
||||
{
|
||||
both_target = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (both_target)
|
||||
{
|
||||
for (set_iterator it = ids.begin(); it != ids.end(); ++it)
|
||||
{
|
||||
turn_type& turn = turns[*it];
|
||||
|
||||
if (both_target)
|
||||
{
|
||||
turn.has_colocated_both = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename Clusters
|
||||
>
|
||||
inline void check_colocation(bool& has_blocked,
|
||||
signed_size_type cluster_id, Turns const& turns, Clusters const& clusters)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
|
||||
has_blocked = false;
|
||||
|
||||
typename Clusters::const_iterator mit = clusters.find(cluster_id);
|
||||
if (mit == clusters.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cluster_info const& cinfo = mit->second;
|
||||
|
||||
for (std::set<signed_size_type>::const_iterator it
|
||||
= cinfo.turn_indices.begin();
|
||||
it != cinfo.turn_indices.end(); ++it)
|
||||
{
|
||||
turn_type const& turn = turns[*it];
|
||||
if (turn.any_blocked())
|
||||
{
|
||||
has_blocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checks colocated turns and flags combinations of uu/other, possibly a
|
||||
// combination of a ring touching another geometry's interior ring which is
|
||||
// tangential to the exterior ring
|
||||
|
||||
// This function can be extended to replace handle_tangencies: at each
|
||||
// colocation incoming and outgoing vectors should be inspected
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename Geometry1,
|
||||
typename Geometry2
|
||||
>
|
||||
inline bool handle_colocations(Turns& turns, Clusters& clusters,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2)
|
||||
{
|
||||
static const detail::overlay::operation_type target_operation
|
||||
= detail::overlay::operation_from_overlay<OverlayType>::value;
|
||||
typedef std::map
|
||||
<
|
||||
segment_identifier,
|
||||
std::vector<turn_operation_index>
|
||||
> map_type;
|
||||
|
||||
// Create and fill map on segment-identifier Map is sorted on seg_id,
|
||||
// meaning it is sorted on ring_identifier too. This means that exterior
|
||||
// rings are handled first. If there is a colocation on the exterior ring,
|
||||
// that information can be used for the interior ring too
|
||||
map_type map;
|
||||
|
||||
signed_size_type index = 0;
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it, ++index)
|
||||
{
|
||||
map[it->operations[0].seg_id].push_back(turn_operation_index(index, 0));
|
||||
map[it->operations[1].seg_id].push_back(turn_operation_index(index, 1));
|
||||
}
|
||||
|
||||
// Check if there are multiple turns on one or more segments,
|
||||
// if not then nothing is to be done
|
||||
bool colocations = 0;
|
||||
for (typename map_type::const_iterator it = map.begin();
|
||||
it != map.end();
|
||||
++it)
|
||||
{
|
||||
if (it->second.size() > 1u)
|
||||
{
|
||||
colocations = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! colocations)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort all vectors, per same segment
|
||||
less_by_fraction_and_type<Turns> less(turns);
|
||||
for (typename map_type::iterator it = map.begin();
|
||||
it != map.end(); ++it)
|
||||
{
|
||||
std::sort(it->second.begin(), it->second.end(), less);
|
||||
}
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::segment_ratio_type segment_ratio_type;
|
||||
|
||||
typedef std::map
|
||||
<
|
||||
segment_fraction<segment_ratio_type>,
|
||||
signed_size_type
|
||||
> cluster_per_segment_type;
|
||||
|
||||
cluster_per_segment_type cluster_per_segment;
|
||||
|
||||
// Assign to zero, because of pre-increment later the cluster_id
|
||||
// effectively starts with 1
|
||||
// (and can later be negated to use uniquely with turn_index)
|
||||
signed_size_type cluster_id = 0;
|
||||
|
||||
for (typename map_type::const_iterator it = map.begin();
|
||||
it != map.end(); ++it)
|
||||
{
|
||||
if (it->second.size() > 1u)
|
||||
{
|
||||
handle_colocation_cluster(turns, cluster_id, cluster_per_segment,
|
||||
it->second, geometry1, geometry2);
|
||||
}
|
||||
}
|
||||
|
||||
assign_cluster_to_turns(turns, clusters, cluster_per_segment);
|
||||
// Get colocated information here and not later, to keep information
|
||||
// on turns which are discarded afterwards
|
||||
set_colocation<OverlayType>(turns, clusters);
|
||||
|
||||
if (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
|
||||
{
|
||||
discard_interior_exterior_turns
|
||||
<
|
||||
do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse1,
|
||||
do_reverse<geometry::point_order<Geometry2>::value>::value != Reverse2
|
||||
>(turns, clusters);
|
||||
}
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
|
||||
std::cout << "*** Colocations " << map.size() << std::endl;
|
||||
for (typename map_type::const_iterator it = map.begin();
|
||||
it != map.end(); ++it)
|
||||
{
|
||||
std::cout << it->first << std::endl;
|
||||
for (std::vector<turn_operation_index>::const_iterator vit
|
||||
= it->second.begin(); vit != it->second.end(); ++vit)
|
||||
{
|
||||
turn_operation_index const& toi = *vit;
|
||||
std::cout << geometry::wkt(turns[toi.turn_index].point)
|
||||
<< std::boolalpha
|
||||
<< " discarded=" << turns[toi.turn_index].discarded
|
||||
<< " colocated(uu)=" << turns[toi.turn_index].colocated_uu
|
||||
<< " colocated(ii)=" << turns[toi.turn_index].colocated_ii
|
||||
<< " " << operation_char(turns[toi.turn_index].operations[0].operation)
|
||||
<< " " << turns[toi.turn_index].operations[0].seg_id
|
||||
<< " " << turns[toi.turn_index].operations[0].fraction
|
||||
<< " // " << operation_char(turns[toi.turn_index].operations[1].operation)
|
||||
<< " " << turns[toi.turn_index].operations[1].seg_id
|
||||
<< " " << turns[toi.turn_index].operations[1].fraction
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct is_turn_index
|
||||
{
|
||||
is_turn_index(signed_size_type index)
|
||||
: m_index(index)
|
||||
{}
|
||||
|
||||
template <typename Indexed>
|
||||
inline bool operator()(Indexed const& indexed) const
|
||||
{
|
||||
// Indexed is a indexed_turn_operation<Operation>
|
||||
return indexed.turn_index == m_index;
|
||||
}
|
||||
|
||||
std::size_t m_index;
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename SideStrategy
|
||||
>
|
||||
inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
|
||||
operation_type for_operation,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
SideStrategy const& strategy)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::point_type point_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
|
||||
// Define sorter, sorting counter-clockwise such that polygons are on the
|
||||
// right side
|
||||
typedef sort_by_side::side_sorter
|
||||
<
|
||||
Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less<int>
|
||||
> sbs_type;
|
||||
|
||||
for (typename Clusters::iterator mit = clusters.begin();
|
||||
mit != clusters.end(); ++mit)
|
||||
{
|
||||
cluster_info& cinfo = mit->second;
|
||||
std::set<signed_size_type> const& ids = cinfo.turn_indices;
|
||||
if (ids.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sbs_type sbs(strategy);
|
||||
point_type turn_point; // should be all the same for all turns in cluster
|
||||
|
||||
bool first = true;
|
||||
for (std::set<signed_size_type>::const_iterator sit = ids.begin();
|
||||
sit != ids.end(); ++sit)
|
||||
{
|
||||
signed_size_type turn_index = *sit;
|
||||
turn_type const& turn = turns[turn_index];
|
||||
if (first)
|
||||
{
|
||||
turn_point = turn.point;
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
turn_operation_type const& op = turn.operations[i];
|
||||
sbs.add(op, turn_index, i, geometry1, geometry2, first);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
sbs.apply(turn_point);
|
||||
|
||||
sbs.find_open();
|
||||
sbs.assign_zones(for_operation);
|
||||
|
||||
cinfo.open_count = sbs.open_count(for_operation);
|
||||
|
||||
bool const set_startable = OverlayType != overlay_dissolve;
|
||||
|
||||
// Unset the startable flag for all 'closed' zones. This does not
|
||||
// apply for self-turns, because those counts are not from both
|
||||
// polygons
|
||||
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
|
||||
{
|
||||
const typename sbs_type::rp& ranked = sbs.m_ranked_points[i];
|
||||
turn_type& turn = turns[ranked.turn_index];
|
||||
turn_operation_type& op = turn.operations[ranked.operation_index];
|
||||
|
||||
if (set_startable
|
||||
&& for_operation == operation_union && cinfo.open_count == 0)
|
||||
{
|
||||
op.enriched.startable = false;
|
||||
}
|
||||
|
||||
if (ranked.direction != sort_by_side::dir_to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
op.enriched.count_left = ranked.count_left;
|
||||
op.enriched.count_right = ranked.count_right;
|
||||
op.enriched.rank = ranked.rank;
|
||||
op.enriched.zone = ranked.zone;
|
||||
|
||||
if (! set_startable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OverlayType != overlay_difference
|
||||
&& is_self_turn<OverlayType>(turn))
|
||||
{
|
||||
// Difference needs the self-turns, TODO: investigate
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((for_operation == operation_union
|
||||
&& ranked.count_left != 0)
|
||||
|| (for_operation == operation_intersection
|
||||
&& ranked.count_right != 2))
|
||||
{
|
||||
op.enriched.startable = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
|
||||
@@ -0,0 +1,219 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/covered_by.hpp>
|
||||
#include <boost/geometry/algorithms/within.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <overlay_type OverlayType>
|
||||
struct check_within
|
||||
{
|
||||
template <typename Turn, typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
bool apply(Turn const& turn, Geometry0 const& geometry0,
|
||||
Geometry1 const& geometry1)
|
||||
{
|
||||
// Operations 0 and 1 have the same source index in self-turns
|
||||
return turn.operations[0].seg_id.source_index == 0
|
||||
? geometry::within(turn.point, geometry1)
|
||||
: geometry::within(turn.point, geometry0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct check_within<overlay_difference>
|
||||
{
|
||||
template <typename Turn, typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
bool apply(Turn const& turn, Geometry0 const& geometry0,
|
||||
Geometry1 const& geometry1)
|
||||
{
|
||||
// difference = intersection(a, reverse(b))
|
||||
// therefore we should reverse the meaning of within for geometry1
|
||||
return turn.operations[0].seg_id.source_index == 0
|
||||
? ! geometry::covered_by(turn.point, geometry1)
|
||||
: geometry::within(turn.point, geometry0);
|
||||
}
|
||||
};
|
||||
|
||||
struct discard_turns
|
||||
{
|
||||
template <typename Turns, typename Clusters, typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
void apply(Turns& , Clusters const& , Geometry0 const& , Geometry1 const& )
|
||||
{}
|
||||
};
|
||||
|
||||
template <overlay_type OverlayType, operation_type OperationType>
|
||||
struct discard_closed_turns : discard_turns {};
|
||||
|
||||
// It is only implemented for operation_union, not in buffer
|
||||
template <>
|
||||
struct discard_closed_turns<overlay_union, operation_union>
|
||||
{
|
||||
|
||||
template <typename Turns, typename Clusters, typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
void apply(Turns& turns, Clusters const& /*clusters*/,
|
||||
Geometry0 const& geometry0, Geometry1 const& geometry1)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
|
||||
if (! turn.discarded
|
||||
&& is_self_turn<overlay_union>(turn)
|
||||
&& check_within<overlay_union>::apply(turn,
|
||||
geometry0, geometry1))
|
||||
{
|
||||
// Turn is in the interior of other geometry
|
||||
turn.discarded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <overlay_type OverlayType>
|
||||
struct discard_self_intersection_turns
|
||||
{
|
||||
private :
|
||||
|
||||
template <typename Turns, typename Clusters>
|
||||
static inline
|
||||
bool is_self_cluster(signed_size_type cluster_id,
|
||||
const Turns& turns, Clusters const& clusters)
|
||||
{
|
||||
typename Clusters::const_iterator cit = clusters.find(cluster_id);
|
||||
if (cit == clusters.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cluster_info const& cinfo = cit->second;
|
||||
for (std::set<signed_size_type>::const_iterator it
|
||||
= cinfo.turn_indices.begin();
|
||||
it != cinfo.turn_indices.end(); ++it)
|
||||
{
|
||||
if (! is_self_turn<OverlayType>(turns[*it]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Turns, typename Clusters,
|
||||
typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
void discard_clusters(Turns& turns, Clusters const& clusters,
|
||||
Geometry0 const& geometry0, Geometry1 const& geometry1)
|
||||
{
|
||||
for (typename Clusters::const_iterator cit = clusters.begin();
|
||||
cit != clusters.end(); ++cit)
|
||||
{
|
||||
signed_size_type const cluster_id = cit->first;
|
||||
|
||||
// If there are only self-turns in the cluster, the cluster should
|
||||
// be located within the other geometry, for intersection
|
||||
if (! cit->second.turn_indices.empty()
|
||||
&& is_self_cluster(cluster_id, turns, clusters))
|
||||
{
|
||||
cluster_info const& cinfo = cit->second;
|
||||
signed_size_type const index = *cinfo.turn_indices.begin();
|
||||
if (! check_within<OverlayType>::apply(turns[index],
|
||||
geometry0, geometry1))
|
||||
{
|
||||
// Discard all turns in cluster
|
||||
for (std::set<signed_size_type>::const_iterator sit
|
||||
= cinfo.turn_indices.begin();
|
||||
sit != cinfo.turn_indices.end(); ++sit)
|
||||
{
|
||||
turns[*sit].discarded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public :
|
||||
|
||||
template <typename Turns, typename Clusters,
|
||||
typename Geometry0, typename Geometry1>
|
||||
static inline
|
||||
void apply(Turns& turns, Clusters const& clusters,
|
||||
Geometry0 const& geometry0, Geometry1 const& geometry1)
|
||||
{
|
||||
discard_clusters(turns, clusters, geometry0, geometry1);
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type& turn = *it;
|
||||
|
||||
// It is a ii self-turn
|
||||
// Check if it is within the other geometry
|
||||
if (! turn.discarded
|
||||
&& is_self_turn<overlay_intersection>(turn)
|
||||
&& ! check_within<OverlayType>::apply(turn, geometry0, geometry1))
|
||||
{
|
||||
// It is not within another geometry, set it as non startable.
|
||||
// It still might be traveled (#case_recursive_boxes_70)
|
||||
turn.operations[0].enriched.startable = false;
|
||||
turn.operations[1].enriched.startable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <overlay_type OverlayType, operation_type OperationType>
|
||||
struct discard_open_turns : discard_turns {};
|
||||
|
||||
// Handler for intersection
|
||||
template <>
|
||||
struct discard_open_turns<overlay_intersection, operation_intersection>
|
||||
: discard_self_intersection_turns<overlay_intersection> {};
|
||||
|
||||
// Handler for difference, with different meaning of 'within'
|
||||
template <>
|
||||
struct discard_open_turns<overlay_difference, operation_intersection>
|
||||
: discard_self_intersection_turns<overlay_difference> {};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014-2015, Oracle and/or its affiliates.
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INCONSISTENT_TURNS_EXCEPTION_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INCONSISTENT_TURNS_EXCEPTION_HPP
|
||||
|
||||
#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
|
||||
#include <boost/geometry/core/exception.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
class inconsistent_turns_exception : public geometry::exception
|
||||
{
|
||||
public:
|
||||
|
||||
inline inconsistent_turns_exception() {}
|
||||
|
||||
virtual ~inconsistent_turns_exception() throw()
|
||||
{}
|
||||
|
||||
virtual char const* what() const throw()
|
||||
{
|
||||
return "Boost.Geometry Inconsistent Turns exception";
|
||||
}
|
||||
};
|
||||
|
||||
}} // boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_OVERLAY_NO_THROW
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INCONSISTENT_TURNS_EXCEPTION_HPP
|
||||
@@ -0,0 +1,96 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2015.
|
||||
// Modifications copyright (c) 2015, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/core/coordinate_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace intersection
|
||||
{
|
||||
|
||||
template <std::size_t Dimension, std::size_t DimensionCount>
|
||||
struct intersection_box_box
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Box1, typename Box2,
|
||||
typename RobustPolicy,
|
||||
typename BoxOut,
|
||||
typename Strategy
|
||||
>
|
||||
static inline bool apply(Box1 const& box1,
|
||||
Box2 const& box2,
|
||||
RobustPolicy const& robust_policy,
|
||||
BoxOut& box_out,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename coordinate_type<BoxOut>::type ct;
|
||||
|
||||
ct max1 = get<max_corner, Dimension>(box1);
|
||||
ct min2 = get<min_corner, Dimension>(box2);
|
||||
|
||||
if (max1 < min2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ct max2 = get<max_corner, Dimension>(box2);
|
||||
ct min1 = get<min_corner, Dimension>(box1);
|
||||
|
||||
if (max2 < min1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set dimensions of output coordinate
|
||||
set<min_corner, Dimension>(box_out, min1 < min2 ? min2 : min1);
|
||||
set<max_corner, Dimension>(box_out, max1 > max2 ? max2 : max1);
|
||||
|
||||
return intersection_box_box<Dimension + 1, DimensionCount>
|
||||
::apply(box1, box2, robust_policy, box_out, strategy);
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t DimensionCount>
|
||||
struct intersection_box_box<DimensionCount, DimensionCount>
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Box1, typename Box2,
|
||||
typename RobustPolicy,
|
||||
typename BoxOut,
|
||||
typename Strategy
|
||||
>
|
||||
static inline bool apply(Box1 const&, Box2 const&,
|
||||
RobustPolicy const&, BoxOut&, Strategy const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::intersection
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2017-2017 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <overlay_type OverlayType>
|
||||
struct is_self_turn_check
|
||||
{
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const& turn)
|
||||
{
|
||||
return turn.operations[0].seg_id.source_index
|
||||
== turn.operations[1].seg_id.source_index;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_self_turn_check<overlay_buffer>
|
||||
{
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const& /*turn*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_self_turn_check<overlay_dissolve>
|
||||
{
|
||||
template <typename Turn>
|
||||
static inline bool apply(Turn const& /*turn*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <overlay_type OverlayType, typename Turn>
|
||||
bool is_self_turn(Turn const& turn)
|
||||
{
|
||||
return is_self_turn_check<OverlayType>::apply(turn);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP
|
||||
@@ -0,0 +1,204 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_ON_SEGMENT_RATIO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_ON_SEGMENT_RATIO_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
|
||||
#include <boost/geometry/strategies/side.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
// Wraps "turn_operation" from turn_info.hpp,
|
||||
// giving it extra information, necessary for sorting
|
||||
template <typename TurnOperation>
|
||||
struct indexed_turn_operation
|
||||
{
|
||||
typedef TurnOperation type;
|
||||
|
||||
std::size_t turn_index;
|
||||
std::size_t operation_index;
|
||||
bool skip;
|
||||
// use pointers to avoid copies, const& is not possible because of usage in vector
|
||||
segment_identifier const* other_seg_id; // segment id of other segment of intersection of two segments
|
||||
TurnOperation const* subject;
|
||||
|
||||
inline indexed_turn_operation(std::size_t ti, std::size_t oi,
|
||||
TurnOperation const& sub,
|
||||
segment_identifier const& oid)
|
||||
: turn_index(ti)
|
||||
, operation_index(oi)
|
||||
, skip(false)
|
||||
, other_seg_id(&oid)
|
||||
, subject(boost::addressof(sub))
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename Indexed,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RobustPolicy,
|
||||
typename SideStrategy,
|
||||
bool Reverse1, bool Reverse2
|
||||
>
|
||||
struct less_by_segment_ratio
|
||||
{
|
||||
inline less_by_segment_ratio(Turns const& turns
|
||||
, Geometry1 const& geometry1
|
||||
, Geometry2 const& geometry2
|
||||
, RobustPolicy const& robust_policy
|
||||
, SideStrategy const& strategy)
|
||||
: m_turns(turns)
|
||||
, m_geometry1(geometry1)
|
||||
, m_geometry2(geometry2)
|
||||
, m_robust_policy(robust_policy)
|
||||
, m_strategy(strategy)
|
||||
{
|
||||
}
|
||||
|
||||
private :
|
||||
|
||||
Turns const& m_turns;
|
||||
Geometry1 const& m_geometry1;
|
||||
Geometry2 const& m_geometry2;
|
||||
RobustPolicy const& m_robust_policy;
|
||||
SideStrategy const& m_strategy;
|
||||
|
||||
typedef typename geometry::point_type<Geometry1>::type point_type;
|
||||
|
||||
inline bool default_order(Indexed const& left, Indexed const& right) const
|
||||
{
|
||||
// We've nothing to sort on. Take the indexes
|
||||
return left.turn_index < right.turn_index;
|
||||
}
|
||||
|
||||
inline bool consider_relative_order(Indexed const& left,
|
||||
Indexed const& right) const
|
||||
{
|
||||
point_type pi, pj, ri, rj, si, sj;
|
||||
|
||||
geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
|
||||
left.subject->seg_id,
|
||||
pi, pj);
|
||||
geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
|
||||
*left.other_seg_id,
|
||||
ri, rj);
|
||||
geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
|
||||
*right.other_seg_id,
|
||||
si, sj);
|
||||
|
||||
int const side_rj_p = m_strategy.apply(pi, pj, rj);
|
||||
int const side_sj_p = m_strategy.apply(pi, pj, sj);
|
||||
|
||||
// Put the one turning left (1; right == -1) as last
|
||||
if (side_rj_p != side_sj_p)
|
||||
{
|
||||
return side_rj_p < side_sj_p;
|
||||
}
|
||||
|
||||
int const side_sj_r = m_strategy.apply(ri, rj, sj);
|
||||
int const side_rj_s = m_strategy.apply(si, sj, rj);
|
||||
|
||||
// If they both turn left: the most left as last
|
||||
// If they both turn right: this is not relevant, but take also here most left
|
||||
if (side_rj_s != side_sj_r)
|
||||
{
|
||||
return side_rj_s < side_sj_r;
|
||||
}
|
||||
|
||||
return default_order(left, right);
|
||||
}
|
||||
|
||||
|
||||
public :
|
||||
|
||||
// Note that left/right do NOT correspond to m_geometry1/m_geometry2
|
||||
// but to the "indexed_turn_operation"
|
||||
inline bool operator()(Indexed const& left, Indexed const& right) const
|
||||
{
|
||||
if (! (left.subject->seg_id == right.subject->seg_id))
|
||||
{
|
||||
return left.subject->seg_id < right.subject->seg_id;
|
||||
}
|
||||
|
||||
// Both left and right are located on the SAME segment.
|
||||
|
||||
if (! (left.subject->fraction == right.subject->fraction))
|
||||
{
|
||||
return left.subject->fraction < right.subject->fraction;
|
||||
}
|
||||
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
turn_type const& left_turn = m_turns[left.turn_index];
|
||||
turn_type const& right_turn = m_turns[right.turn_index];
|
||||
|
||||
// First check "real" intersection (crosses)
|
||||
// -> distance zero due to precision, solve it by sorting
|
||||
if (left_turn.method == method_crosses
|
||||
&& right_turn.method == method_crosses)
|
||||
{
|
||||
return consider_relative_order(left, right);
|
||||
}
|
||||
|
||||
bool const left_both_xx = left_turn.both(operation_blocked);
|
||||
bool const right_both_xx = right_turn.both(operation_blocked);
|
||||
if (left_both_xx && ! right_both_xx)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (! left_both_xx && right_both_xx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool const left_both_uu = left_turn.both(operation_union);
|
||||
bool const right_both_uu = right_turn.both(operation_union);
|
||||
if (left_both_uu && ! right_both_uu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (! left_both_uu && right_both_uu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return default_order(left, right);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_ON_SEGMENT_RATIO_HPP
|
||||
@@ -0,0 +1,332 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014-2017, Oracle and/or its affiliates.
|
||||
|
||||
// Licensed under the Boost Software License version 1.0.
|
||||
// http://www.boost.org/users/license.html
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/relate/turns.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/turns/compare_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/turns/filter_continue_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/turns/remove_duplicate_turns.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename LineStringOut,
|
||||
overlay_type OverlayType,
|
||||
typename Geometry,
|
||||
typename GeometryTag
|
||||
>
|
||||
struct linear_linear_no_intersections;
|
||||
|
||||
|
||||
template <typename LineStringOut, typename LineString>
|
||||
struct linear_linear_no_intersections
|
||||
<
|
||||
LineStringOut, overlay_difference, LineString, linestring_tag
|
||||
>
|
||||
{
|
||||
template <typename OutputIterator>
|
||||
static inline OutputIterator apply(LineString const& linestring,
|
||||
OutputIterator oit)
|
||||
{
|
||||
LineStringOut ls_out;
|
||||
geometry::convert(linestring, ls_out);
|
||||
*oit++ = ls_out;
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename LineStringOut, typename MultiLineString>
|
||||
struct linear_linear_no_intersections
|
||||
<
|
||||
LineStringOut,
|
||||
overlay_difference,
|
||||
MultiLineString,
|
||||
multi_linestring_tag
|
||||
>
|
||||
{
|
||||
template <typename OutputIterator>
|
||||
static inline OutputIterator apply(MultiLineString const& multilinestring,
|
||||
OutputIterator oit)
|
||||
{
|
||||
for (typename boost::range_iterator<MultiLineString const>::type
|
||||
it = boost::begin(multilinestring);
|
||||
it != boost::end(multilinestring); ++it)
|
||||
{
|
||||
LineStringOut ls_out;
|
||||
geometry::convert(*it, ls_out);
|
||||
*oit++ = ls_out;
|
||||
}
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename LineStringOut, typename Geometry, typename GeometryTag>
|
||||
struct linear_linear_no_intersections
|
||||
<
|
||||
LineStringOut, overlay_intersection, Geometry, GeometryTag
|
||||
>
|
||||
{
|
||||
template <typename OutputIterator>
|
||||
static inline OutputIterator apply(Geometry const&,
|
||||
OutputIterator oit)
|
||||
{
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Linear1,
|
||||
typename Linear2,
|
||||
typename LinestringOut,
|
||||
overlay_type OverlayType,
|
||||
bool EnableFilterContinueTurns = false,
|
||||
bool EnableRemoveDuplicateTurns = false,
|
||||
bool EnableDegenerateTurns = true,
|
||||
#ifdef BOOST_GEOMETRY_INTERSECTION_DO_NOT_INCLUDE_ISOLATED_POINTS
|
||||
bool EnableFollowIsolatedPoints = false
|
||||
#else
|
||||
bool EnableFollowIsolatedPoints = true
|
||||
#endif
|
||||
>
|
||||
class linear_linear_linestring
|
||||
{
|
||||
protected:
|
||||
struct assign_policy
|
||||
{
|
||||
static bool const include_no_turn = false;
|
||||
static bool const include_degenerate = EnableDegenerateTurns;
|
||||
static bool const include_opposite = false;
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Turns,
|
||||
typename LinearGeometry1,
|
||||
typename LinearGeometry2,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy
|
||||
>
|
||||
static inline void compute_turns(Turns& turns,
|
||||
LinearGeometry1 const& linear1,
|
||||
LinearGeometry2 const& linear2,
|
||||
IntersectionStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy)
|
||||
{
|
||||
turns.clear();
|
||||
|
||||
detail::get_turns::no_interrupt_policy interrupt_policy;
|
||||
|
||||
geometry::detail::relate::turns::get_turns
|
||||
<
|
||||
LinearGeometry1,
|
||||
LinearGeometry2,
|
||||
detail::get_turns::get_turn_info_type
|
||||
<
|
||||
LinearGeometry1,
|
||||
LinearGeometry2,
|
||||
assign_policy
|
||||
>,
|
||||
RobustPolicy
|
||||
>::apply(turns, linear1, linear2, interrupt_policy, strategy, robust_policy);
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayTypeForFollow,
|
||||
bool FollowIsolatedPoints,
|
||||
typename Turns,
|
||||
typename LinearGeometry1,
|
||||
typename LinearGeometry2,
|
||||
typename OutputIterator,
|
||||
typename IntersectionStrategy
|
||||
>
|
||||
static inline OutputIterator
|
||||
sort_and_follow_turns(Turns& turns,
|
||||
LinearGeometry1 const& linear1,
|
||||
LinearGeometry2 const& linear2,
|
||||
OutputIterator oit,
|
||||
IntersectionStrategy const& strategy)
|
||||
{
|
||||
// remove turns that have no added value
|
||||
turns::filter_continue_turns
|
||||
<
|
||||
Turns,
|
||||
EnableFilterContinueTurns && OverlayType != overlay_intersection
|
||||
>::apply(turns);
|
||||
|
||||
// sort by seg_id, distance, and operation
|
||||
std::sort(boost::begin(turns), boost::end(turns),
|
||||
detail::turns::less_seg_fraction_other_op<>());
|
||||
|
||||
// remove duplicate turns
|
||||
turns::remove_duplicate_turns
|
||||
<
|
||||
Turns, EnableRemoveDuplicateTurns
|
||||
>::apply(turns);
|
||||
|
||||
return detail::overlay::following::linear::follow
|
||||
<
|
||||
LinestringOut,
|
||||
LinearGeometry1,
|
||||
LinearGeometry2,
|
||||
OverlayTypeForFollow,
|
||||
FollowIsolatedPoints,
|
||||
!EnableFilterContinueTurns || OverlayType == overlay_intersection
|
||||
>::apply(linear1, linear2, boost::begin(turns), boost::end(turns),
|
||||
oit, strategy.get_side_strategy());
|
||||
}
|
||||
|
||||
public:
|
||||
template
|
||||
<
|
||||
typename RobustPolicy, typename OutputIterator, typename Strategy
|
||||
>
|
||||
static inline OutputIterator apply(Linear1 const& linear1,
|
||||
Linear2 const& linear2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename detail::relate::turns::get_turns
|
||||
<
|
||||
Linear1,
|
||||
Linear2,
|
||||
detail::get_turns::get_turn_info_type
|
||||
<
|
||||
Linear1,
|
||||
Linear2,
|
||||
assign_policy
|
||||
>,
|
||||
RobustPolicy
|
||||
>::turn_info turn_info;
|
||||
|
||||
typedef std::vector<turn_info> turns_container;
|
||||
|
||||
turns_container turns;
|
||||
compute_turns(turns, linear1, linear2, strategy, robust_policy);
|
||||
|
||||
if ( turns.empty() )
|
||||
{
|
||||
// the two linear geometries are disjoint
|
||||
return linear_linear_no_intersections
|
||||
<
|
||||
LinestringOut,
|
||||
OverlayType,
|
||||
Linear1,
|
||||
typename tag<Linear1>::type
|
||||
>::apply(linear1, oit);
|
||||
}
|
||||
|
||||
return sort_and_follow_turns
|
||||
<
|
||||
OverlayType,
|
||||
EnableFollowIsolatedPoints
|
||||
&& OverlayType == overlay_intersection
|
||||
>(turns, linear1, linear2, oit, strategy);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Linear1,
|
||||
typename Linear2,
|
||||
typename LinestringOut,
|
||||
bool EnableFilterContinueTurns,
|
||||
bool EnableRemoveDuplicateTurns,
|
||||
bool EnableDegenerateTurns,
|
||||
bool EnableFollowIsolatedPoints
|
||||
>
|
||||
struct linear_linear_linestring
|
||||
<
|
||||
Linear1, Linear2, LinestringOut, overlay_union,
|
||||
EnableFilterContinueTurns, EnableRemoveDuplicateTurns,
|
||||
EnableDegenerateTurns, EnableFollowIsolatedPoints
|
||||
>
|
||||
{
|
||||
template
|
||||
<
|
||||
typename RobustPolicy, typename OutputIterator, typename Strategy
|
||||
>
|
||||
static inline OutputIterator apply(Linear1 const& linear1,
|
||||
Linear2 const& linear2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
oit = linear_linear_no_intersections
|
||||
<
|
||||
LinestringOut,
|
||||
overlay_difference,
|
||||
Linear1,
|
||||
typename tag<Linear1>::type
|
||||
>::apply(linear1, oit);
|
||||
|
||||
return linear_linear_linestring
|
||||
<
|
||||
Linear2, Linear1, LinestringOut, overlay_difference,
|
||||
EnableFilterContinueTurns, EnableRemoveDuplicateTurns,
|
||||
EnableDegenerateTurns, EnableFollowIsolatedPoints
|
||||
>::apply(linear2, linear1, robust_policy, oit, strategy);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP
|
||||
@@ -0,0 +1,83 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2017-2017 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
#include <boost/geometry/algorithms/num_interior_rings.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename tag<Geometry>::type
|
||||
>
|
||||
struct needs_self_turns
|
||||
{
|
||||
};
|
||||
|
||||
template <typename Geometry>
|
||||
struct needs_self_turns<Geometry, box_tag>
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Geometry>
|
||||
struct needs_self_turns<Geometry, ring_tag>
|
||||
{
|
||||
static inline bool apply(Geometry const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Geometry>
|
||||
struct needs_self_turns<Geometry, polygon_tag>
|
||||
{
|
||||
static inline bool apply(Geometry const& polygon)
|
||||
{
|
||||
return geometry::num_interior_rings(polygon) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Geometry>
|
||||
struct needs_self_turns<Geometry, multi_polygon_tag>
|
||||
{
|
||||
static inline bool apply(Geometry const& multi)
|
||||
{
|
||||
typedef typename boost::range_value<Geometry>::type polygon_type;
|
||||
std::size_t const n = boost::size(multi);
|
||||
return n > 1 || (n == 1
|
||||
&& needs_self_turns<polygon_type>
|
||||
::apply(*boost::begin(multi)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP
|
||||
@@ -0,0 +1,442 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2013-2017 Adam Wulkiewicz, Lodz, Poland
|
||||
|
||||
// This file was modified by Oracle on 2015, 2017.
|
||||
// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP
|
||||
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/mpl/assert.hpp>
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/traverse.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/recalculate.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/is_empty.hpp>
|
||||
#include <boost/geometry/algorithms/reverse.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/add_rings.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/select_rings.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
|
||||
|
||||
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
|
||||
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
|
||||
# include <boost/geometry/io/dsv/write.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
//! Default visitor for overlay, doing nothing
|
||||
struct overlay_null_visitor
|
||||
{
|
||||
void print(char const* ) {}
|
||||
|
||||
template <typename Turns>
|
||||
void print(char const* , Turns const& , int) {}
|
||||
|
||||
template <typename Turns>
|
||||
void print(char const* , Turns const& , int , int ) {}
|
||||
|
||||
template <typename Turns>
|
||||
void visit_turns(int , Turns const& ) {}
|
||||
|
||||
template <typename Clusters, typename Turns>
|
||||
void visit_clusters(Clusters const& , Turns const& ) {}
|
||||
|
||||
template <typename Turns, typename Turn, typename Operation>
|
||||
void visit_traverse(Turns const& , Turn const& , Operation const& , char const*)
|
||||
{}
|
||||
|
||||
template <typename Turns, typename Turn, typename Operation>
|
||||
void visit_traverse_reject(Turns const& , Turn const& , Operation const& , traverse_error_type )
|
||||
{}
|
||||
|
||||
template <typename Rings>
|
||||
void visit_generated_rings(Rings const& )
|
||||
{}
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename TurnInfoMap,
|
||||
typename Turns,
|
||||
typename Clusters
|
||||
>
|
||||
inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, Clusters const& clusters)
|
||||
{
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
typedef typename turn_type::container_type container_type;
|
||||
|
||||
static const operation_type target_operation
|
||||
= operation_from_overlay<OverlayType>::value;
|
||||
static const operation_type opposite_operation
|
||||
= target_operation == operation_union
|
||||
? operation_intersection
|
||||
: operation_union;
|
||||
|
||||
for (typename boost::range_iterator<Turns const>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
turn_type const& turn = *it;
|
||||
|
||||
bool cluster_checked = false;
|
||||
bool has_blocked = false;
|
||||
|
||||
if (is_self_turn<OverlayType>(turn) && turn.discarded)
|
||||
{
|
||||
// Discarded self-turns don't count as traversed
|
||||
continue;
|
||||
}
|
||||
|
||||
for (typename boost::range_iterator<container_type const>::type
|
||||
op_it = boost::begin(turn.operations);
|
||||
op_it != boost::end(turn.operations);
|
||||
++op_it)
|
||||
{
|
||||
turn_operation_type const& op = *op_it;
|
||||
ring_identifier const ring_id
|
||||
(
|
||||
op.seg_id.source_index,
|
||||
op.seg_id.multi_index,
|
||||
op.seg_id.ring_index
|
||||
);
|
||||
|
||||
if (! is_self_turn<OverlayType>(turn)
|
||||
&& (
|
||||
(BOOST_GEOMETRY_CONDITION(target_operation == operation_union)
|
||||
&& op.enriched.count_left > 0)
|
||||
|| (BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection)
|
||||
&& op.enriched.count_right <= 2)))
|
||||
{
|
||||
// Avoid including untraversed rings which have polygons on
|
||||
// their left side (union) or not two on their right side (int)
|
||||
// This can only be done for non-self-turns because of count
|
||||
// information
|
||||
turn_info_map[ring_id].has_blocked_turn = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (turn.any_blocked())
|
||||
{
|
||||
turn_info_map[ring_id].has_blocked_turn = true;
|
||||
}
|
||||
if (turn_info_map[ring_id].has_traversed_turn
|
||||
|| turn_info_map[ring_id].has_blocked_turn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check information in colocated turns
|
||||
if (! cluster_checked && turn.is_clustered())
|
||||
{
|
||||
check_colocation(has_blocked, turn.cluster_id, turns, clusters);
|
||||
cluster_checked = true;
|
||||
}
|
||||
|
||||
// Block rings where any other turn is blocked,
|
||||
// and (with exceptions): i for union and u for intersection
|
||||
// Exceptions: don't block self-uu for intersection
|
||||
// don't block self-ii for union
|
||||
// don't block (for union) i/u if there is an self-ii too
|
||||
if (has_blocked
|
||||
|| (op.operation == opposite_operation
|
||||
&& ! turn.has_colocated_both
|
||||
&& ! (turn.both(opposite_operation)
|
||||
&& is_self_turn<OverlayType>(turn))))
|
||||
{
|
||||
turn_info_map[ring_id].has_blocked_turn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename GeometryOut, overlay_type OverlayType, bool ReverseOut,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename OutputIterator, typename Strategy
|
||||
>
|
||||
inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
OutputIterator out, Strategy const& strategy)
|
||||
{
|
||||
typedef std::deque
|
||||
<
|
||||
typename geometry::ring_type<GeometryOut>::type
|
||||
> ring_container_type;
|
||||
|
||||
typedef typename geometry::point_type<Geometry1>::type point_type1;
|
||||
|
||||
typedef ring_properties
|
||||
<
|
||||
point_type1,
|
||||
typename Strategy::template area_strategy
|
||||
<
|
||||
point_type1
|
||||
>::type::template result_type<point_type1>::type
|
||||
> properties;
|
||||
|
||||
// Silence warning C4127: conditional expression is constant
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
// Union: return either of them
|
||||
// Intersection: return nothing
|
||||
// Difference: return first of them
|
||||
if (OverlayType == overlay_intersection
|
||||
|| (OverlayType == overlay_difference && geometry::is_empty(geometry1)))
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
std::map<ring_identifier, ring_turn_info> empty;
|
||||
std::map<ring_identifier, properties> all_of_one_of_them;
|
||||
|
||||
select_rings<OverlayType>(geometry1, geometry2, empty, all_of_one_of_them, strategy);
|
||||
ring_container_type rings;
|
||||
assign_parents<OverlayType>(geometry1, geometry2, rings, all_of_one_of_them, strategy);
|
||||
return add_rings<GeometryOut>(all_of_one_of_them, geometry1, geometry2, rings, out,
|
||||
strategy.template get_area_strategy<point_type1>());
|
||||
}
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry1, typename Geometry2,
|
||||
bool Reverse1, bool Reverse2, bool ReverseOut,
|
||||
typename GeometryOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct overlay
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy, typename Visitor>
|
||||
static inline OutputIterator apply(
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out,
|
||||
Strategy const& strategy,
|
||||
Visitor& visitor)
|
||||
{
|
||||
bool const is_empty1 = geometry::is_empty(geometry1);
|
||||
bool const is_empty2 = geometry::is_empty(geometry2);
|
||||
|
||||
if (is_empty1 && is_empty2)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
if (is_empty1 || is_empty2)
|
||||
{
|
||||
return return_if_one_input_is_empty
|
||||
<
|
||||
GeometryOut, OverlayType, ReverseOut
|
||||
>(geometry1, geometry2, out, strategy);
|
||||
}
|
||||
|
||||
typedef typename geometry::point_type<GeometryOut>::type point_type;
|
||||
typedef detail::overlay::traversal_turn_info
|
||||
<
|
||||
point_type,
|
||||
typename geometry::segment_ratio_type<point_type, RobustPolicy>::type
|
||||
> turn_info;
|
||||
typedef std::deque<turn_info> turn_container_type;
|
||||
|
||||
typedef std::deque
|
||||
<
|
||||
typename geometry::ring_type<GeometryOut>::type
|
||||
> ring_container_type;
|
||||
|
||||
// Define the clusters, mapping cluster_id -> turns
|
||||
typedef std::map
|
||||
<
|
||||
signed_size_type,
|
||||
cluster_info
|
||||
> cluster_type;
|
||||
|
||||
turn_container_type turns;
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
|
||||
std::cout << "get turns" << std::endl;
|
||||
#endif
|
||||
detail::get_turns::no_interrupt_policy policy;
|
||||
geometry::get_turns
|
||||
<
|
||||
Reverse1, Reverse2,
|
||||
detail::overlay::assign_null_policy
|
||||
>(geometry1, geometry2, strategy, robust_policy, turns, policy);
|
||||
|
||||
visitor.visit_turns(1, turns);
|
||||
|
||||
#if ! defined(BOOST_GEOMETRY_NO_SELF_TURNS)
|
||||
if (needs_self_turns<Geometry1>::apply(geometry1))
|
||||
{
|
||||
self_get_turn_points::self_turns<Reverse1, assign_null_policy>(geometry1,
|
||||
strategy, robust_policy, turns, policy, 0);
|
||||
}
|
||||
if (needs_self_turns<Geometry2>::apply(geometry2))
|
||||
{
|
||||
self_get_turn_points::self_turns<Reverse2, assign_null_policy>(geometry2,
|
||||
strategy, robust_policy, turns, policy, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
|
||||
std::cout << "enrich" << std::endl;
|
||||
#endif
|
||||
typename Strategy::side_strategy_type side_strategy = strategy.get_side_strategy();
|
||||
cluster_type clusters;
|
||||
std::map<ring_identifier, ring_turn_info> turn_info_per_ring;
|
||||
|
||||
geometry::enrich_intersection_points<Reverse1, Reverse2, OverlayType>(turns,
|
||||
clusters, geometry1, geometry2,
|
||||
robust_policy,
|
||||
side_strategy);
|
||||
|
||||
visitor.visit_turns(2, turns);
|
||||
|
||||
visitor.visit_clusters(clusters, turns);
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
|
||||
std::cout << "traverse" << std::endl;
|
||||
#endif
|
||||
// Traverse through intersection/turn points and create rings of them.
|
||||
// Note that these rings are always in clockwise order, even in CCW polygons,
|
||||
// and are marked as "to be reversed" below
|
||||
ring_container_type rings;
|
||||
traverse<Reverse1, Reverse2, Geometry1, Geometry2, OverlayType>::apply
|
||||
(
|
||||
geometry1, geometry2,
|
||||
strategy,
|
||||
robust_policy,
|
||||
turns, rings,
|
||||
turn_info_per_ring,
|
||||
clusters,
|
||||
visitor
|
||||
);
|
||||
visitor.visit_turns(3, turns);
|
||||
|
||||
get_ring_turn_info<OverlayType>(turn_info_per_ring, turns, clusters);
|
||||
|
||||
typedef typename Strategy::template area_strategy<point_type>::type area_strategy_type;
|
||||
|
||||
typedef ring_properties
|
||||
<
|
||||
point_type,
|
||||
typename area_strategy_type::template result_type<point_type>::type
|
||||
> properties;
|
||||
|
||||
// Select all rings which are NOT touched by any intersection point
|
||||
std::map<ring_identifier, properties> selected_ring_properties;
|
||||
select_rings<OverlayType>(geometry1, geometry2, turn_info_per_ring,
|
||||
selected_ring_properties, strategy);
|
||||
|
||||
// Add rings created during traversal
|
||||
area_strategy_type const area_strategy = strategy.template get_area_strategy<point_type>();
|
||||
{
|
||||
ring_identifier id(2, 0, -1);
|
||||
for (typename boost::range_iterator<ring_container_type>::type
|
||||
it = boost::begin(rings);
|
||||
it != boost::end(rings);
|
||||
++it)
|
||||
{
|
||||
selected_ring_properties[id] = properties(*it, area_strategy);
|
||||
selected_ring_properties[id].reversed = ReverseOut;
|
||||
id.multi_index++;
|
||||
}
|
||||
}
|
||||
|
||||
assign_parents<OverlayType>(geometry1, geometry2,
|
||||
rings, selected_ring_properties, strategy);
|
||||
|
||||
// NOTE: There is no need to check result area for union because
|
||||
// as long as the polygons in the input are valid the resulting
|
||||
// polygons should be valid as well.
|
||||
// By default the area is checked (this is old behavior) however this
|
||||
// can be changed with #define. This may be important in non-cartesian CSes.
|
||||
// The result may be too big, so the area is negative. In this case either
|
||||
// it can be returned or an exception can be thrown.
|
||||
return add_rings<GeometryOut>(selected_ring_properties, geometry1, geometry2, rings, out,
|
||||
area_strategy,
|
||||
OverlayType == overlay_union ?
|
||||
#if defined(BOOST_GEOMETRY_UNION_THROW_INVALID_OUTPUT_EXCEPTION)
|
||||
add_rings_throw_if_reversed
|
||||
#elif defined(BOOST_GEOMETRY_UNION_RETURN_INVALID)
|
||||
add_rings_add_unordered
|
||||
#else
|
||||
add_rings_ignore_unordered
|
||||
#endif
|
||||
: add_rings_ignore_unordered);
|
||||
}
|
||||
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator out,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
overlay_null_visitor visitor;
|
||||
return apply(geometry1, geometry2, robust_policy, out, strategy, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP
|
||||
@@ -0,0 +1,85 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP
|
||||
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
// TODO: move to detail
|
||||
enum overlay_type
|
||||
{
|
||||
overlay_union,
|
||||
overlay_intersection,
|
||||
overlay_difference,
|
||||
overlay_buffer,
|
||||
overlay_dissolve
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
enum operation_type
|
||||
{
|
||||
operation_none,
|
||||
operation_union,
|
||||
operation_intersection,
|
||||
operation_blocked,
|
||||
operation_continue,
|
||||
operation_opposite
|
||||
};
|
||||
|
||||
|
||||
template <overlay_type OverlayType>
|
||||
struct operation_from_overlay
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct operation_from_overlay<overlay_union>
|
||||
{
|
||||
static const operation_type value = operation_union;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct operation_from_overlay<overlay_buffer>
|
||||
{
|
||||
static const operation_type value = operation_union;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct operation_from_overlay<overlay_intersection>
|
||||
{
|
||||
static const operation_type value = operation_intersection;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct operation_from_overlay<overlay_difference>
|
||||
{
|
||||
static const operation_type value = operation_intersection;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct operation_from_overlay<overlay_dissolve>
|
||||
{
|
||||
static const operation_type value = operation_union;
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP
|
||||
@@ -0,0 +1,393 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// Copyright (c) 2015-2017, Oracle and/or its affiliates.
|
||||
|
||||
// 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_OVERLAY_POINTLIKE_LINEAR_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_LINEAR_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
|
||||
#include <boost/geometry/iterators/segment_iterator.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/disjoint.hpp>
|
||||
#include <boost/geometry/algorithms/envelope.hpp>
|
||||
#include <boost/geometry/algorithms/expand.hpp>
|
||||
#include <boost/geometry/algorithms/not_implemented.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/not.hpp>
|
||||
#include <boost/geometry/algorithms/detail/partition.hpp>
|
||||
#include <boost/geometry/algorithms/detail/disjoint/point_geometry.hpp>
|
||||
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
// action struct for pointlike-linear difference/intersection
|
||||
// it works the same as its pointlike-pointlike counterpart, hence the
|
||||
// derivation
|
||||
template <typename PointOut, overlay_type OverlayType>
|
||||
struct action_selector_pl_l
|
||||
: action_selector_pl_pl<PointOut, OverlayType>
|
||||
{};
|
||||
|
||||
// difference/intersection of point-linear
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename Linear,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType,
|
||||
typename Policy
|
||||
>
|
||||
struct point_linear_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(Point const& point,
|
||||
Linear const& linear,
|
||||
RobustPolicy const&,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
action_selector_pl_l
|
||||
<
|
||||
PointOut, OverlayType
|
||||
>::apply(point, Policy::apply(point, linear, strategy), oit);
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
// difference/intersection of multipoint-segment
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Segment,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType,
|
||||
typename Policy
|
||||
>
|
||||
struct multipoint_segment_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(MultiPoint const& multipoint,
|
||||
Segment const& segment,
|
||||
RobustPolicy const&,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
for (typename boost::range_iterator<MultiPoint const>::type
|
||||
it = boost::begin(multipoint);
|
||||
it != boost::end(multipoint);
|
||||
++it)
|
||||
{
|
||||
action_selector_pl_l
|
||||
<
|
||||
PointOut, OverlayType
|
||||
>::apply(*it, Policy::apply(*it, segment, strategy), oit);
|
||||
}
|
||||
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// difference/intersection of multipoint-linear
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Linear,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType,
|
||||
typename Policy
|
||||
>
|
||||
class multipoint_linear_point
|
||||
{
|
||||
private:
|
||||
// structs for partition -- start
|
||||
struct expand_box_point
|
||||
{
|
||||
template <typename Box, typename Point>
|
||||
static inline void apply(Box& total, Point const& point)
|
||||
{
|
||||
geometry::expand(total, point);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename EnvelopeStrategy>
|
||||
struct expand_box_segment
|
||||
{
|
||||
explicit expand_box_segment(EnvelopeStrategy const& strategy)
|
||||
: m_strategy(strategy)
|
||||
{}
|
||||
|
||||
template <typename Box, typename Segment>
|
||||
inline void apply(Box& total, Segment const& segment) const
|
||||
{
|
||||
geometry::expand(total,
|
||||
geometry::return_envelope<Box>(segment, m_strategy));
|
||||
}
|
||||
|
||||
EnvelopeStrategy const& m_strategy;
|
||||
};
|
||||
|
||||
struct overlaps_box_point
|
||||
{
|
||||
template <typename Box, typename Point>
|
||||
static inline bool apply(Box const& box, Point const& point)
|
||||
{
|
||||
return ! geometry::disjoint(point, box);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DisjointStrategy>
|
||||
struct overlaps_box_segment
|
||||
{
|
||||
explicit overlaps_box_segment(DisjointStrategy const& strategy)
|
||||
: m_strategy(strategy)
|
||||
{}
|
||||
|
||||
template <typename Box, typename Segment>
|
||||
inline bool apply(Box const& box, Segment const& segment) const
|
||||
{
|
||||
return ! geometry::disjoint(segment, box, m_strategy);
|
||||
}
|
||||
|
||||
DisjointStrategy const& m_strategy;
|
||||
};
|
||||
|
||||
template <typename OutputIterator, typename Strategy>
|
||||
class item_visitor_type
|
||||
{
|
||||
public:
|
||||
item_visitor_type(OutputIterator& oit, Strategy const& strategy)
|
||||
: m_oit(oit)
|
||||
, m_strategy(strategy)
|
||||
{}
|
||||
|
||||
template <typename Item1, typename Item2>
|
||||
inline bool apply(Item1 const& item1, Item2 const& item2)
|
||||
{
|
||||
action_selector_pl_l
|
||||
<
|
||||
PointOut, overlay_intersection
|
||||
>::apply(item1, Policy::apply(item1, item2, m_strategy), m_oit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
OutputIterator& m_oit;
|
||||
Strategy const& m_strategy;
|
||||
};
|
||||
// structs for partition -- end
|
||||
|
||||
class segment_range
|
||||
{
|
||||
public:
|
||||
typedef geometry::segment_iterator<Linear const> const_iterator;
|
||||
typedef const_iterator iterator;
|
||||
|
||||
segment_range(Linear const& linear)
|
||||
: m_linear(linear)
|
||||
{}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return geometry::segments_begin(m_linear);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return geometry::segments_end(m_linear);
|
||||
}
|
||||
|
||||
private:
|
||||
Linear const& m_linear;
|
||||
};
|
||||
|
||||
template <typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator get_common_points(MultiPoint const& multipoint,
|
||||
Linear const& linear,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
item_visitor_type<OutputIterator, Strategy> item_visitor(oit, strategy);
|
||||
|
||||
typedef typename Strategy::envelope_strategy_type envelope_strategy_type;
|
||||
typedef typename Strategy::disjoint_strategy_type disjoint_strategy_type;
|
||||
|
||||
// TODO: disjoint Segment/Box may be called in partition multiple times
|
||||
// possibly for non-cartesian segments which could be slow. We should consider
|
||||
// passing a range of bounding boxes of segments after calculating them once.
|
||||
// Alternatively instead of a range of segments a range of Segment/Envelope pairs
|
||||
// should be passed, where envelope would be lazily calculated when needed the first time
|
||||
geometry::partition
|
||||
<
|
||||
geometry::model::box
|
||||
<
|
||||
typename boost::range_value<MultiPoint>::type
|
||||
>
|
||||
>::apply(multipoint, segment_range(linear), item_visitor,
|
||||
expand_box_point(),
|
||||
overlaps_box_point(),
|
||||
expand_box_segment<envelope_strategy_type>(strategy.get_envelope_strategy()),
|
||||
overlaps_box_segment<disjoint_strategy_type>(strategy.get_disjoint_strategy()));
|
||||
|
||||
return oit;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(MultiPoint const& multipoint,
|
||||
Linear const& linear,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef std::vector
|
||||
<
|
||||
typename boost::range_value<MultiPoint>::type
|
||||
> point_vector_type;
|
||||
|
||||
point_vector_type common_points;
|
||||
|
||||
// compute the common points
|
||||
get_common_points(multipoint, linear,
|
||||
std::back_inserter(common_points),
|
||||
strategy);
|
||||
|
||||
return multipoint_multipoint_point
|
||||
<
|
||||
MultiPoint, point_vector_type, PointOut, OverlayType
|
||||
>::apply(multipoint, common_points, robust_policy, oit, strategy);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace detail_dispatch { namespace overlay
|
||||
{
|
||||
|
||||
// dispatch struct for pointlike-linear difference/intersection computation
|
||||
template
|
||||
<
|
||||
typename PointLike,
|
||||
typename Linear,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType,
|
||||
typename Tag1,
|
||||
typename Tag2
|
||||
>
|
||||
struct pointlike_linear_point
|
||||
: not_implemented<PointLike, Linear, PointOut>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename Linear,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_linear_point
|
||||
<
|
||||
Point, Linear, PointOut, OverlayType, point_tag, linear_tag
|
||||
> : detail::overlay::point_linear_point
|
||||
<
|
||||
Point, Linear, PointOut, OverlayType,
|
||||
detail::not_<detail::disjoint::reverse_covered_by>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename Segment,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_linear_point
|
||||
<
|
||||
Point, Segment, PointOut, OverlayType, point_tag, segment_tag
|
||||
> : detail::overlay::point_linear_point
|
||||
<
|
||||
Point, Segment, PointOut, OverlayType,
|
||||
detail::not_<detail::disjoint::reverse_covered_by>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Linear,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_linear_point
|
||||
<
|
||||
MultiPoint, Linear, PointOut, OverlayType, multi_point_tag, linear_tag
|
||||
> : detail::overlay::multipoint_linear_point
|
||||
<
|
||||
MultiPoint, Linear, PointOut, OverlayType,
|
||||
detail::not_<detail::disjoint::reverse_covered_by>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Segment,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_linear_point
|
||||
<
|
||||
MultiPoint, Segment, PointOut, OverlayType, multi_point_tag, segment_tag
|
||||
> : detail::overlay::multipoint_segment_point
|
||||
<
|
||||
MultiPoint, Segment, PointOut, OverlayType,
|
||||
detail::not_<detail::disjoint::reverse_covered_by>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
}} // namespace detail_dispatch::overlay
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_LINEAR_HPP
|
||||
@@ -0,0 +1,440 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2014-2018, Oracle and/or its affiliates.
|
||||
|
||||
// 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_OVERLAY_POINTLIKE_POINTLIKE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/core/point_type.hpp>
|
||||
#include <boost/geometry/core/tag.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/convert.hpp>
|
||||
#include <boost/geometry/algorithms/not_implemented.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
|
||||
#include <boost/geometry/policies/compare.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
// struct for copying points of the pointlike geometries to output
|
||||
template
|
||||
<
|
||||
typename PointOut,
|
||||
typename GeometryIn,
|
||||
typename TagIn = typename tag<GeometryIn>::type
|
||||
>
|
||||
struct copy_points
|
||||
: not_implemented<PointOut, GeometryIn>
|
||||
{};
|
||||
|
||||
template <typename PointOut, typename PointIn>
|
||||
struct copy_points<PointOut, PointIn, point_tag>
|
||||
{
|
||||
template <typename OutputIterator>
|
||||
static inline void apply(PointIn const& point_in,
|
||||
OutputIterator& oit)
|
||||
{
|
||||
PointOut point_out;
|
||||
geometry::convert(point_in, point_out);
|
||||
*oit++ = point_out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename PointOut, typename MultiPointIn>
|
||||
struct copy_points<PointOut, MultiPointIn, multi_point_tag>
|
||||
{
|
||||
template <typename OutputIterator>
|
||||
static inline void apply(MultiPointIn const& multi_point_in,
|
||||
OutputIterator& oit)
|
||||
{
|
||||
for (typename boost::range_iterator<MultiPointIn const>::type
|
||||
it = boost::begin(multi_point_in);
|
||||
it != boost::end(multi_point_in); ++it)
|
||||
{
|
||||
PointOut point_out;
|
||||
geometry::convert(*it, point_out);
|
||||
*oit++ = point_out;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// action struct for difference/intersection
|
||||
template <typename PointOut, overlay_type OverlayType>
|
||||
struct action_selector_pl_pl
|
||||
{};
|
||||
|
||||
template <typename PointOut>
|
||||
struct action_selector_pl_pl<PointOut, overlay_intersection>
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename OutputIterator
|
||||
>
|
||||
static inline void apply(Point const& point,
|
||||
bool is_common,
|
||||
OutputIterator& oit)
|
||||
{
|
||||
if ( is_common )
|
||||
{
|
||||
copy_points<PointOut, Point>::apply(point, oit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename PointOut>
|
||||
struct action_selector_pl_pl<PointOut, overlay_difference>
|
||||
{
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename OutputIterator
|
||||
>
|
||||
static inline void apply(Point const& point,
|
||||
bool is_common,
|
||||
OutputIterator& oit)
|
||||
{
|
||||
if ( !is_common )
|
||||
{
|
||||
copy_points<PointOut, Point>::apply(point, oit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
// difference/intersection of point-point
|
||||
template
|
||||
<
|
||||
typename Point1,
|
||||
typename Point2,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct point_point_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(Point1 const& point1,
|
||||
Point2 const& point2,
|
||||
RobustPolicy const& ,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
action_selector_pl_pl
|
||||
<
|
||||
PointOut, OverlayType
|
||||
>::apply(point1,
|
||||
detail::equals::equals_point_point(point1, point2, strategy),
|
||||
oit);
|
||||
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// difference of multipoint-point
|
||||
//
|
||||
// the apply method in the following struct is called only for
|
||||
// difference; for intersection the reversal will
|
||||
// always call the point-multipoint version
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Point,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct multipoint_point_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(MultiPoint const& multipoint,
|
||||
Point const& point,
|
||||
RobustPolicy const& ,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
BOOST_GEOMETRY_ASSERT( OverlayType == overlay_difference );
|
||||
|
||||
for (typename boost::range_iterator<MultiPoint const>::type
|
||||
it = boost::begin(multipoint);
|
||||
it != boost::end(multipoint); ++it)
|
||||
{
|
||||
action_selector_pl_pl
|
||||
<
|
||||
PointOut, OverlayType
|
||||
>::apply(*it,
|
||||
detail::equals::equals_point_point(*it, point, strategy),
|
||||
oit);
|
||||
}
|
||||
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// difference/intersection of point-multipoint
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename MultiPoint,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct point_multipoint_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(Point const& point,
|
||||
MultiPoint const& multipoint,
|
||||
RobustPolicy const& ,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef action_selector_pl_pl<PointOut, OverlayType> action;
|
||||
|
||||
for (typename boost::range_iterator<MultiPoint const>::type
|
||||
it = boost::begin(multipoint);
|
||||
it != boost::end(multipoint); ++it)
|
||||
{
|
||||
if ( detail::equals::equals_point_point(*it, point, strategy) )
|
||||
{
|
||||
action::apply(point, true, oit);
|
||||
return oit;
|
||||
}
|
||||
}
|
||||
|
||||
action::apply(point, false, oit);
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// difference/intersection of multipoint-multipoint
|
||||
template
|
||||
<
|
||||
typename MultiPoint1,
|
||||
typename MultiPoint2,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct multipoint_multipoint_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(MultiPoint1 const& multipoint1,
|
||||
MultiPoint2 const& multipoint2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
if ( OverlayType != overlay_difference
|
||||
&& boost::size(multipoint1) > boost::size(multipoint2) )
|
||||
{
|
||||
return multipoint_multipoint_point
|
||||
<
|
||||
MultiPoint2, MultiPoint1, PointOut, OverlayType
|
||||
>::apply(multipoint2, multipoint1, robust_policy, oit, strategy);
|
||||
}
|
||||
|
||||
typedef typename boost::range_value<MultiPoint2>::type point2_type;
|
||||
|
||||
std::vector<point2_type> points2(boost::begin(multipoint2),
|
||||
boost::end(multipoint2));
|
||||
|
||||
geometry::less<> const less = geometry::less<>();
|
||||
std::sort(points2.begin(), points2.end(), less);
|
||||
|
||||
for (typename boost::range_iterator<MultiPoint1 const>::type
|
||||
it1 = boost::begin(multipoint1);
|
||||
it1 != boost::end(multipoint1); ++it1)
|
||||
{
|
||||
bool found = std::binary_search(points2.begin(), points2.end(),
|
||||
*it1, less);
|
||||
|
||||
action_selector_pl_pl
|
||||
<
|
||||
PointOut, OverlayType
|
||||
>::apply(*it1, found, oit);
|
||||
}
|
||||
return oit;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace detail_dispatch { namespace overlay
|
||||
{
|
||||
|
||||
// dispatch struct for pointlike-pointlike difference/intersection
|
||||
// computation
|
||||
template
|
||||
<
|
||||
typename PointLike1,
|
||||
typename PointLike2,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType,
|
||||
typename Tag1,
|
||||
typename Tag2
|
||||
>
|
||||
struct pointlike_pointlike_point
|
||||
: not_implemented<PointLike1, PointLike2, PointOut>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Point1,
|
||||
typename Point2,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_pointlike_point
|
||||
<
|
||||
Point1, Point2, PointOut, OverlayType,
|
||||
point_tag, point_tag
|
||||
> : detail::overlay::point_point_point
|
||||
<
|
||||
Point1, Point2, PointOut, OverlayType
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename MultiPoint,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_pointlike_point
|
||||
<
|
||||
Point, MultiPoint, PointOut, OverlayType,
|
||||
point_tag, multi_point_tag
|
||||
> : detail::overlay::point_multipoint_point
|
||||
<
|
||||
Point, MultiPoint, PointOut, OverlayType
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiPoint,
|
||||
typename Point,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_pointlike_point
|
||||
<
|
||||
MultiPoint, Point, PointOut, OverlayType,
|
||||
multi_point_tag, point_tag
|
||||
> : detail::overlay::multipoint_point_point
|
||||
<
|
||||
MultiPoint, Point, PointOut, OverlayType
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename MultiPoint1,
|
||||
typename MultiPoint2,
|
||||
typename PointOut,
|
||||
overlay_type OverlayType
|
||||
>
|
||||
struct pointlike_pointlike_point
|
||||
<
|
||||
MultiPoint1, MultiPoint2, PointOut, OverlayType,
|
||||
multi_point_tag, multi_point_tag
|
||||
> : detail::overlay::multipoint_multipoint_point
|
||||
<
|
||||
MultiPoint1, MultiPoint2, PointOut, OverlayType
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
}} // namespace detail_dispatch::overlay
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
// generic pointlike-pointlike union implementation
|
||||
template
|
||||
<
|
||||
typename PointLike1,
|
||||
typename PointLike2,
|
||||
typename PointOut
|
||||
>
|
||||
struct union_pointlike_pointlike_point
|
||||
{
|
||||
template <typename RobustPolicy, typename OutputIterator, typename Strategy>
|
||||
static inline OutputIterator apply(PointLike1 const& pointlike1,
|
||||
PointLike2 const& pointlike2,
|
||||
RobustPolicy const& robust_policy,
|
||||
OutputIterator oit,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
copy_points<PointOut, PointLike1>::apply(pointlike1, oit);
|
||||
|
||||
return detail_dispatch::overlay::pointlike_pointlike_point
|
||||
<
|
||||
PointLike2, PointLike1, PointOut, overlay_difference,
|
||||
typename tag<PointLike2>::type,
|
||||
typename tag<PointLike1>::type
|
||||
>::apply(pointlike2, pointlike1, robust_policy, oit, strategy);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP
|
||||
@@ -0,0 +1,178 @@
|
||||
// Boost.Geometry
|
||||
|
||||
// Copyright (c) 2017 Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RANGE_IN_GEOMETRY_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RANGE_IN_GEOMETRY_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/covered_by.hpp>
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
#include <boost/geometry/iterators/point_iterator.hpp>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename geometry::tag<Geometry>::type
|
||||
>
|
||||
struct points_range
|
||||
{
|
||||
typedef geometry::point_iterator<Geometry const> iterator_type;
|
||||
|
||||
explicit points_range(Geometry const& geometry)
|
||||
: m_geometry(geometry)
|
||||
{}
|
||||
|
||||
iterator_type begin() const
|
||||
{
|
||||
return geometry::points_begin(m_geometry);
|
||||
}
|
||||
|
||||
iterator_type end() const
|
||||
{
|
||||
return geometry::points_end(m_geometry);
|
||||
}
|
||||
|
||||
Geometry const& m_geometry;
|
||||
};
|
||||
// Specialized because point_iterator doesn't support boxes
|
||||
template <typename Box>
|
||||
struct points_range<Box, box_tag>
|
||||
{
|
||||
typedef typename geometry::point_type<Box>::type point_type;
|
||||
typedef const point_type * iterator_type;
|
||||
|
||||
explicit points_range(Box const& box)
|
||||
{
|
||||
detail::assign_box_corners(box,
|
||||
m_corners[0], m_corners[1], m_corners[2], m_corners[3]);
|
||||
}
|
||||
|
||||
iterator_type begin() const
|
||||
{
|
||||
return m_corners;
|
||||
}
|
||||
|
||||
iterator_type end() const
|
||||
{
|
||||
return m_corners + 4;
|
||||
}
|
||||
|
||||
point_type m_corners[4];
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
typename Geometry,
|
||||
typename Tag = typename geometry::tag<Geometry>::type
|
||||
>
|
||||
struct point_in_geometry_helper
|
||||
{
|
||||
template <typename Point, typename Strategy>
|
||||
static inline int apply(Point const& point, Geometry const& geometry,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
return detail::within::point_in_geometry(point, geometry, strategy);
|
||||
}
|
||||
};
|
||||
// Specialized because point_in_geometry doesn't support Boxes
|
||||
template <typename Box>
|
||||
struct point_in_geometry_helper<Box, box_tag>
|
||||
{
|
||||
template <typename Point, typename Strategy>
|
||||
static inline int apply(Point const& point, Box const& box,
|
||||
Strategy const&)
|
||||
{
|
||||
return geometry::covered_by(point, box) ? 1 : -1;
|
||||
}
|
||||
};
|
||||
|
||||
// This function returns
|
||||
// when it finds a point of geometry1 inside or outside geometry2
|
||||
template <typename Geometry1, typename Geometry2, typename Strategy>
|
||||
static inline int range_in_geometry(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
Strategy const& strategy,
|
||||
bool skip_first = false)
|
||||
{
|
||||
int result = 0;
|
||||
points_range<Geometry1> points(geometry1);
|
||||
typedef typename points_range<Geometry1>::iterator_type iterator_type;
|
||||
iterator_type const end = points.end();
|
||||
iterator_type it = points.begin();
|
||||
if (it == end)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (skip_first)
|
||||
{
|
||||
++it;
|
||||
}
|
||||
|
||||
typename Strategy::template point_in_geometry_strategy
|
||||
<
|
||||
Geometry1, Geometry2
|
||||
>::type const in_strategy
|
||||
= strategy.template get_point_in_geometry_strategy<Geometry1, Geometry2>();
|
||||
|
||||
for ( ; it != end; ++it)
|
||||
{
|
||||
result = point_in_geometry_helper<Geometry2>::apply(*it, geometry2, in_strategy);
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// all points contained entirely by the boundary
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function returns if first_point1 is inside or outside geometry2 or
|
||||
// when it finds a point of geometry1 inside or outside geometry2
|
||||
template <typename Point1, typename Geometry1, typename Geometry2, typename Strategy>
|
||||
inline int range_in_geometry(Point1 const& first_point1,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
// check a point on border of geometry1 first
|
||||
int result = point_in_geometry_helper<Geometry2>::apply(first_point1, geometry2,
|
||||
strategy.template get_point_in_geometry_strategy<Point1, Geometry2>());
|
||||
if (result == 0)
|
||||
{
|
||||
// if a point is on boundary of geometry2
|
||||
// check points of geometry1 until point inside/outside is found
|
||||
// NOTE: skip first point because it should be already tested above
|
||||
result = range_in_geometry(geometry1, geometry2, strategy, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RANGE_IN_GEOMETRY_HPP
|
||||
@@ -0,0 +1,82 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/area.hpp>
|
||||
#include <boost/geometry/algorithms/within.hpp>
|
||||
#include <boost/geometry/algorithms/detail/point_on_border.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
template <typename Point, typename AreaType>
|
||||
struct ring_properties
|
||||
{
|
||||
typedef Point point_type;
|
||||
typedef AreaType area_type;
|
||||
|
||||
bool valid;
|
||||
|
||||
// Filled by "select_rings"
|
||||
Point point;
|
||||
area_type area;
|
||||
|
||||
// Filled by "update_ring_selection"
|
||||
bool reversed;
|
||||
|
||||
// Filled/used by "assign_rings"
|
||||
bool discarded;
|
||||
ring_identifier parent;
|
||||
area_type parent_area;
|
||||
std::vector<ring_identifier> children;
|
||||
|
||||
inline ring_properties()
|
||||
: valid(false)
|
||||
, area(area_type())
|
||||
, reversed(false)
|
||||
, discarded(false)
|
||||
, parent_area(-1)
|
||||
{}
|
||||
|
||||
template <typename RingOrBox, typename AreaStrategy>
|
||||
inline ring_properties(RingOrBox const& ring_or_box, AreaStrategy const& strategy)
|
||||
: reversed(false)
|
||||
, discarded(false)
|
||||
, parent_area(-1)
|
||||
{
|
||||
this->area = geometry::area(ring_or_box, strategy);
|
||||
valid = geometry::point_on_border(this->point, ring_or_box);
|
||||
}
|
||||
|
||||
inline area_type get_area() const
|
||||
{
|
||||
return reversed ? -area : area;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP
|
||||
@@ -0,0 +1,102 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP
|
||||
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_OVERLAY)
|
||||
# define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER)
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Internal struct to uniquely identify a segment
|
||||
// on a linestring,ring
|
||||
// or polygon (needs ring_index)
|
||||
// or multi-geometry (needs multi_index)
|
||||
struct segment_identifier
|
||||
{
|
||||
inline segment_identifier()
|
||||
: source_index(-1)
|
||||
, multi_index(-1)
|
||||
, ring_index(-1)
|
||||
, segment_index(-1)
|
||||
, piece_index(-1)
|
||||
{}
|
||||
|
||||
inline segment_identifier(signed_size_type src,
|
||||
signed_size_type mul,
|
||||
signed_size_type rin,
|
||||
signed_size_type seg)
|
||||
: source_index(src)
|
||||
, multi_index(mul)
|
||||
, ring_index(rin)
|
||||
, segment_index(seg)
|
||||
, piece_index(-1)
|
||||
{}
|
||||
|
||||
inline bool operator<(segment_identifier const& other) const
|
||||
{
|
||||
return source_index != other.source_index ? source_index < other.source_index
|
||||
: multi_index !=other.multi_index ? multi_index < other.multi_index
|
||||
: ring_index != other.ring_index ? ring_index < other.ring_index
|
||||
: piece_index != other.piece_index ? piece_index < other.piece_index
|
||||
: segment_index < other.segment_index
|
||||
;
|
||||
}
|
||||
|
||||
inline bool operator==(segment_identifier const& other) const
|
||||
{
|
||||
return source_index == other.source_index
|
||||
&& segment_index == other.segment_index
|
||||
&& ring_index == other.ring_index
|
||||
&& piece_index == other.piece_index
|
||||
&& multi_index == other.multi_index
|
||||
;
|
||||
}
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER)
|
||||
friend std::ostream& operator<<(std::ostream &os, segment_identifier const& seg_id)
|
||||
{
|
||||
os
|
||||
<< "s:" << seg_id.source_index
|
||||
<< ", v:" << seg_id.segment_index // v:vertex because s is used for source
|
||||
;
|
||||
if (seg_id.ring_index >= 0) os << ", r:" << seg_id.ring_index;
|
||||
if (seg_id.multi_index >= 0) os << ", m:" << seg_id.multi_index;
|
||||
if (seg_id.piece_index >= 0) os << ", p:" << seg_id.piece_index;
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
signed_size_type source_index;
|
||||
signed_size_type multi_index;
|
||||
signed_size_type ring_index;
|
||||
signed_size_type segment_index;
|
||||
|
||||
// For buffer - todo: move this to buffer-only
|
||||
signed_size_type piece_index;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP
|
||||
@@ -0,0 +1,372 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/covered_by.hpp>
|
||||
#include <boost/geometry/algorithms/detail/interior_iterator.hpp>
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
struct ring_turn_info
|
||||
{
|
||||
bool has_traversed_turn;
|
||||
bool has_blocked_turn;
|
||||
bool within_other;
|
||||
|
||||
ring_turn_info()
|
||||
: has_traversed_turn(false)
|
||||
, has_blocked_turn(false)
|
||||
, within_other(false)
|
||||
{}
|
||||
};
|
||||
|
||||
namespace dispatch
|
||||
{
|
||||
|
||||
template <typename Tag, typename Geometry>
|
||||
struct select_rings
|
||||
{};
|
||||
|
||||
template <typename Box>
|
||||
struct select_rings<box_tag, Box>
|
||||
{
|
||||
template <typename Geometry, typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Box const& box, Geometry const& ,
|
||||
ring_identifier const& id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy);
|
||||
}
|
||||
|
||||
template <typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Box const& box,
|
||||
ring_identifier const& id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ring>
|
||||
struct select_rings<ring_tag, Ring>
|
||||
{
|
||||
template <typename Geometry, typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Ring const& ring, Geometry const& ,
|
||||
ring_identifier const& id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
if (boost::size(ring) > 0)
|
||||
{
|
||||
ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Ring const& ring,
|
||||
ring_identifier const& id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
if (boost::size(ring) > 0)
|
||||
{
|
||||
ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Polygon>
|
||||
struct select_rings<polygon_tag, Polygon>
|
||||
{
|
||||
template <typename Geometry, typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Polygon const& polygon, Geometry const& geometry,
|
||||
ring_identifier id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::ring_type<Polygon>::type ring_type;
|
||||
typedef select_rings<ring_tag, ring_type> per_ring;
|
||||
|
||||
per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties, strategy);
|
||||
|
||||
typename interior_return_type<Polygon const>::type
|
||||
rings = interior_rings(polygon);
|
||||
for (typename detail::interior_iterator<Polygon const>::type
|
||||
it = boost::begin(rings); it != boost::end(rings); ++it)
|
||||
{
|
||||
id.ring_index++;
|
||||
per_ring::apply(*it, geometry, id, ring_properties, strategy);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Polygon const& polygon,
|
||||
ring_identifier id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::ring_type<Polygon>::type ring_type;
|
||||
typedef select_rings<ring_tag, ring_type> per_ring;
|
||||
|
||||
per_ring::apply(exterior_ring(polygon), id, ring_properties, strategy);
|
||||
|
||||
typename interior_return_type<Polygon const>::type
|
||||
rings = interior_rings(polygon);
|
||||
for (typename detail::interior_iterator<Polygon const>::type
|
||||
it = boost::begin(rings); it != boost::end(rings); ++it)
|
||||
{
|
||||
id.ring_index++;
|
||||
per_ring::apply(*it, id, ring_properties, strategy);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Multi>
|
||||
struct select_rings<multi_polygon_tag, Multi>
|
||||
{
|
||||
template <typename Geometry, typename RingPropertyMap, typename AreaStrategy>
|
||||
static inline void apply(Multi const& multi, Geometry const& geometry,
|
||||
ring_identifier id, RingPropertyMap& ring_properties,
|
||||
AreaStrategy const& strategy)
|
||||
{
|
||||
typedef typename boost::range_iterator
|
||||
<
|
||||
Multi const
|
||||
>::type iterator_type;
|
||||
|
||||
typedef select_rings<polygon_tag, typename boost::range_value<Multi>::type> per_polygon;
|
||||
|
||||
id.multi_index = 0;
|
||||
for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it)
|
||||
{
|
||||
id.ring_index = -1;
|
||||
per_polygon::apply(*it, geometry, id, ring_properties, strategy);
|
||||
id.multi_index++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dispatch
|
||||
|
||||
|
||||
template<overlay_type OverlayType>
|
||||
struct decide
|
||||
{
|
||||
// Default implementation (union, inflate, deflate, dissolve)
|
||||
static bool include(ring_identifier const& , ring_turn_info const& info)
|
||||
{
|
||||
return ! info.within_other;
|
||||
}
|
||||
|
||||
static bool reversed(ring_identifier const& , ring_turn_info const& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<>
|
||||
struct decide<overlay_difference>
|
||||
{
|
||||
static bool include(ring_identifier const& id, ring_turn_info const& info)
|
||||
{
|
||||
// Difference: A - B
|
||||
|
||||
// If this is A (source_index=0), then the ring is inside B
|
||||
// If this is B (source_index=1), then the ring is NOT inside A
|
||||
|
||||
// If this is A and the ring is within the other geometry,
|
||||
// then we should NOT include it.
|
||||
// If this is B then we SHOULD include it.
|
||||
|
||||
return id.source_index == 0
|
||||
? ! info.within_other
|
||||
: info.within_other;
|
||||
}
|
||||
|
||||
static bool reversed(ring_identifier const& id, ring_turn_info const& info)
|
||||
{
|
||||
// Difference: A - B
|
||||
// If this is B, and the ring is included, it should be reversed afterwards
|
||||
|
||||
return id.source_index == 1 && include(id, info);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct decide<overlay_intersection>
|
||||
{
|
||||
static bool include(ring_identifier const& , ring_turn_info const& info)
|
||||
{
|
||||
return info.within_other;
|
||||
}
|
||||
|
||||
static bool reversed(ring_identifier const& , ring_turn_info const& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename TurnInfoMap,
|
||||
typename RingPropertyMap,
|
||||
typename Strategy
|
||||
>
|
||||
inline void update_ring_selection(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
TurnInfoMap const& turn_info_map,
|
||||
RingPropertyMap const& all_ring_properties,
|
||||
RingPropertyMap& selected_ring_properties,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
selected_ring_properties.clear();
|
||||
|
||||
for (typename RingPropertyMap::const_iterator it = boost::begin(all_ring_properties);
|
||||
it != boost::end(all_ring_properties);
|
||||
++it)
|
||||
{
|
||||
ring_identifier const& id = it->first;
|
||||
|
||||
ring_turn_info info;
|
||||
|
||||
typename TurnInfoMap::const_iterator tcit = turn_info_map.find(id);
|
||||
if (tcit != turn_info_map.end())
|
||||
{
|
||||
info = tcit->second; // Copy by value
|
||||
}
|
||||
|
||||
if (info.has_traversed_turn || info.has_blocked_turn)
|
||||
{
|
||||
// This turn is traversed or blocked,
|
||||
// don't include the original ring
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the ring is within the other geometry, by taking
|
||||
// a point lying on the ring
|
||||
switch(id.source_index)
|
||||
{
|
||||
// within
|
||||
case 0 :
|
||||
info.within_other = range_in_geometry(it->second.point,
|
||||
geometry1, geometry2,
|
||||
strategy) > 0;
|
||||
break;
|
||||
case 1 :
|
||||
info.within_other = range_in_geometry(it->second.point,
|
||||
geometry2, geometry1,
|
||||
strategy) > 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (decide<OverlayType>::include(id, info))
|
||||
{
|
||||
typename RingPropertyMap::mapped_type properties = it->second; // Copy by value
|
||||
properties.reversed = decide<OverlayType>::reversed(id, info);
|
||||
selected_ring_properties[id] = properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief The function select_rings select rings based on the overlay-type (union,intersection)
|
||||
*/
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename RingTurnInfoMap,
|
||||
typename RingPropertyMap,
|
||||
typename Strategy
|
||||
>
|
||||
inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RingTurnInfoMap const& turn_info_per_ring,
|
||||
RingPropertyMap& selected_ring_properties,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::tag<Geometry1>::type tag1;
|
||||
typedef typename geometry::tag<Geometry2>::type tag2;
|
||||
typedef typename geometry::point_type<Geometry1>::type point1_type;
|
||||
typedef typename geometry::point_type<Geometry2>::type point2_type;
|
||||
|
||||
RingPropertyMap all_ring_properties;
|
||||
dispatch::select_rings<tag1, Geometry1>::apply(geometry1, geometry2,
|
||||
ring_identifier(0, -1, -1), all_ring_properties,
|
||||
strategy.template get_area_strategy<point1_type>());
|
||||
dispatch::select_rings<tag2, Geometry2>::apply(geometry2, geometry1,
|
||||
ring_identifier(1, -1, -1), all_ring_properties,
|
||||
strategy.template get_area_strategy<point2_type>());
|
||||
|
||||
update_ring_selection<OverlayType>(geometry1, geometry2, turn_info_per_ring,
|
||||
all_ring_properties, selected_ring_properties,
|
||||
strategy);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
overlay_type OverlayType,
|
||||
typename Geometry,
|
||||
typename RingTurnInfoMap,
|
||||
typename RingPropertyMap,
|
||||
typename Strategy
|
||||
>
|
||||
inline void select_rings(Geometry const& geometry,
|
||||
RingTurnInfoMap const& turn_info_per_ring,
|
||||
RingPropertyMap& selected_ring_properties,
|
||||
Strategy const& strategy)
|
||||
{
|
||||
typedef typename geometry::tag<Geometry>::type tag;
|
||||
typedef typename geometry::point_type<Geometry>::type point_type;
|
||||
|
||||
RingPropertyMap all_ring_properties;
|
||||
dispatch::select_rings<tag, Geometry>::apply(geometry,
|
||||
ring_identifier(0, -1, -1), all_ring_properties,
|
||||
strategy.template get_area_strategy<point_type>());
|
||||
|
||||
update_ring_selection<OverlayType>(geometry, geometry, turn_info_per_ring,
|
||||
all_ring_properties, selected_ring_properties,
|
||||
strategy);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP
|
||||
@@ -0,0 +1,378 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017, 2018.
|
||||
// Modifications copyright (c) 2017-2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/mpl/vector_c.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/core/coordinate_dimension.hpp>
|
||||
#include <boost/geometry/core/point_order.hpp>
|
||||
#include <boost/geometry/core/tags.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/concepts/check.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
|
||||
#include <boost/geometry/algorithms/detail/partition.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
|
||||
#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace self_get_turn_points
|
||||
{
|
||||
|
||||
struct no_interrupt_policy
|
||||
{
|
||||
static bool const enabled = false;
|
||||
static bool const has_intersections = false;
|
||||
|
||||
|
||||
template <typename Range>
|
||||
static inline bool apply(Range const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename Geometry,
|
||||
typename Turns,
|
||||
typename TurnPolicy,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename InterruptPolicy
|
||||
>
|
||||
struct self_section_visitor
|
||||
{
|
||||
Geometry const& m_geometry;
|
||||
IntersectionStrategy const& m_intersection_strategy;
|
||||
RobustPolicy const& m_rescale_policy;
|
||||
Turns& m_turns;
|
||||
InterruptPolicy& m_interrupt_policy;
|
||||
int m_source_index;
|
||||
bool m_skip_adjacent;
|
||||
|
||||
inline self_section_visitor(Geometry const& g,
|
||||
IntersectionStrategy const& is,
|
||||
RobustPolicy const& rp,
|
||||
Turns& turns,
|
||||
InterruptPolicy& ip,
|
||||
int source_index,
|
||||
bool skip_adjacent)
|
||||
: m_geometry(g)
|
||||
, m_intersection_strategy(is)
|
||||
, m_rescale_policy(rp)
|
||||
, m_turns(turns)
|
||||
, m_interrupt_policy(ip)
|
||||
, m_source_index(source_index)
|
||||
, m_skip_adjacent(skip_adjacent)
|
||||
{}
|
||||
|
||||
template <typename Section>
|
||||
inline bool apply(Section const& sec1, Section const& sec2)
|
||||
{
|
||||
if (! detail::disjoint::disjoint_box_box(sec1.bounding_box,
|
||||
sec2.bounding_box,
|
||||
m_intersection_strategy.get_disjoint_box_box_strategy())
|
||||
&& ! sec1.duplicate
|
||||
&& ! sec2.duplicate)
|
||||
{
|
||||
// false if interrupted
|
||||
return detail::get_turns::get_turns_in_sections
|
||||
<
|
||||
Geometry, Geometry,
|
||||
Reverse, Reverse,
|
||||
Section, Section,
|
||||
TurnPolicy
|
||||
>::apply(m_source_index, m_geometry, sec1,
|
||||
m_source_index, m_geometry, sec2,
|
||||
false, m_skip_adjacent,
|
||||
m_intersection_strategy,
|
||||
m_rescale_policy,
|
||||
m_turns, m_interrupt_policy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <bool Reverse, typename TurnPolicy>
|
||||
struct get_turns
|
||||
{
|
||||
template <typename Geometry, typename IntersectionStrategy, typename RobustPolicy, typename Turns, typename InterruptPolicy>
|
||||
static inline bool apply(
|
||||
Geometry const& geometry,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
Turns& turns,
|
||||
InterruptPolicy& interrupt_policy,
|
||||
int source_index, bool skip_adjacent)
|
||||
{
|
||||
typedef model::box
|
||||
<
|
||||
typename geometry::robust_point_type
|
||||
<
|
||||
typename geometry::point_type<Geometry>::type,
|
||||
RobustPolicy
|
||||
>::type
|
||||
> box_type;
|
||||
|
||||
// sectionalize in two dimensions to detect
|
||||
// all potential spikes correctly
|
||||
typedef geometry::sections<box_type, 2> sections_type;
|
||||
|
||||
typedef boost::mpl::vector_c<std::size_t, 0, 1> dimensions;
|
||||
|
||||
sections_type sec;
|
||||
geometry::sectionalize<Reverse, dimensions>(geometry, robust_policy, sec,
|
||||
intersection_strategy.get_envelope_strategy(),
|
||||
intersection_strategy.get_expand_strategy());
|
||||
|
||||
self_section_visitor
|
||||
<
|
||||
Reverse, Geometry,
|
||||
Turns, TurnPolicy, IntersectionStrategy, RobustPolicy, InterruptPolicy
|
||||
> visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index, skip_adjacent);
|
||||
|
||||
typedef detail::section::get_section_box
|
||||
<
|
||||
typename IntersectionStrategy::expand_box_strategy_type
|
||||
> get_section_box_type;
|
||||
typedef detail::section::overlaps_section_box
|
||||
<
|
||||
typename IntersectionStrategy::disjoint_box_box_strategy_type
|
||||
> overlaps_section_box_type;
|
||||
|
||||
// false if interrupted
|
||||
geometry::partition
|
||||
<
|
||||
box_type
|
||||
>::apply(sec, visitor,
|
||||
get_section_box_type(),
|
||||
overlaps_section_box_type());
|
||||
|
||||
return ! interrupt_policy.has_intersections;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::self_get_turn_points
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DISPATCH
|
||||
namespace dispatch
|
||||
{
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename GeometryTag,
|
||||
typename Geometry,
|
||||
typename TurnPolicy
|
||||
>
|
||||
struct self_get_turn_points
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename Ring,
|
||||
typename TurnPolicy
|
||||
>
|
||||
struct self_get_turn_points
|
||||
<
|
||||
Reverse, ring_tag, Ring,
|
||||
TurnPolicy
|
||||
>
|
||||
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename Box,
|
||||
typename TurnPolicy
|
||||
>
|
||||
struct self_get_turn_points
|
||||
<
|
||||
Reverse, box_tag, Box,
|
||||
TurnPolicy
|
||||
>
|
||||
{
|
||||
template <typename Strategy, typename RobustPolicy, typename Turns, typename InterruptPolicy>
|
||||
static inline bool apply(
|
||||
Box const& ,
|
||||
Strategy const& ,
|
||||
RobustPolicy const& ,
|
||||
Turns& ,
|
||||
InterruptPolicy& ,
|
||||
int /*source_index*/,
|
||||
bool /*skip_adjacent*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename Polygon,
|
||||
typename TurnPolicy
|
||||
>
|
||||
struct self_get_turn_points
|
||||
<
|
||||
Reverse, polygon_tag, Polygon,
|
||||
TurnPolicy
|
||||
>
|
||||
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
|
||||
{};
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename MultiPolygon,
|
||||
typename TurnPolicy
|
||||
>
|
||||
struct self_get_turn_points
|
||||
<
|
||||
Reverse, multi_polygon_tag, MultiPolygon,
|
||||
TurnPolicy
|
||||
>
|
||||
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
|
||||
{};
|
||||
|
||||
|
||||
} // namespace dispatch
|
||||
#endif // DOXYGEN_NO_DISPATCH
|
||||
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace self_get_turn_points
|
||||
{
|
||||
|
||||
// Version where Reverse can be specified manually. TODO:
|
||||
// can most probably be merged with self_get_turn_points::get_turn
|
||||
template
|
||||
<
|
||||
bool Reverse,
|
||||
typename AssignPolicy,
|
||||
typename Geometry,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename Turns,
|
||||
typename InterruptPolicy
|
||||
>
|
||||
inline void self_turns(Geometry const& geometry,
|
||||
IntersectionStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
Turns& turns,
|
||||
InterruptPolicy& interrupt_policy,
|
||||
int source_index = 0,
|
||||
bool skip_adjacent = false)
|
||||
{
|
||||
concepts::check<Geometry const>();
|
||||
|
||||
typedef detail::overlay::get_turn_info<detail::overlay::assign_null_policy> turn_policy;
|
||||
|
||||
dispatch::self_get_turn_points
|
||||
<
|
||||
Reverse,
|
||||
typename tag<Geometry>::type,
|
||||
Geometry,
|
||||
turn_policy
|
||||
>::apply(geometry, strategy, robust_policy, turns, interrupt_policy,
|
||||
source_index, skip_adjacent);
|
||||
}
|
||||
|
||||
}} // namespace detail::self_get_turn_points
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
/*!
|
||||
\brief Calculate self intersections of a geometry
|
||||
\ingroup overlay
|
||||
\tparam Geometry geometry type
|
||||
\tparam Turns type of intersection container
|
||||
(e.g. vector of "intersection/turn point"'s)
|
||||
\param geometry geometry
|
||||
\param strategy strategy to be used
|
||||
\param robust_policy policy to handle robustness issues
|
||||
\param turns container which will contain intersection points
|
||||
\param interrupt_policy policy determining if process is stopped
|
||||
when intersection is found
|
||||
\param source_index source index for generated turns
|
||||
\param skip_adjacent indicates if adjacent turns should be skipped
|
||||
*/
|
||||
template
|
||||
<
|
||||
typename AssignPolicy,
|
||||
typename Geometry,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename Turns,
|
||||
typename InterruptPolicy
|
||||
>
|
||||
inline void self_turns(Geometry const& geometry,
|
||||
IntersectionStrategy const& strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
Turns& turns,
|
||||
InterruptPolicy& interrupt_policy,
|
||||
int source_index = 0,
|
||||
bool skip_adjacent = false)
|
||||
{
|
||||
concepts::check<Geometry const>();
|
||||
|
||||
static bool const reverse = detail::overlay::do_reverse
|
||||
<
|
||||
geometry::point_order<Geometry>::value
|
||||
>::value;
|
||||
|
||||
detail::self_get_turn_points::self_turns
|
||||
<
|
||||
reverse,
|
||||
AssignPolicy
|
||||
>(geometry, strategy, robust_policy, turns, interrupt_policy,
|
||||
source_index, skip_adjacent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP
|
||||
@@ -0,0 +1,648 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
|
||||
|
||||
// This file was modified by Oracle on 2017.
|
||||
// Modifications copyright (c) 2017 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/geometry/algorithms/num_points.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
|
||||
#include <boost/geometry/algorithms/detail/direction_code.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay { namespace sort_by_side
|
||||
{
|
||||
|
||||
enum direction_type { dir_unknown = -1, dir_from = 0, dir_to = 1 };
|
||||
|
||||
typedef signed_size_type rank_type;
|
||||
|
||||
|
||||
// Point-wrapper, adding some properties
|
||||
template <typename Point>
|
||||
struct ranked_point
|
||||
{
|
||||
ranked_point()
|
||||
: rank(0)
|
||||
, turn_index(-1)
|
||||
, operation_index(-1)
|
||||
, direction(dir_unknown)
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, operation(operation_none)
|
||||
{}
|
||||
|
||||
template <typename Op>
|
||||
ranked_point(const Point& p, signed_size_type ti, int oi,
|
||||
direction_type d, Op op)
|
||||
: point(p)
|
||||
, rank(0)
|
||||
, zone(-1)
|
||||
, turn_index(ti)
|
||||
, operation_index(oi)
|
||||
, direction(d)
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, operation(op.operation)
|
||||
, seg_id(op.seg_id)
|
||||
{}
|
||||
|
||||
Point point;
|
||||
rank_type rank;
|
||||
signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones
|
||||
signed_size_type turn_index;
|
||||
int operation_index; // 0,1
|
||||
direction_type direction;
|
||||
std::size_t count_left;
|
||||
std::size_t count_right;
|
||||
operation_type operation;
|
||||
segment_identifier seg_id;
|
||||
};
|
||||
|
||||
struct less_by_turn_index
|
||||
{
|
||||
template <typename T>
|
||||
inline bool operator()(const T& first, const T& second) const
|
||||
{
|
||||
return first.turn_index == second.turn_index
|
||||
? first.index < second.index
|
||||
: first.turn_index < second.turn_index
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
struct less_by_index
|
||||
{
|
||||
template <typename T>
|
||||
inline bool operator()(const T& first, const T& second) const
|
||||
{
|
||||
// Length might be considered too
|
||||
// First order by from/to
|
||||
if (first.direction != second.direction)
|
||||
{
|
||||
return first.direction < second.direction;
|
||||
}
|
||||
// Then by turn index
|
||||
if (first.turn_index != second.turn_index)
|
||||
{
|
||||
return first.turn_index < second.turn_index;
|
||||
}
|
||||
// This can also be the same (for example in buffer), but seg_id is
|
||||
// never the same
|
||||
return first.seg_id < second.seg_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct less_false
|
||||
{
|
||||
template <typename T>
|
||||
inline bool operator()(const T&, const T& ) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Point, typename SideStrategy, typename LessOnSame, typename Compare>
|
||||
struct less_by_side
|
||||
{
|
||||
less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy)
|
||||
: m_p1(p1)
|
||||
, m_p2(p2)
|
||||
, m_strategy(strategy)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator()(const T& first, const T& second) const
|
||||
{
|
||||
LessOnSame on_same;
|
||||
Compare compare;
|
||||
|
||||
int const side_first = m_strategy.apply(m_p1, m_p2, first.point);
|
||||
int const side_second = m_strategy.apply(m_p1, m_p2, second.point);
|
||||
|
||||
if (side_first == 0 && side_second == 0)
|
||||
{
|
||||
// Both collinear. They might point into different directions: <------*------>
|
||||
// If so, order the one going backwards as the very first.
|
||||
|
||||
int const first_code = direction_code(m_p1, m_p2, first.point);
|
||||
int const second_code = direction_code(m_p1, m_p2, second.point);
|
||||
|
||||
// Order by code, backwards first, then forward.
|
||||
return first_code != second_code
|
||||
? first_code < second_code
|
||||
: on_same(first, second)
|
||||
;
|
||||
}
|
||||
else if (side_first == 0
|
||||
&& direction_code(m_p1, m_p2, first.point) == -1)
|
||||
{
|
||||
// First collinear and going backwards.
|
||||
// Order as the very first, so return always true
|
||||
return true;
|
||||
}
|
||||
else if (side_second == 0
|
||||
&& direction_code(m_p1, m_p2, second.point) == -1)
|
||||
{
|
||||
// Second is collinear and going backwards
|
||||
// Order as very last, so return always false
|
||||
return false;
|
||||
}
|
||||
|
||||
// They are not both collinear
|
||||
|
||||
if (side_first != side_second)
|
||||
{
|
||||
return compare(side_first, side_second);
|
||||
}
|
||||
|
||||
// They are both left, both right, and/or both collinear (with each other and/or with p1,p2)
|
||||
// Check mutual side
|
||||
int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point);
|
||||
|
||||
if (side_second_wrt_first == 0)
|
||||
{
|
||||
return on_same(first, second);
|
||||
}
|
||||
|
||||
int const side_first_wrt_second = -side_second_wrt_first;
|
||||
|
||||
// Both are on same side, and not collinear
|
||||
// Union: return true if second is right w.r.t. first, so -1,
|
||||
// so other is 1. union has greater as compare functor
|
||||
// Intersection: v.v.
|
||||
return compare(side_first_wrt_second, side_second_wrt_first);
|
||||
}
|
||||
|
||||
private :
|
||||
Point m_p1, m_p2;
|
||||
SideStrategy const& m_strategy;
|
||||
};
|
||||
|
||||
// Sorts vectors in counter clockwise order (by default)
|
||||
template
|
||||
<
|
||||
bool Reverse1,
|
||||
bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Point,
|
||||
typename SideStrategy,
|
||||
typename Compare
|
||||
>
|
||||
struct side_sorter
|
||||
{
|
||||
typedef ranked_point<Point> rp;
|
||||
|
||||
private :
|
||||
struct include_union
|
||||
{
|
||||
inline bool operator()(rp const& ranked_point) const
|
||||
{
|
||||
// New candidate if there are no polygons on left side,
|
||||
// but there are on right side
|
||||
return ranked_point.count_left == 0
|
||||
&& ranked_point.count_right > 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct include_intersection
|
||||
{
|
||||
inline bool operator()(rp const& ranked_point) const
|
||||
{
|
||||
// New candidate if there are two polygons on right side,
|
||||
// and less on the left side
|
||||
return ranked_point.count_left < 2
|
||||
&& ranked_point.count_right >= 2;
|
||||
}
|
||||
};
|
||||
|
||||
public :
|
||||
side_sorter(SideStrategy const& strategy)
|
||||
: m_origin_count(0)
|
||||
, m_origin_segment_distance(0)
|
||||
, m_strategy(strategy)
|
||||
{}
|
||||
|
||||
template <typename Operation, typename Geometry1, typename Geometry2>
|
||||
Point add(Operation const& op, signed_size_type turn_index, int op_index,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
bool is_origin)
|
||||
{
|
||||
Point point1, point2, point3;
|
||||
geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
|
||||
op.seg_id, point1, point2, point3);
|
||||
Point const& point_to = op.fraction.is_one() ? point3 : point2;
|
||||
|
||||
m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op));
|
||||
m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op));
|
||||
if (is_origin)
|
||||
{
|
||||
m_origin = point1;
|
||||
m_origin_count++;
|
||||
}
|
||||
return point1;
|
||||
}
|
||||
|
||||
template <typename Operation, typename Geometry1, typename Geometry2>
|
||||
void add(Operation const& op, signed_size_type turn_index, int op_index,
|
||||
segment_identifier const& departure_seg_id,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
bool check_origin)
|
||||
{
|
||||
Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false);
|
||||
|
||||
if (check_origin)
|
||||
{
|
||||
bool const is_origin
|
||||
= op.seg_id.source_index == departure_seg_id.source_index
|
||||
&& op.seg_id.ring_index == departure_seg_id.ring_index
|
||||
&& op.seg_id.multi_index == departure_seg_id.multi_index;
|
||||
|
||||
if (is_origin)
|
||||
{
|
||||
signed_size_type const segment_distance = calculate_segment_distance(op, departure_seg_id, geometry1, geometry2);
|
||||
if (m_origin_count == 0 ||
|
||||
segment_distance < m_origin_segment_distance)
|
||||
{
|
||||
m_origin = point1;
|
||||
m_origin_segment_distance = segment_distance;
|
||||
}
|
||||
m_origin_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Operation, typename Geometry1, typename Geometry2>
|
||||
static signed_size_type calculate_segment_distance(Operation const& op,
|
||||
segment_identifier const& departure_seg_id,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2)
|
||||
{
|
||||
if (op.seg_id.segment_index >= departure_seg_id.segment_index)
|
||||
{
|
||||
return op.seg_id.segment_index - departure_seg_id.segment_index;
|
||||
}
|
||||
// Take wrap into account
|
||||
// Suppose ring_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2, then distance=10-9+2
|
||||
// Generic function (is this used somewhere else too?)
|
||||
ring_identifier const rid(op.seg_id.source_index, op.seg_id.multi_index, op.seg_id.ring_index);
|
||||
signed_size_type const segment_count
|
||||
(op.seg_id.source_index == 0
|
||||
? geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry1>::type>::apply(rid, geometry1))
|
||||
: geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry2>::type>::apply(rid, geometry2)));
|
||||
return ((segment_count - 1) - departure_seg_id.segment_index) + op.seg_id.segment_index;
|
||||
}
|
||||
|
||||
void apply(Point const& turn_point)
|
||||
{
|
||||
// We need three compare functors:
|
||||
// 1) to order clockwise (union) or counter clockwise (intersection)
|
||||
// 2) to order by side, resulting in unique ranks for all points
|
||||
// 3) to order by side, resulting in non-unique ranks
|
||||
// to give colinear points
|
||||
|
||||
// Sort by side and assign rank
|
||||
less_by_side<Point, SideStrategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy);
|
||||
less_by_side<Point, SideStrategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy);
|
||||
|
||||
std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique);
|
||||
|
||||
std::size_t colinear_rank = 0;
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
if (i > 0
|
||||
&& less_non_unique(m_ranked_points[i - 1], m_ranked_points[i]))
|
||||
{
|
||||
// It is not collinear
|
||||
colinear_rank++;
|
||||
}
|
||||
|
||||
m_ranked_points[i].rank = colinear_rank;
|
||||
}
|
||||
}
|
||||
|
||||
template <signed_size_type segment_identifier::*Member, typename Map>
|
||||
void find_open_generic(Map& handled, bool check)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
const rp& ranked = m_ranked_points[i];
|
||||
if (ranked.direction != dir_from)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
signed_size_type const& index = ranked.seg_id.*Member;
|
||||
if (check && (index < 0 || index > 1))
|
||||
{
|
||||
// Should not occur
|
||||
continue;
|
||||
}
|
||||
if (! handled[index])
|
||||
{
|
||||
find_polygons_for_source<Member>(index, i);
|
||||
handled[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void find_open()
|
||||
{
|
||||
if (OverlayType == overlay_buffer)
|
||||
{
|
||||
// For buffers, use piece index
|
||||
std::map<signed_size_type, bool> handled;
|
||||
find_open_generic
|
||||
<
|
||||
&segment_identifier::piece_index
|
||||
>(handled, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For other operations, by source (there should only source 0,1)
|
||||
bool handled[2] = {false, false};
|
||||
find_open_generic
|
||||
<
|
||||
&segment_identifier::source_index
|
||||
>(handled, true);
|
||||
}
|
||||
}
|
||||
|
||||
void reverse()
|
||||
{
|
||||
if (m_ranked_points.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t const last = 1 + m_ranked_points.back().rank;
|
||||
|
||||
// Move iterator after rank==0
|
||||
bool has_first = false;
|
||||
typename container_type::iterator it = m_ranked_points.begin() + 1;
|
||||
for (; it != m_ranked_points.end() && it->rank == 0; ++it)
|
||||
{
|
||||
has_first = true;
|
||||
}
|
||||
|
||||
if (has_first)
|
||||
{
|
||||
// Reverse first part (having rank == 0), if any,
|
||||
// but skip the very first row
|
||||
std::reverse(m_ranked_points.begin() + 1, it);
|
||||
for (typename container_type::iterator fit = m_ranked_points.begin();
|
||||
fit != it; ++fit)
|
||||
{
|
||||
BOOST_ASSERT(fit->rank == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the rest (main rank > 0)
|
||||
std::reverse(it, m_ranked_points.end());
|
||||
for (; it != m_ranked_points.end(); ++it)
|
||||
{
|
||||
BOOST_ASSERT(it->rank > 0);
|
||||
it->rank = last - it->rank;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_origin() const
|
||||
{
|
||||
return m_origin_count > 0;
|
||||
}
|
||||
|
||||
//private :
|
||||
|
||||
typedef std::vector<rp> container_type;
|
||||
container_type m_ranked_points;
|
||||
Point m_origin;
|
||||
std::size_t m_origin_count;
|
||||
signed_size_type m_origin_segment_distance;
|
||||
SideStrategy m_strategy;
|
||||
|
||||
private :
|
||||
|
||||
//! Check how many open spaces there are
|
||||
template <typename Include>
|
||||
inline std::size_t open_count(Include const& include_functor) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
rank_type last_rank = 0;
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
rp const& ranked_point = m_ranked_points[i];
|
||||
|
||||
if (ranked_point.rank > last_rank
|
||||
&& ranked_point.direction == sort_by_side::dir_to
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
result++;
|
||||
last_rank = ranked_point.rank;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t move(std::size_t index) const
|
||||
{
|
||||
std::size_t const result = index + 1;
|
||||
return result >= m_ranked_points.size() ? 0 : result;
|
||||
}
|
||||
|
||||
//! member is pointer to member (source_index or multi_index)
|
||||
template <signed_size_type segment_identifier::*Member>
|
||||
std::size_t move(signed_size_type member_index, std::size_t index) const
|
||||
{
|
||||
std::size_t result = move(index);
|
||||
while (m_ranked_points[result].seg_id.*Member != member_index)
|
||||
{
|
||||
result = move(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void assign_ranks(rank_type min_rank, rank_type max_rank, int side_index)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
rp& ranked = m_ranked_points[i];
|
||||
// Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6
|
||||
// if min=5,max=2: assign from 5,6,7,1,2
|
||||
bool const in_range
|
||||
= max_rank >= min_rank
|
||||
? ranked.rank >= min_rank && ranked.rank <= max_rank
|
||||
: ranked.rank >= min_rank || ranked.rank <= max_rank
|
||||
;
|
||||
|
||||
if (in_range)
|
||||
{
|
||||
if (side_index == 1)
|
||||
{
|
||||
ranked.count_left++;
|
||||
}
|
||||
else if (side_index == 2)
|
||||
{
|
||||
ranked.count_right++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <signed_size_type segment_identifier::*Member>
|
||||
void find_polygons_for_source(signed_size_type the_index,
|
||||
std::size_t start_index)
|
||||
{
|
||||
bool in_polygon = true; // Because start_index is "from", arrives at the turn
|
||||
rp const& start_rp = m_ranked_points[start_index];
|
||||
rank_type last_from_rank = start_rp.rank;
|
||||
rank_type previous_rank = start_rp.rank;
|
||||
|
||||
for (std::size_t index = move<Member>(the_index, start_index);
|
||||
;
|
||||
index = move<Member>(the_index, index))
|
||||
{
|
||||
rp& ranked = m_ranked_points[index];
|
||||
|
||||
if (ranked.rank != previous_rank && ! in_polygon)
|
||||
{
|
||||
assign_ranks(last_from_rank, previous_rank - 1, 1);
|
||||
assign_ranks(last_from_rank + 1, previous_rank, 2);
|
||||
}
|
||||
|
||||
if (index == start_index)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ranked.direction == dir_from)
|
||||
{
|
||||
last_from_rank = ranked.rank;
|
||||
in_polygon = true;
|
||||
}
|
||||
else if (ranked.direction == dir_to)
|
||||
{
|
||||
in_polygon = false;
|
||||
}
|
||||
|
||||
previous_rank = ranked.rank;
|
||||
}
|
||||
}
|
||||
|
||||
//! Find closed zones and assign it
|
||||
template <typename Include>
|
||||
std::size_t assign_zones(Include const& include_functor)
|
||||
{
|
||||
// Find a starting point (the first rank after an outgoing rank
|
||||
// with no polygons on the left side)
|
||||
rank_type start_rank = m_ranked_points.size() + 1;
|
||||
std::size_t start_index = 0;
|
||||
rank_type max_rank = 0;
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
rp const& ranked_point = m_ranked_points[i];
|
||||
if (ranked_point.rank > max_rank)
|
||||
{
|
||||
max_rank = ranked_point.rank;
|
||||
}
|
||||
if (ranked_point.direction == sort_by_side::dir_to
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
start_rank = ranked_point.rank + 1;
|
||||
}
|
||||
if (ranked_point.rank == start_rank && start_index == 0)
|
||||
{
|
||||
start_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the zones
|
||||
rank_type const undefined_rank = max_rank + 1;
|
||||
std::size_t zone_id = 0;
|
||||
rank_type last_rank = 0;
|
||||
rank_type rank_at_next_zone = undefined_rank;
|
||||
std::size_t index = start_index;
|
||||
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
|
||||
{
|
||||
rp& ranked_point = m_ranked_points[index];
|
||||
|
||||
// Implement cyclic behavior
|
||||
index++;
|
||||
if (index == m_ranked_points.size())
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if (ranked_point.rank != last_rank)
|
||||
{
|
||||
if (ranked_point.rank == rank_at_next_zone)
|
||||
{
|
||||
zone_id++;
|
||||
rank_at_next_zone = undefined_rank;
|
||||
}
|
||||
|
||||
if (ranked_point.direction == sort_by_side::dir_to
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
rank_at_next_zone = ranked_point.rank + 1;
|
||||
if (rank_at_next_zone > max_rank)
|
||||
{
|
||||
rank_at_next_zone = 0;
|
||||
}
|
||||
}
|
||||
|
||||
last_rank = ranked_point.rank;
|
||||
}
|
||||
|
||||
ranked_point.zone = zone_id;
|
||||
}
|
||||
return zone_id;
|
||||
}
|
||||
|
||||
public :
|
||||
inline std::size_t open_count(operation_type for_operation) const
|
||||
{
|
||||
return for_operation == operation_union
|
||||
? open_count(include_union())
|
||||
: open_count(include_intersection())
|
||||
;
|
||||
}
|
||||
|
||||
inline std::size_t assign_zones(operation_type for_operation)
|
||||
{
|
||||
return for_operation == operation_union
|
||||
? assign_zones(include_union())
|
||||
: assign_zones(include_intersection())
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}}} // namespace detail::overlay::sort_by_side
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
|
||||
@@ -0,0 +1,79 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2018.
|
||||
// Modifications copyright (c) 2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
static inline std::string dir(int d)
|
||||
{
|
||||
return d == 0 ? "-" : (d == 1 ? "L" : d == -1 ? "R" : "#");
|
||||
}
|
||||
static inline std::string how_str(int h)
|
||||
{
|
||||
return h == 0 ? "-" : (h == 1 ? "A" : "D");
|
||||
}
|
||||
|
||||
template <typename P, typename SR, typename O, typename C>
|
||||
std::ostream& operator<<(std::ostream &os, turn_info<P, SR, O, C> const& info)
|
||||
{
|
||||
os << "\t"
|
||||
<< " src " << info.seg_id.source_index
|
||||
<< " seg " << info.seg_id.segment_index
|
||||
<< " (// " << info.other_id.source_index
|
||||
<< "." << info.other_id.segment_index << ")"
|
||||
<< " how " << info.how
|
||||
<< "[" << how_str(info.arrival)
|
||||
<< " " << dir(info.direction)
|
||||
<< (info.opposite ? " o" : "")
|
||||
<< "]"
|
||||
<< " sd "
|
||||
<< dir(info.sides.template get<0,0>())
|
||||
<< dir(info.sides.template get<0,1>())
|
||||
<< dir(info.sides.template get<1,0>())
|
||||
<< dir(info.sides.template get<1,1>())
|
||||
<< " nxt seg " << info.travels_to_vertex_index
|
||||
<< " , ip " << info.travels_to_ip_index
|
||||
<< " , or " << info.next_ip_index
|
||||
<< " frac " << info.fraction
|
||||
<< info.visit_state;
|
||||
if (info.flagged)
|
||||
{
|
||||
os << " FLAGGED";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP
|
||||
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template <typename Point, typename SegmentRatio>
|
||||
struct traversal_turn_operation : public turn_operation<Point, SegmentRatio>
|
||||
{
|
||||
enrichment_info<Point> enriched;
|
||||
visit_info visited;
|
||||
};
|
||||
|
||||
template <typename Point, typename SegmentRatio>
|
||||
struct traversal_turn_info
|
||||
: public turn_info
|
||||
<
|
||||
Point,
|
||||
SegmentRatio,
|
||||
traversal_turn_operation<Point, SegmentRatio>
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP
|
||||
@@ -0,0 +1,426 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2017, 2018.
|
||||
// Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/traversal.hpp>
|
||||
#include <boost/geometry/algorithms/num_points.hpp>
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/core/closure.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse1,
|
||||
bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename Turns,
|
||||
typename TurnInfoMap,
|
||||
typename Clusters,
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename Visitor,
|
||||
typename Backtrack
|
||||
>
|
||||
struct traversal_ring_creator
|
||||
{
|
||||
typedef traversal
|
||||
<
|
||||
Reverse1, Reverse2, OverlayType,
|
||||
Geometry1, Geometry2, Turns, Clusters,
|
||||
RobustPolicy, typename IntersectionStrategy::side_strategy_type,
|
||||
Visitor
|
||||
> traversal_type;
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
|
||||
static const operation_type target_operation
|
||||
= operation_from_overlay<OverlayType>::value;
|
||||
|
||||
inline traversal_ring_creator(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
Turns& turns, TurnInfoMap& turn_info_map,
|
||||
Clusters const& clusters,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy, Visitor& visitor)
|
||||
: m_trav(geometry1, geometry2, turns, clusters,
|
||||
robust_policy, intersection_strategy.get_side_strategy(),
|
||||
visitor)
|
||||
, m_geometry1(geometry1)
|
||||
, m_geometry2(geometry2)
|
||||
, m_turns(turns)
|
||||
, m_turn_info_map(turn_info_map)
|
||||
, m_clusters(clusters)
|
||||
, m_intersection_strategy(intersection_strategy)
|
||||
, m_robust_policy(robust_policy)
|
||||
, m_visitor(visitor)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Ring>
|
||||
inline traverse_error_type travel_to_next_turn(signed_size_type start_turn_index,
|
||||
int start_op_index,
|
||||
signed_size_type& turn_index,
|
||||
int& op_index,
|
||||
Ring& current_ring,
|
||||
bool is_start)
|
||||
{
|
||||
int const previous_op_index = op_index;
|
||||
signed_size_type const previous_turn_index = turn_index;
|
||||
turn_type& previous_turn = m_turns[turn_index];
|
||||
turn_operation_type& previous_op = previous_turn.operations[op_index];
|
||||
segment_identifier previous_seg_id;
|
||||
|
||||
signed_size_type to_vertex_index = -1;
|
||||
if (! m_trav.select_turn_from_enriched(turn_index, previous_seg_id,
|
||||
to_vertex_index, start_turn_index, start_op_index,
|
||||
previous_turn, previous_op, is_start))
|
||||
{
|
||||
return is_start
|
||||
? traverse_error_no_next_ip_at_start
|
||||
: traverse_error_no_next_ip;
|
||||
}
|
||||
if (to_vertex_index >= 0)
|
||||
{
|
||||
if (previous_op.seg_id.source_index == 0)
|
||||
{
|
||||
geometry::copy_segments<Reverse1>(m_geometry1,
|
||||
previous_op.seg_id, to_vertex_index,
|
||||
m_intersection_strategy.get_side_strategy(),
|
||||
m_robust_policy, current_ring);
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry::copy_segments<Reverse2>(m_geometry2,
|
||||
previous_op.seg_id, to_vertex_index,
|
||||
m_intersection_strategy.get_side_strategy(),
|
||||
m_robust_policy, current_ring);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_turns[turn_index].discarded)
|
||||
{
|
||||
return is_start
|
||||
? traverse_error_dead_end_at_start
|
||||
: traverse_error_dead_end;
|
||||
}
|
||||
|
||||
if (is_start)
|
||||
{
|
||||
// Register the start
|
||||
previous_op.visited.set_started();
|
||||
m_visitor.visit_traverse(m_turns, previous_turn, previous_op, "Start");
|
||||
}
|
||||
|
||||
if (! m_trav.select_turn(start_turn_index, start_op_index,
|
||||
turn_index, op_index,
|
||||
previous_op_index, previous_turn_index, previous_seg_id,
|
||||
is_start))
|
||||
{
|
||||
return is_start
|
||||
? traverse_error_no_next_ip_at_start
|
||||
: traverse_error_no_next_ip;
|
||||
}
|
||||
|
||||
{
|
||||
// Check operation (TODO: this might be redundant or should be catched before)
|
||||
const turn_type& current_turn = m_turns[turn_index];
|
||||
const turn_operation_type& op = current_turn.operations[op_index];
|
||||
if (op.visited.finalized()
|
||||
|| m_trav.is_visited(current_turn, op, turn_index, op_index))
|
||||
{
|
||||
return traverse_error_visit_again;
|
||||
}
|
||||
}
|
||||
|
||||
// Update registration and append point
|
||||
turn_type& current_turn = m_turns[turn_index];
|
||||
turn_operation_type& op = current_turn.operations[op_index];
|
||||
detail::overlay::append_no_collinear(current_ring, current_turn.point,
|
||||
m_intersection_strategy.get_side_strategy(),
|
||||
m_robust_policy);
|
||||
|
||||
// Register the visit
|
||||
m_trav.set_visited(current_turn, op);
|
||||
m_visitor.visit_traverse(m_turns, current_turn, op, "Visit");
|
||||
|
||||
return traverse_error_none;
|
||||
}
|
||||
|
||||
template <typename Ring>
|
||||
inline traverse_error_type traverse(Ring& ring,
|
||||
signed_size_type start_turn_index, int start_op_index)
|
||||
{
|
||||
turn_type const& start_turn = m_turns[start_turn_index];
|
||||
turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index];
|
||||
|
||||
detail::overlay::append_no_collinear(ring, start_turn.point,
|
||||
m_intersection_strategy.get_side_strategy(),
|
||||
m_robust_policy);
|
||||
|
||||
signed_size_type current_turn_index = start_turn_index;
|
||||
int current_op_index = start_op_index;
|
||||
|
||||
traverse_error_type error = travel_to_next_turn(start_turn_index,
|
||||
start_op_index,
|
||||
current_turn_index, current_op_index,
|
||||
ring, true);
|
||||
|
||||
if (error != traverse_error_none)
|
||||
{
|
||||
// This is not necessarily a problem, it happens for clustered turns
|
||||
// which are "build in" or otherwise point inwards
|
||||
return error;
|
||||
}
|
||||
|
||||
if (current_turn_index == start_turn_index)
|
||||
{
|
||||
start_op.visited.set_finished();
|
||||
m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish");
|
||||
return traverse_error_none;
|
||||
}
|
||||
|
||||
if (start_turn.is_clustered())
|
||||
{
|
||||
turn_type& turn = m_turns[current_turn_index];
|
||||
turn_operation_type& op = turn.operations[current_op_index];
|
||||
if (turn.cluster_id == start_turn.cluster_id
|
||||
&& op.enriched.get_next_turn_index() == start_turn_index)
|
||||
{
|
||||
op.visited.set_finished();
|
||||
m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish (cluster)");
|
||||
return traverse_error_none;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t const max_iterations = 2 + 2 * m_turns.size();
|
||||
for (std::size_t i = 0; i <= max_iterations; i++)
|
||||
{
|
||||
// We assume clockwise polygons only, non self-intersecting, closed.
|
||||
// However, the input might be different, and checking validity
|
||||
// is up to the library user.
|
||||
|
||||
// Therefore we make here some sanity checks. If the input
|
||||
// violates the assumptions, the output polygon will not be correct
|
||||
// but the routine will stop and output the current polygon, and
|
||||
// will continue with the next one.
|
||||
|
||||
// Below three reasons to stop.
|
||||
error = travel_to_next_turn(start_turn_index, start_op_index,
|
||||
current_turn_index, current_op_index,
|
||||
ring, false);
|
||||
|
||||
if (error != traverse_error_none)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (current_turn_index == start_turn_index
|
||||
&& current_op_index == start_op_index)
|
||||
{
|
||||
start_op.visited.set_finished();
|
||||
m_visitor.visit_traverse(m_turns, start_turn, start_op, "Finish");
|
||||
return traverse_error_none;
|
||||
}
|
||||
}
|
||||
|
||||
return traverse_error_endless_loop;
|
||||
}
|
||||
|
||||
template <typename Rings>
|
||||
void traverse_with_operation(turn_type const& start_turn,
|
||||
std::size_t turn_index, int op_index,
|
||||
Rings& rings, std::size_t& finalized_ring_size,
|
||||
typename Backtrack::state_type& state)
|
||||
{
|
||||
typedef typename boost::range_value<Rings>::type ring_type;
|
||||
|
||||
turn_operation_type const& start_op = start_turn.operations[op_index];
|
||||
|
||||
if (! start_op.visited.none()
|
||||
|| ! start_op.enriched.startable
|
||||
|| start_op.visited.rejected()
|
||||
|| ! (start_op.operation == target_operation
|
||||
|| start_op.operation == detail::overlay::operation_continue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ring_type ring;
|
||||
traverse_error_type traverse_error = traverse(ring, turn_index, op_index);
|
||||
|
||||
if (traverse_error == traverse_error_none)
|
||||
{
|
||||
std::size_t const min_num_points
|
||||
= core_detail::closure::minimum_ring_size
|
||||
<
|
||||
geometry::closure<ring_type>::value
|
||||
>::value;
|
||||
|
||||
if (geometry::num_points(ring) >= min_num_points)
|
||||
{
|
||||
clean_closing_dups_and_spikes(ring,
|
||||
m_intersection_strategy.get_side_strategy(),
|
||||
m_robust_policy);
|
||||
rings.push_back(ring);
|
||||
|
||||
m_trav.finalize_visit_info(m_turn_info_map);
|
||||
finalized_ring_size++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Backtrack::apply(
|
||||
finalized_ring_size,
|
||||
rings, ring, m_turns, start_turn,
|
||||
m_turns[turn_index].operations[op_index],
|
||||
traverse_error,
|
||||
m_geometry1, m_geometry2,
|
||||
m_intersection_strategy, m_robust_policy,
|
||||
state, m_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
int get_operation_index(turn_type const& turn) const
|
||||
{
|
||||
// When starting with a continue operation, the one
|
||||
// with the smallest (for intersection) or largest (for union)
|
||||
// remaining distance (#8310b)
|
||||
// Also to avoid skipping a turn in between, which can happen
|
||||
// in rare cases (e.g. #130)
|
||||
static const bool is_union
|
||||
= operation_from_overlay<OverlayType>::value == operation_union;
|
||||
|
||||
turn_operation_type const& op0 = turn.operations[0];
|
||||
turn_operation_type const& op1 = turn.operations[1];
|
||||
return op0.remaining_distance <= op1.remaining_distance
|
||||
? (is_union ? 1 : 0)
|
||||
: (is_union ? 0 : 1);
|
||||
}
|
||||
|
||||
template <typename Rings>
|
||||
void iterate(Rings& rings, std::size_t& finalized_ring_size,
|
||||
typename Backtrack::state_type& state)
|
||||
{
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
|
||||
if (turn.discarded || turn.blocked())
|
||||
{
|
||||
// Skip discarded and blocked turns
|
||||
continue;
|
||||
}
|
||||
|
||||
if (turn.both(operation_continue))
|
||||
{
|
||||
traverse_with_operation(turn, turn_index,
|
||||
get_operation_index(turn),
|
||||
rings, finalized_ring_size, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int op_index = 0; op_index < 2; op_index++)
|
||||
{
|
||||
traverse_with_operation(turn, turn_index, op_index,
|
||||
rings, finalized_ring_size, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Rings>
|
||||
void iterate_with_preference(std::size_t phase,
|
||||
Rings& rings, std::size_t& finalized_ring_size,
|
||||
typename Backtrack::state_type& state)
|
||||
{
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
|
||||
if (turn.discarded || turn.blocked())
|
||||
{
|
||||
// Skip discarded and blocked turns
|
||||
continue;
|
||||
}
|
||||
|
||||
turn_operation_type const& op0 = turn.operations[0];
|
||||
turn_operation_type const& op1 = turn.operations[1];
|
||||
|
||||
if (phase == 0)
|
||||
{
|
||||
if (! op0.enriched.prefer_start && ! op1.enriched.prefer_start)
|
||||
{
|
||||
// Not preferred, take next one
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (turn.both(operation_continue))
|
||||
{
|
||||
traverse_with_operation(turn, turn_index,
|
||||
get_operation_index(turn),
|
||||
rings, finalized_ring_size, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool const forward = op0.enriched.prefer_start;
|
||||
|
||||
int op_index = forward ? 0 : 1;
|
||||
int const increment = forward ? 1 : -1;
|
||||
|
||||
for (int i = 0; i < 2; i++, op_index += increment)
|
||||
{
|
||||
traverse_with_operation(turn, turn_index, op_index,
|
||||
rings, finalized_ring_size, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
traversal_type m_trav;
|
||||
|
||||
Geometry1 const& m_geometry1;
|
||||
Geometry2 const& m_geometry2;
|
||||
Turns& m_turns;
|
||||
TurnInfoMap& m_turn_info_map; // contains turn-info information per ring
|
||||
Clusters const& m_clusters;
|
||||
IntersectionStrategy const& m_intersection_strategy;
|
||||
RobustPolicy const& m_robust_policy;
|
||||
Visitor& m_visitor;
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP
|
||||
@@ -0,0 +1,674 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2015-2016 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// This file was modified by Oracle on 2018.
|
||||
// Modifications copyright (c) 2018 Oracle and/or its affiliates.
|
||||
|
||||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
#include <boost/geometry/core/assert.hpp>
|
||||
#include <boost/geometry/util/condition.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
// Generic function (is this used somewhere else too?)
|
||||
inline ring_identifier ring_id_by_seg_id(segment_identifier const& seg_id)
|
||||
{
|
||||
return ring_identifier(seg_id.source_index, seg_id.multi_index, seg_id.ring_index);
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
bool Reverse1,
|
||||
bool Reverse2,
|
||||
overlay_type OverlayType,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename RobustPolicy,
|
||||
typename Visitor
|
||||
>
|
||||
struct traversal_switch_detector
|
||||
{
|
||||
static const operation_type target_operation
|
||||
= operation_from_overlay<OverlayType>::value;
|
||||
static const operation_type opposite_operation
|
||||
= target_operation == operation_union
|
||||
? operation_intersection
|
||||
: operation_union;
|
||||
|
||||
enum isolation_type
|
||||
{
|
||||
isolation_unknown = -1,
|
||||
isolation_no = 0,
|
||||
isolation_yes = 1,
|
||||
isolation_multiple = 2
|
||||
};
|
||||
|
||||
typedef typename boost::range_value<Turns>::type turn_type;
|
||||
typedef typename turn_type::turn_operation_type turn_operation_type;
|
||||
typedef std::set<signed_size_type> set_type;
|
||||
|
||||
// Per ring, first turns are collected (in turn_indices), and later
|
||||
// a region_id is assigned
|
||||
struct merged_ring_properties
|
||||
{
|
||||
signed_size_type region_id;
|
||||
set_type turn_indices;
|
||||
|
||||
merged_ring_properties()
|
||||
: region_id(-1)
|
||||
{}
|
||||
};
|
||||
|
||||
struct connection_properties
|
||||
{
|
||||
std::size_t count;
|
||||
// Contains turn-index OR, if clustered, minus-cluster_id
|
||||
set_type unique_turn_ids;
|
||||
connection_properties()
|
||||
: count(0)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::map<signed_size_type, connection_properties> connection_map;
|
||||
|
||||
// Per region, a set of properties is maintained, including its connections
|
||||
// to other regions
|
||||
struct region_properties
|
||||
{
|
||||
signed_size_type region_id;
|
||||
isolation_type isolated;
|
||||
set_type unique_turn_ids;
|
||||
|
||||
// Maps from connected region_id to their properties
|
||||
connection_map connected_region_counts;
|
||||
|
||||
region_properties()
|
||||
: region_id(-1)
|
||||
, isolated(isolation_unknown)
|
||||
{}
|
||||
};
|
||||
|
||||
// Keeps turn indices per ring
|
||||
typedef std::map<ring_identifier, merged_ring_properties > merge_map;
|
||||
typedef std::map<signed_size_type, region_properties> region_connection_map;
|
||||
|
||||
typedef set_type::const_iterator set_iterator;
|
||||
|
||||
inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
Turns& turns, Clusters& clusters,
|
||||
RobustPolicy const& robust_policy, Visitor& visitor)
|
||||
: m_geometry1(geometry1)
|
||||
, m_geometry2(geometry2)
|
||||
, m_turns(turns)
|
||||
, m_clusters(clusters)
|
||||
, m_robust_policy(robust_policy)
|
||||
, m_visitor(visitor)
|
||||
{
|
||||
}
|
||||
|
||||
bool one_connection_to_another_region(region_properties const& region) const
|
||||
{
|
||||
// For example:
|
||||
// +----------------------+
|
||||
// | __ |
|
||||
// | / \|
|
||||
// | | x
|
||||
// | \__/|
|
||||
// | |
|
||||
// +----------------------+
|
||||
|
||||
if (region.connected_region_counts.size() == 1)
|
||||
{
|
||||
connection_properties const& cprop = region.connected_region_counts.begin()->second;
|
||||
return cprop.count <= 1;
|
||||
}
|
||||
return region.connected_region_counts.empty();
|
||||
}
|
||||
|
||||
// TODO: might be combined with previous
|
||||
bool multiple_connections_to_one_region(region_properties const& region) const
|
||||
{
|
||||
// For example:
|
||||
// +----------------------+
|
||||
// | __ |
|
||||
// | / \|
|
||||
// | | x
|
||||
// | \ /|
|
||||
// | / \|
|
||||
// | | x
|
||||
// | \__/|
|
||||
// | |
|
||||
// +----------------------+
|
||||
|
||||
if (region.connected_region_counts.size() == 1)
|
||||
{
|
||||
connection_properties const& cprop = region.connected_region_counts.begin()->second;
|
||||
return cprop.count > 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool one_connection_to_multiple_regions(region_properties const& region) const
|
||||
{
|
||||
// For example:
|
||||
// +----------------------+
|
||||
// | __ | __
|
||||
// | / \|/ |
|
||||
// | | x |
|
||||
// | \__/|\__|
|
||||
// | |
|
||||
// +----------------------+
|
||||
|
||||
bool first = true;
|
||||
signed_size_type first_turn_id = 0;
|
||||
for (typename connection_map::const_iterator it = region.connected_region_counts.begin();
|
||||
it != region.connected_region_counts.end(); ++it)
|
||||
{
|
||||
connection_properties const& cprop = it->second;
|
||||
|
||||
if (cprop.count != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin();
|
||||
if (first)
|
||||
{
|
||||
first_turn_id = unique_turn_id;
|
||||
first = false;
|
||||
}
|
||||
else if (first_turn_id != unique_turn_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ii_turn_connects_two_regions(region_properties const& region,
|
||||
region_properties const& connected_region,
|
||||
signed_size_type turn_index) const
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
if (! turn.both(operation_intersection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
signed_size_type const id0 = turn.operations[0].enriched.region_id;
|
||||
signed_size_type const id1 = turn.operations[1].enriched.region_id;
|
||||
|
||||
return (id0 == region.region_id && id1 == connected_region.region_id)
|
||||
|| (id1 == region.region_id && id0 == connected_region.region_id);
|
||||
}
|
||||
|
||||
|
||||
bool isolated_multiple_connection(region_properties const& region,
|
||||
region_properties const& connected_region) const
|
||||
{
|
||||
if (connected_region.isolated != isolation_multiple)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First step: compare turns of regions with turns of connected region
|
||||
set_type turn_ids = region.unique_turn_ids;
|
||||
for (set_iterator sit = connected_region.unique_turn_ids.begin();
|
||||
sit != connected_region.unique_turn_ids.end(); ++sit)
|
||||
{
|
||||
turn_ids.erase(*sit);
|
||||
}
|
||||
|
||||
// There should be one connection (turn or cluster) left
|
||||
if (turn_ids.size() != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (set_iterator sit = connected_region.unique_turn_ids.begin();
|
||||
sit != connected_region.unique_turn_ids.end(); ++sit)
|
||||
{
|
||||
signed_size_type const id_or_index = *sit;
|
||||
if (id_or_index >= 0)
|
||||
{
|
||||
if (! ii_turn_connects_two_regions(region, connected_region, id_or_index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
signed_size_type const cluster_id = -id_or_index;
|
||||
typename Clusters::const_iterator it = m_clusters.find(cluster_id);
|
||||
if (it != m_clusters.end())
|
||||
{
|
||||
cluster_info const& cinfo = it->second;
|
||||
for (set_iterator cit = cinfo.turn_indices.begin();
|
||||
cit != cinfo.turn_indices.end(); ++cit)
|
||||
{
|
||||
if (! ii_turn_connects_two_regions(region, connected_region, *cit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_only_isolated_children(region_properties const& region) const
|
||||
{
|
||||
bool first_with_turn = true;
|
||||
signed_size_type first_turn_id = 0;
|
||||
|
||||
for (typename connection_map::const_iterator it = region.connected_region_counts.begin();
|
||||
it != region.connected_region_counts.end(); ++it)
|
||||
{
|
||||
signed_size_type const region_id = it->first;
|
||||
connection_properties const& cprop = it->second;
|
||||
|
||||
typename region_connection_map::const_iterator mit = m_connected_regions.find(region_id);
|
||||
if (mit == m_connected_regions.end())
|
||||
{
|
||||
// Should not occur
|
||||
return false;
|
||||
}
|
||||
|
||||
region_properties const& connected_region = mit->second;
|
||||
|
||||
if (cprop.count != 1)
|
||||
{
|
||||
// If there are more connections, check their isolation
|
||||
if (! isolated_multiple_connection(region, connected_region))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (connected_region.isolated != isolation_yes
|
||||
&& connected_region.isolated != isolation_multiple)
|
||||
{
|
||||
signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin();
|
||||
if (first_with_turn)
|
||||
{
|
||||
first_turn_id = unique_turn_id;
|
||||
first_with_turn = false;
|
||||
}
|
||||
else if (first_turn_id != unique_turn_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one connection (with a 'parent'), and all other
|
||||
// connections are itself isolated, it is isolated
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_isolated_regions()
|
||||
{
|
||||
typedef typename region_connection_map::iterator it_type;
|
||||
|
||||
// First time: check regions isolated (one connection only),
|
||||
// semi-isolated (multiple connections between same region),
|
||||
// and complex isolated (connection with multiple rings but all
|
||||
// at same point)
|
||||
for (it_type it = m_connected_regions.begin();
|
||||
it != m_connected_regions.end(); ++it)
|
||||
{
|
||||
region_properties& properties = it->second;
|
||||
if (one_connection_to_another_region(properties))
|
||||
{
|
||||
properties.isolated = isolation_yes;
|
||||
}
|
||||
else if (multiple_connections_to_one_region(properties))
|
||||
{
|
||||
properties.isolated = isolation_multiple;
|
||||
}
|
||||
else if (one_connection_to_multiple_regions(properties))
|
||||
{
|
||||
properties.isolated = isolation_yes;
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate isolation to next level
|
||||
// TODO: should be optimized
|
||||
std::size_t defensive_check = 0;
|
||||
bool changed = true;
|
||||
while (changed && defensive_check++ < m_connected_regions.size())
|
||||
{
|
||||
changed = false;
|
||||
for (it_type it = m_connected_regions.begin();
|
||||
it != m_connected_regions.end(); ++it)
|
||||
{
|
||||
region_properties& properties = it->second;
|
||||
|
||||
if (properties.isolated == isolation_unknown
|
||||
&& has_only_isolated_children(properties))
|
||||
{
|
||||
properties.isolated = isolation_yes;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assign_isolation()
|
||||
{
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type& turn = m_turns[turn_index];
|
||||
|
||||
for (int op_index = 0; op_index < 2; op_index++)
|
||||
{
|
||||
turn_operation_type& op = turn.operations[op_index];
|
||||
typename region_connection_map::const_iterator mit = m_connected_regions.find(op.enriched.region_id);
|
||||
if (mit != m_connected_regions.end())
|
||||
{
|
||||
region_properties const& prop = mit->second;
|
||||
op.enriched.isolated = prop.isolated == isolation_yes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assign_region_ids()
|
||||
{
|
||||
for (typename merge_map::const_iterator it
|
||||
= m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
|
||||
{
|
||||
ring_identifier const& ring_id = it->first;
|
||||
merged_ring_properties const& properties = it->second;
|
||||
|
||||
for (set_iterator sit = properties.turn_indices.begin();
|
||||
sit != properties.turn_indices.end(); ++sit)
|
||||
{
|
||||
turn_type& turn = m_turns[*sit];
|
||||
|
||||
if (! acceptable(turn))
|
||||
{
|
||||
// No assignment necessary
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
turn_operation_type& op = turn.operations[i];
|
||||
if (ring_id_by_seg_id(op.seg_id) == ring_id)
|
||||
{
|
||||
op.enriched.region_id = properties.region_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assign_connected_regions()
|
||||
{
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
|
||||
signed_size_type const unique_turn_id
|
||||
= turn.is_clustered() ? -turn.cluster_id : turn_index;
|
||||
|
||||
turn_operation_type op0 = turn.operations[0];
|
||||
turn_operation_type op1 = turn.operations[1];
|
||||
|
||||
signed_size_type const& id0 = op0.enriched.region_id;
|
||||
signed_size_type const& id1 = op1.enriched.region_id;
|
||||
|
||||
// Add region (by assigning) and add involved turns
|
||||
if (id0 != -1)
|
||||
{
|
||||
m_connected_regions[id0].region_id = id0;
|
||||
m_connected_regions[id0].unique_turn_ids.insert(unique_turn_id);
|
||||
}
|
||||
if (id1 != -1 && id0 != id1)
|
||||
{
|
||||
m_connected_regions[id1].region_id = id1;
|
||||
m_connected_regions[id1].unique_turn_ids.insert(unique_turn_id);
|
||||
}
|
||||
|
||||
if (id0 != id1 && id0 != -1 && id1 != -1)
|
||||
{
|
||||
// Assign connections
|
||||
connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1];
|
||||
connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0];
|
||||
|
||||
// Reference this turn or cluster to later check uniqueness on ring
|
||||
if (prop0.unique_turn_ids.count(unique_turn_id) == 0)
|
||||
{
|
||||
prop0.count++;
|
||||
prop0.unique_turn_ids.insert(unique_turn_id);
|
||||
}
|
||||
if (prop1.unique_turn_ids.count(unique_turn_id) == 0)
|
||||
{
|
||||
prop1.count++;
|
||||
prop1.unique_turn_ids.insert(unique_turn_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool acceptable(turn_type const& turn) const
|
||||
{
|
||||
// Discarded turns don't connect rings to the same region
|
||||
// Also xx are not relevant
|
||||
// (otherwise discarded colocated uu turn could make a connection)
|
||||
return ! turn.discarded
|
||||
&& ! turn.both(operation_blocked);
|
||||
}
|
||||
|
||||
inline bool connects_same_region(turn_type const& turn) const
|
||||
{
|
||||
if (! acceptable(turn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! turn.is_clustered())
|
||||
{
|
||||
// If it is a uu/ii-turn (non clustered), it is never same region
|
||||
return ! (turn.both(operation_union) || turn.both(operation_intersection));
|
||||
}
|
||||
|
||||
if (BOOST_GEOMETRY_CONDITION(target_operation == operation_union))
|
||||
{
|
||||
// It is a cluster, check zones
|
||||
// (assigned by sort_by_side/handle colocations) of both operations
|
||||
return turn.operations[0].enriched.zone
|
||||
== turn.operations[1].enriched.zone;
|
||||
}
|
||||
|
||||
// For an intersection, two regions connect if they are not ii
|
||||
// (ii-regions are isolated) or, in some cases, not iu (for example
|
||||
// when a multi-polygon is inside an interior ring and connecting it)
|
||||
return ! (turn.both(operation_intersection)
|
||||
|| turn.combination(operation_intersection, operation_union));
|
||||
}
|
||||
|
||||
|
||||
inline signed_size_type get_region_id(turn_operation_type const& op) const
|
||||
{
|
||||
return op.enriched.region_id;
|
||||
}
|
||||
|
||||
|
||||
void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id,
|
||||
merged_ring_properties& properties, signed_size_type region_id = -1)
|
||||
{
|
||||
if (properties.region_id > 0)
|
||||
{
|
||||
// Already handled
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign new id if this is a new region
|
||||
if (region_id == -1)
|
||||
{
|
||||
region_id = new_region_id++;
|
||||
}
|
||||
|
||||
// Assign this ring to specified region
|
||||
properties.region_id = region_id;
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
|
||||
std::cout << " ADD " << ring_id << " TO REGION " << region_id << std::endl;
|
||||
#endif
|
||||
|
||||
// Find connecting rings, recursively
|
||||
for (set_iterator sit = properties.turn_indices.begin();
|
||||
sit != properties.turn_indices.end(); ++sit)
|
||||
{
|
||||
signed_size_type const turn_index = *sit;
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
if (! connects_same_region(turn))
|
||||
{
|
||||
// This is a non clustered uu/ii-turn, or a cluster connecting different 'zones'
|
||||
continue;
|
||||
}
|
||||
|
||||
// Union: This turn connects two rings (interior connected), create the region
|
||||
// Intersection: This turn connects two rings, set same regions for these two rings
|
||||
for (int op_index = 0; op_index < 2; op_index++)
|
||||
{
|
||||
turn_operation_type const& op = turn.operations[op_index];
|
||||
ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id);
|
||||
if (connected_ring_id != ring_id)
|
||||
{
|
||||
propagate_region(new_region_id, connected_ring_id, region_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void propagate_region(signed_size_type& new_region_id,
|
||||
ring_identifier const& ring_id, signed_size_type region_id)
|
||||
{
|
||||
typename merge_map::iterator it = m_turns_per_ring.find(ring_id);
|
||||
if (it != m_turns_per_ring.end())
|
||||
{
|
||||
create_region(new_region_id, ring_id, it->second, region_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void iterate()
|
||||
{
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
|
||||
std::cout << "BEGIN ITERATION GETTING REGION_IDS" << std::endl;
|
||||
#endif
|
||||
|
||||
// Collect turns per ring
|
||||
m_turns_per_ring.clear();
|
||||
m_connected_regions.clear();
|
||||
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
|
||||
if (turn.discarded
|
||||
&& BOOST_GEOMETRY_CONDITION(target_operation == operation_intersection))
|
||||
{
|
||||
// Discarded turn (union currently still needs it to determine regions)
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int op_index = 0; op_index < 2; op_index++)
|
||||
{
|
||||
turn_operation_type const& op = turn.operations[op_index];
|
||||
m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].turn_indices.insert(turn_index);
|
||||
}
|
||||
}
|
||||
|
||||
// All rings having turns are in turns/ring map. Process them.
|
||||
{
|
||||
signed_size_type new_region_id = 1;
|
||||
for (typename merge_map::iterator it
|
||||
= m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
|
||||
{
|
||||
create_region(new_region_id, it->first, it->second);
|
||||
}
|
||||
|
||||
assign_region_ids();
|
||||
assign_connected_regions();
|
||||
get_isolated_regions();
|
||||
assign_isolation();
|
||||
}
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
|
||||
std::cout << "END ITERATION GETTIN REGION_IDS" << std::endl;
|
||||
|
||||
for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
|
||||
{
|
||||
turn_type const& turn = m_turns[turn_index];
|
||||
|
||||
if ((turn.both(operation_union) || turn.both(operation_intersection))
|
||||
&& ! turn.is_clustered())
|
||||
{
|
||||
std::cout << "UU/II RESULT "
|
||||
<< turn_index << " -> "
|
||||
<< turn.operations[0].enriched.region_id
|
||||
<< " " << turn.operations[1].enriched.region_id
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (typename Clusters::const_iterator it = m_clusters.begin(); it != m_clusters.end(); ++it)
|
||||
{
|
||||
cluster_info const& cinfo = it->second;
|
||||
std::cout << "CL RESULT " << it->first
|
||||
<< " -> " << cinfo.open_count << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Geometry1 const& m_geometry1;
|
||||
Geometry2 const& m_geometry2;
|
||||
Turns& m_turns;
|
||||
Clusters& m_clusters;
|
||||
merge_map m_turns_per_ring;
|
||||
region_connection_map m_connected_regions;
|
||||
RobustPolicy const& m_robust_policy;
|
||||
Visitor& m_visitor;
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
|
||||
@@ -0,0 +1,114 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Traverses through intersection points / geometries
|
||||
\ingroup overlay
|
||||
*/
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Geometry1,
|
||||
typename Geometry2,
|
||||
overlay_type OverlayType,
|
||||
typename Backtrack = backtrack_check_self_intersections<Geometry1, Geometry2>
|
||||
>
|
||||
class traverse
|
||||
{
|
||||
|
||||
template <typename Turns>
|
||||
static void reset_visits(Turns& turns)
|
||||
{
|
||||
for (typename boost::range_iterator<Turns>::type
|
||||
it = boost::begin(turns);
|
||||
it != boost::end(turns);
|
||||
++it)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
it->operations[i].visited.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public :
|
||||
template
|
||||
<
|
||||
typename IntersectionStrategy,
|
||||
typename RobustPolicy,
|
||||
typename Turns,
|
||||
typename Rings,
|
||||
typename TurnInfoMap,
|
||||
typename Clusters,
|
||||
typename Visitor
|
||||
>
|
||||
static inline void apply(Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
IntersectionStrategy const& intersection_strategy,
|
||||
RobustPolicy const& robust_policy,
|
||||
Turns& turns, Rings& rings,
|
||||
TurnInfoMap& turn_info_map,
|
||||
Clusters& clusters,
|
||||
Visitor& visitor)
|
||||
{
|
||||
traversal_switch_detector
|
||||
<
|
||||
Reverse1, Reverse2, OverlayType,
|
||||
Geometry1, Geometry2,
|
||||
Turns, Clusters,
|
||||
RobustPolicy, Visitor
|
||||
> switch_detector(geometry1, geometry2, turns, clusters,
|
||||
robust_policy, visitor);
|
||||
|
||||
switch_detector.iterate();
|
||||
reset_visits(turns);
|
||||
|
||||
traversal_ring_creator
|
||||
<
|
||||
Reverse1, Reverse2, OverlayType,
|
||||
Geometry1, Geometry2,
|
||||
Turns, TurnInfoMap, Clusters,
|
||||
IntersectionStrategy,
|
||||
RobustPolicy, Visitor,
|
||||
Backtrack
|
||||
> trav(geometry1, geometry2, turns, turn_info_map, clusters,
|
||||
intersection_strategy, robust_policy, visitor);
|
||||
|
||||
std::size_t finalized_ring_size = boost::size(rings);
|
||||
|
||||
typename Backtrack::state_type state;
|
||||
|
||||
trav.iterate(rings, finalized_ring_size, state);
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif // DOXYGEN_NO_DETAIL
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
||||
@@ -0,0 +1,159 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP
|
||||
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include <boost/geometry/core/coordinate_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
enum method_type
|
||||
{
|
||||
method_none,
|
||||
method_disjoint,
|
||||
method_crosses,
|
||||
method_touch,
|
||||
method_touch_interior,
|
||||
method_collinear,
|
||||
method_equal,
|
||||
method_error
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
\brief Turn operation: operation
|
||||
\details Information necessary for traversal phase (a phase
|
||||
of the overlay process). The information is gathered during the
|
||||
get_turns (segment intersection) phase.
|
||||
The class is to be included in the turn_info class, either direct
|
||||
or a derived or similar class with more (e.g. enrichment) information.
|
||||
*/
|
||||
template <typename Point, typename SegmentRatio>
|
||||
struct turn_operation
|
||||
{
|
||||
typedef SegmentRatio segment_ratio_type;
|
||||
|
||||
operation_type operation;
|
||||
segment_identifier seg_id;
|
||||
SegmentRatio fraction;
|
||||
|
||||
typedef typename coordinate_type<Point>::type comparable_distance_type;
|
||||
comparable_distance_type remaining_distance;
|
||||
|
||||
inline turn_operation()
|
||||
: operation(operation_none)
|
||||
, remaining_distance(0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
\brief Turn information: intersection point, method, and turn information
|
||||
\details Information necessary for traversal phase (a phase
|
||||
of the overlay process). The information is gathered during the
|
||||
get_turns (segment intersection) phase.
|
||||
\tparam Point point type of intersection point
|
||||
\tparam Operation gives classes opportunity to add additional info
|
||||
\tparam Container gives classes opportunity to define how operations are stored
|
||||
*/
|
||||
template
|
||||
<
|
||||
typename Point,
|
||||
typename SegmentRatio,
|
||||
typename Operation = turn_operation<Point, SegmentRatio>,
|
||||
typename Container = boost::array<Operation, 2>
|
||||
>
|
||||
struct turn_info
|
||||
{
|
||||
typedef Point point_type;
|
||||
typedef SegmentRatio segment_ratio_type;
|
||||
typedef Operation turn_operation_type;
|
||||
typedef Container container_type;
|
||||
|
||||
Point point;
|
||||
method_type method;
|
||||
bool touch_only; // True in case of method touch(interior) and lines do not cross
|
||||
signed_size_type cluster_id; // For multiple turns on same location, > 0. Else -1. 0 is unused.
|
||||
bool discarded;
|
||||
|
||||
bool has_colocated_both; // Colocated with a uu turn (for union) or ii (other)
|
||||
|
||||
Container operations;
|
||||
|
||||
inline turn_info()
|
||||
: method(method_none)
|
||||
, touch_only(false)
|
||||
, cluster_id(-1)
|
||||
, discarded(false)
|
||||
, has_colocated_both(false)
|
||||
{}
|
||||
|
||||
inline bool both(operation_type type) const
|
||||
{
|
||||
return has12(type, type);
|
||||
}
|
||||
|
||||
inline bool has(operation_type type) const
|
||||
{
|
||||
return this->operations[0].operation == type
|
||||
|| this->operations[1].operation == type;
|
||||
}
|
||||
|
||||
inline bool combination(operation_type type1, operation_type type2) const
|
||||
{
|
||||
return has12(type1, type2) || has12(type2, type1);
|
||||
}
|
||||
|
||||
inline bool blocked() const
|
||||
{
|
||||
return both(operation_blocked);
|
||||
}
|
||||
inline bool opposite() const
|
||||
{
|
||||
return both(operation_opposite);
|
||||
}
|
||||
inline bool any_blocked() const
|
||||
{
|
||||
return has(operation_blocked);
|
||||
}
|
||||
inline bool is_clustered() const
|
||||
{
|
||||
return cluster_id > 0;
|
||||
}
|
||||
|
||||
private :
|
||||
inline bool has12(operation_type type1, operation_type type2) const
|
||||
{
|
||||
return this->operations[0].operation == type1
|
||||
&& this->operations[1].operation == type2
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP
|
||||
@@ -0,0 +1,98 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
||||
|
||||
// Use, modification and distribution is subject to the Boost Software License,
|
||||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP
|
||||
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay
|
||||
{
|
||||
|
||||
class visit_info
|
||||
{
|
||||
private :
|
||||
static const int NONE = 0;
|
||||
static const int STARTED = 1;
|
||||
static const int VISITED = 2;
|
||||
static const int FINISHED = 3;
|
||||
static const int REJECTED = 4;
|
||||
|
||||
int m_visit_code;
|
||||
bool m_rejected;
|
||||
bool m_final;
|
||||
|
||||
public:
|
||||
inline visit_info()
|
||||
: m_visit_code(0)
|
||||
, m_rejected(false)
|
||||
, m_final(false)
|
||||
{}
|
||||
|
||||
inline void set_visited() { m_visit_code = VISITED; }
|
||||
inline void set_started() { m_visit_code = STARTED; }
|
||||
inline void set_finished() { m_visit_code = FINISHED; }
|
||||
inline void set_rejected()
|
||||
{
|
||||
m_visit_code = REJECTED;
|
||||
m_rejected = true;
|
||||
}
|
||||
|
||||
inline bool none() const { return m_visit_code == NONE; }
|
||||
inline bool visited() const { return m_visit_code == VISITED; }
|
||||
inline bool started() const { return m_visit_code == STARTED; }
|
||||
inline bool finished() const { return m_visit_code == FINISHED; }
|
||||
inline bool rejected() const { return m_rejected; }
|
||||
inline bool finalized() const { return m_final; }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
if (! m_rejected && ! m_final)
|
||||
{
|
||||
m_visit_code = NONE;
|
||||
}
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
*this = visit_info();
|
||||
}
|
||||
|
||||
inline void finalize()
|
||||
{
|
||||
if (visited() || started() || finished() )
|
||||
{
|
||||
m_final = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION
|
||||
friend std::ostream& operator<<(std::ostream &os, visit_info const& v)
|
||||
{
|
||||
if (v.m_visit_code != 0)
|
||||
{
|
||||
os << " VIS: " << int(v.m_visit_code);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
}} // namespace detail::overlay
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP
|
||||
Reference in New Issue
Block a user