add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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