mirror of
https://github.com/boostorg/geometry.git
synced 2026-02-01 08:32:10 +00:00
Merge branch 'feature/intvalid' into develop
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
|
||||
// Copyright (c) 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_AGGREGATE_OPERATIONS_HPP
|
||||
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
|
||||
|
||||
namespace boost { namespace geometry
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace overlay { namespace sort_by_side
|
||||
{
|
||||
|
||||
struct ring_with_direction
|
||||
{
|
||||
ring_identifier ring_id;
|
||||
direction_type direction;
|
||||
bool only_turn_on_ring;
|
||||
|
||||
|
||||
inline bool operator<(ring_with_direction const& other) const
|
||||
{
|
||||
return this->ring_id != other.ring_id
|
||||
? this->ring_id < other.ring_id
|
||||
: this->direction < other.direction;
|
||||
}
|
||||
|
||||
ring_with_direction()
|
||||
: direction(dir_unknown)
|
||||
, only_turn_on_ring(false)
|
||||
{}
|
||||
};
|
||||
|
||||
struct rank_with_rings
|
||||
{
|
||||
std::size_t rank;
|
||||
std::set<ring_with_direction> rings;
|
||||
|
||||
rank_with_rings()
|
||||
: rank(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool all_to() const
|
||||
{
|
||||
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
|
||||
it != rings.end(); ++it)
|
||||
{
|
||||
if (it->direction == sort_by_side::dir_from)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Sbs>
|
||||
inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& aggregation)
|
||||
{
|
||||
aggregation.clear();
|
||||
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
|
||||
{
|
||||
typename Sbs::rp const& ranked_point = sbs.m_ranked_points[i];
|
||||
|
||||
if (aggregation.empty() || aggregation.back().rank != ranked_point.rank)
|
||||
{
|
||||
rank_with_rings current;
|
||||
current.rank = ranked_point.rank;
|
||||
aggregation.push_back(current);
|
||||
}
|
||||
|
||||
ring_with_direction rwd;
|
||||
segment_identifier const& sid = ranked_point.seg_id;
|
||||
rwd.ring_id = ring_identifier(sid.source_index, sid.multi_index, sid.ring_index);
|
||||
rwd.direction = ranked_point.direction;
|
||||
rwd.only_turn_on_ring = ranked_point.only_turn_on_ring;
|
||||
|
||||
|
||||
aggregation.back().rings.insert(rwd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}} // namespace detail::overlay::sort_by_side
|
||||
#endif //DOXYGEN_NO_DETAIL
|
||||
|
||||
|
||||
}} // namespace boost::geometry
|
||||
|
||||
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP
|
||||
@@ -38,6 +38,7 @@ struct enrichment_info
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, zone(-1)
|
||||
, only_turn_on_ring(false)
|
||||
{}
|
||||
|
||||
// vertex to which is free travel after this IP,
|
||||
@@ -57,6 +58,7 @@ struct enrichment_info
|
||||
std::size_t count_left;
|
||||
std::size_t count_right;
|
||||
signed_size_type zone; // open zone, in cluster
|
||||
bool only_turn_on_ring; // True if it is the only turn on a ring (for clusters)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -687,6 +687,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
|
||||
sbs.apply(turn_point);
|
||||
|
||||
sbs.find_open();
|
||||
sbs.assign_zones(for_operation);
|
||||
|
||||
// Unset the startable flag for all 'closed' zones
|
||||
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
|
||||
@@ -713,7 +714,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
|
||||
}
|
||||
}
|
||||
|
||||
cinfo.open_count = sbs.open_count();
|
||||
cinfo.open_count = sbs.open_count(for_operation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,12 @@ struct ranked_point
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, operation(operation_none)
|
||||
, only_turn_on_ring(false)
|
||||
{}
|
||||
|
||||
template <typename Op>
|
||||
ranked_point(const Point& p, signed_size_type ti, int oi,
|
||||
direction_type d, operation_type op, segment_identifier sid)
|
||||
direction_type d, Op op)
|
||||
: point(p)
|
||||
, rank(0)
|
||||
, zone(-1)
|
||||
@@ -49,8 +51,9 @@ struct ranked_point
|
||||
, direction(d)
|
||||
, count_left(0)
|
||||
, count_right(0)
|
||||
, operation(op)
|
||||
, seg_id(sid)
|
||||
, operation(op.operation)
|
||||
, seg_id(op.seg_id)
|
||||
, only_turn_on_ring(op.enriched.only_turn_on_ring)
|
||||
{}
|
||||
|
||||
Point point;
|
||||
@@ -63,6 +66,7 @@ struct ranked_point
|
||||
std::size_t count_right;
|
||||
operation_type operation;
|
||||
segment_identifier seg_id;
|
||||
bool only_turn_on_ring;
|
||||
};
|
||||
|
||||
struct less_by_turn_index
|
||||
@@ -181,11 +185,36 @@ private :
|
||||
Point m_p1, m_p2;
|
||||
};
|
||||
|
||||
// Sorts vectors in counter clockwise order (by default)
|
||||
template <bool Reverse1, bool Reverse2, typename Point, 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 :
|
||||
inline void set_origin(Point const& origin)
|
||||
{
|
||||
m_origin = origin;
|
||||
@@ -202,8 +231,8 @@ struct side_sorter
|
||||
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.operation, op.seg_id));
|
||||
m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op.operation, op.seg_id));
|
||||
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)
|
||||
{
|
||||
@@ -292,8 +321,6 @@ struct side_sorter
|
||||
&segment_identifier::source_index
|
||||
>(handled);
|
||||
}
|
||||
|
||||
assign_zones();
|
||||
}
|
||||
|
||||
void reverse()
|
||||
@@ -334,8 +361,17 @@ struct side_sorter
|
||||
}
|
||||
}
|
||||
|
||||
//private :
|
||||
|
||||
typedef std::vector<rp> container_type;
|
||||
container_type m_ranked_points;
|
||||
Point m_origin;
|
||||
|
||||
private :
|
||||
|
||||
//! Check how many open spaces there are
|
||||
inline std::size_t open_count() const
|
||||
template <typename Include>
|
||||
inline std::size_t open_count(Include const& include_functor) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
std::size_t last_rank = 0;
|
||||
@@ -345,8 +381,7 @@ struct side_sorter
|
||||
|
||||
if (ranked_point.rank > last_rank
|
||||
&& ranked_point.direction == sort_by_side::dir_to
|
||||
&& ranked_point.count_left == 0
|
||||
&& ranked_point.count_right > 0)
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
result++;
|
||||
last_rank = ranked_point.rank;
|
||||
@@ -355,15 +390,6 @@ struct side_sorter
|
||||
return result;
|
||||
}
|
||||
|
||||
//protected :
|
||||
|
||||
typedef std::vector<rp> container_type;
|
||||
container_type m_ranked_points;
|
||||
Point m_origin;
|
||||
|
||||
private :
|
||||
|
||||
|
||||
std::size_t move(std::size_t index) const
|
||||
{
|
||||
std::size_t const result = index + 1;
|
||||
@@ -449,7 +475,8 @@ private :
|
||||
}
|
||||
|
||||
//! Find closed zones and assign it
|
||||
void assign_zones()
|
||||
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)
|
||||
@@ -464,8 +491,7 @@ private :
|
||||
max_rank = ranked_point.rank;
|
||||
}
|
||||
if (ranked_point.direction == sort_by_side::dir_to
|
||||
&& ranked_point.count_left == 0
|
||||
&& ranked_point.count_right > 0)
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
start_rank = ranked_point.rank + 1;
|
||||
}
|
||||
@@ -501,8 +527,7 @@ private :
|
||||
}
|
||||
|
||||
if (ranked_point.direction == sort_by_side::dir_to
|
||||
&& ranked_point.count_left == 0
|
||||
&& ranked_point.count_right > 0)
|
||||
&& include_functor(ranked_point))
|
||||
{
|
||||
rank_at_next_zone = ranked_point.rank + 1;
|
||||
if (rank_at_next_zone > max_rank)
|
||||
@@ -516,8 +541,25 @@ private :
|
||||
|
||||
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())
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#include <boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
||||
#include <boost/geometry/core/access.hpp>
|
||||
@@ -334,13 +335,10 @@ struct traversal
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool select_from_cluster(signed_size_type& turn_index,
|
||||
inline bool select_from_cluster_union(signed_size_type& turn_index,
|
||||
int& op_index, signed_size_type start_turn_index,
|
||||
sbs_type const& sbs, bool is_touching) const
|
||||
{
|
||||
bool const is_union = target_operation == operation_union;
|
||||
bool const is_intersection = target_operation == operation_intersection;
|
||||
|
||||
std::size_t selected_rank = 0;
|
||||
std::size_t min_rank = 0;
|
||||
bool result = false;
|
||||
@@ -373,11 +371,8 @@ struct traversal
|
||||
&& (ranked_point.rank > min_rank
|
||||
|| ranked_turn.both(operation_continue)))
|
||||
{
|
||||
if ((is_union
|
||||
&& ranked_op.enriched.count_left == 0
|
||||
if (ranked_op.enriched.count_left == 0
|
||||
&& ranked_op.enriched.count_right > 0)
|
||||
|| (is_intersection
|
||||
&& ranked_op.enriched.count_right == 2))
|
||||
{
|
||||
if (result && ranked_point.turn_index != start_turn_index)
|
||||
{
|
||||
@@ -388,16 +383,6 @@ struct traversal
|
||||
turn_index = ranked_point.turn_index;
|
||||
op_index = ranked_point.operation_index;
|
||||
|
||||
if (is_intersection
|
||||
&& ranked_turn.both(operation_intersection)
|
||||
&& ranked_op.visited.finalized())
|
||||
{
|
||||
// Override:
|
||||
// For a ii turn, even though one operation might be selected,
|
||||
// it should take the other one if the first one is used in a completed ring
|
||||
op_index = 1 - ranked_point.operation_index;
|
||||
}
|
||||
|
||||
result = true;
|
||||
selected_rank = ranked_point.rank;
|
||||
}
|
||||
@@ -410,10 +395,76 @@ struct traversal
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool analyze_cluster_intersection(signed_size_type& turn_index,
|
||||
int& op_index,
|
||||
sbs_type const& sbs) const
|
||||
{
|
||||
std::vector<sort_by_side::rank_with_rings> aggregation;
|
||||
sort_by_side::aggregate_operations(sbs, aggregation);
|
||||
|
||||
std::size_t selected_rank = 0;
|
||||
|
||||
for (std::size_t i = 0; i < aggregation.size(); i++)
|
||||
{
|
||||
sort_by_side::rank_with_rings const& rwr = aggregation[i];
|
||||
|
||||
if (i > 1
|
||||
&& i - 1 == selected_rank
|
||||
&& rwr.rings.size() == 1)
|
||||
{
|
||||
sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin();
|
||||
|
||||
if (rwd.only_turn_on_ring)
|
||||
{
|
||||
// Find if this arriving ring was leaving previously
|
||||
sort_by_side::ring_with_direction leaving = rwd;
|
||||
leaving.direction = sort_by_side::dir_to;
|
||||
|
||||
sort_by_side::rank_with_rings const& previous = aggregation[i - 1];
|
||||
|
||||
if (previous.rings.size() == 1
|
||||
&& previous.rings.count(leaving) == 1)
|
||||
{
|
||||
// It arrives back - if this is one of the selected, unselect it
|
||||
selected_rank = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rwr.all_to())
|
||||
{
|
||||
if (selected_rank == 0)
|
||||
{
|
||||
// Take the first (= right) where segments leave,
|
||||
// having the polygon on the right side
|
||||
selected_rank = rwr.rank;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_rank > 0)
|
||||
{
|
||||
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
|
||||
{
|
||||
typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i];
|
||||
if (ranked_point.rank == selected_rank)
|
||||
{
|
||||
// Take the first turn from this rank
|
||||
turn_index = ranked_point.turn_index;
|
||||
op_index = ranked_point.operation_index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool select_turn_from_cluster(signed_size_type& turn_index,
|
||||
int& op_index, bool& is_touching,
|
||||
signed_size_type start_turn_index,
|
||||
segment_identifier const& previous_seg_id) const
|
||||
segment_identifier const& previous_seg_id,
|
||||
bool is_start) const
|
||||
{
|
||||
bool const is_union = target_operation == operation_union;
|
||||
|
||||
@@ -475,30 +526,76 @@ struct traversal
|
||||
|
||||
sbs.apply(turn.point);
|
||||
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
|
||||
is_touching = is_union && cinfo.open_count > 1;
|
||||
if (is_touching)
|
||||
bool result = false;
|
||||
|
||||
if (is_union)
|
||||
{
|
||||
if (cinfo.switch_source)
|
||||
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
|
||||
is_touching = cinfo.open_count > 1;
|
||||
if (is_touching)
|
||||
{
|
||||
is_touching = false;
|
||||
std::cout << "CLUSTER: SWITCH SOURCES at " << turn_index << std::endl;
|
||||
if (cinfo.switch_source)
|
||||
{
|
||||
is_touching = false;
|
||||
std::cout << "CLUSTER: SWITCH SOURCES at " << turn_index << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "CLUSTER: CONTINUE at " << turn_index << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
#else
|
||||
is_touching = cinfo.open_count > 1 && ! cinfo.switch_source;
|
||||
#endif
|
||||
|
||||
if (is_touching)
|
||||
{
|
||||
std::cout << "CLUSTER: CONTINUE at " << turn_index << std::endl;
|
||||
sbs.reverse();
|
||||
}
|
||||
|
||||
result = select_from_cluster_union(turn_index, op_index, start_turn_index, sbs,
|
||||
is_touching);
|
||||
}
|
||||
#else
|
||||
is_touching = is_union && cinfo.open_count > 1 && ! cinfo.switch_source;
|
||||
#endif
|
||||
if (is_touching)
|
||||
else
|
||||
{
|
||||
sbs.reverse();
|
||||
if (is_start
|
||||
&& turn.both(operation_intersection)
|
||||
&& turn.operations[op_index].enriched.only_turn_on_ring)
|
||||
{
|
||||
// For an ii (usually interior ring), only turn on ring,
|
||||
// reverse to take first exit
|
||||
sbs.reverse();
|
||||
}
|
||||
|
||||
result = analyze_cluster_intersection(turn_index, op_index, sbs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool analyze_ii_intersection(signed_size_type& turn_index, int& op_index,
|
||||
turn_type const& current_turn,
|
||||
segment_identifier const& previous_seg_id)
|
||||
{
|
||||
sbs_type sbs;
|
||||
|
||||
// Add this turn to the sort-by-side sorter
|
||||
bool has_origin = false;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
turn_operation_type const& op = current_turn.operations[i];
|
||||
bool const is_origin = op.seg_id.source_index
|
||||
== previous_seg_id.source_index;
|
||||
has_origin = has_origin || is_origin;
|
||||
sbs.add(op, turn_index, i, m_geometry1, m_geometry2, is_origin);
|
||||
}
|
||||
|
||||
return select_from_cluster(turn_index, op_index, start_turn_index, sbs,
|
||||
is_touching);
|
||||
if (! has_origin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sbs.apply(current_turn.point);
|
||||
return analyze_cluster_intersection(turn_index, op_index, sbs);
|
||||
}
|
||||
|
||||
inline void change_index_for_self_turn(signed_size_type& to_vertex_index,
|
||||
@@ -594,7 +691,7 @@ struct traversal
|
||||
return true;
|
||||
}
|
||||
|
||||
bool select_turn(signed_size_type start_turn_index,
|
||||
bool select_turn(signed_size_type start_turn_index, int start_op_index,
|
||||
signed_size_type& turn_index,
|
||||
int& op_index,
|
||||
bool& is_touching,
|
||||
@@ -603,10 +700,37 @@ struct traversal
|
||||
segment_identifier const& previous_seg_id,
|
||||
bool is_start)
|
||||
{
|
||||
if (m_turns[turn_index].cluster_id >= 0)
|
||||
turn_type const& current_turn = m_turns[turn_index];
|
||||
|
||||
if (target_operation == operation_intersection)
|
||||
{
|
||||
bool const back_at_start_cluster
|
||||
= current_turn.cluster_id >= 0
|
||||
&& m_turns[start_turn_index].cluster_id == current_turn.cluster_id;
|
||||
|
||||
if (turn_index == start_turn_index || back_at_start_cluster)
|
||||
{
|
||||
// Intersection can always be finished if returning
|
||||
turn_index = start_turn_index;
|
||||
op_index = start_op_index;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current_turn.cluster_id < 0
|
||||
&& current_turn.both(operation_intersection))
|
||||
{
|
||||
if (analyze_ii_intersection(turn_index, op_index, current_turn,
|
||||
previous_seg_id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_turn.cluster_id >= 0)
|
||||
{
|
||||
if (! select_turn_from_cluster(turn_index, op_index, is_touching,
|
||||
start_turn_index, previous_seg_id))
|
||||
start_turn_index, previous_seg_id, is_start))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -618,8 +742,6 @@ struct traversal
|
||||
}
|
||||
else
|
||||
{
|
||||
turn_type const& current_turn = m_turns[turn_index];
|
||||
|
||||
op_index = starting_operation_index(current_turn);
|
||||
if (op_index == -1)
|
||||
{
|
||||
|
||||
@@ -121,7 +121,8 @@ struct traversal_ring_creator
|
||||
}
|
||||
|
||||
bool is_touching = false;
|
||||
if (! m_trav.select_turn(start_turn_index, turn_index, op_index,
|
||||
if (! m_trav.select_turn(start_turn_index, start_op_index,
|
||||
turn_index, op_index,
|
||||
is_touching,
|
||||
previous_op_index, previous_turn_index, previous_seg_id,
|
||||
is_start))
|
||||
@@ -187,6 +188,18 @@ struct traversal_ring_creator
|
||||
return traverse_error_none;
|
||||
}
|
||||
|
||||
if (start_turn.cluster_id >= 0)
|
||||
{
|
||||
turn_type const& turn = m_turns[current_turn_index];
|
||||
if (turn.cluster_id == start_turn.cluster_id)
|
||||
{
|
||||
turn_operation_type& op = m_turns[start_turn_index].operations[current_op_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++)
|
||||
{
|
||||
|
||||
@@ -132,6 +132,56 @@ struct traversal_switch_detector
|
||||
}
|
||||
}
|
||||
|
||||
void check_turns_per_ring(ring_identifier const& ring_id,
|
||||
std::set<signed_size_type> const& ring_turn_indices)
|
||||
{
|
||||
bool only_turn_on_ring = true;
|
||||
if (ring_turn_indices.size() > 1)
|
||||
{
|
||||
// More turns on this ring. Only leave only_turn_on_ring true
|
||||
// if they are all of the same cluster
|
||||
int cluster_id = -1;
|
||||
for (set_iterator sit = ring_turn_indices.begin();
|
||||
sit != ring_turn_indices.end(); ++sit)
|
||||
{
|
||||
turn_type const& turn = m_turns[*sit];
|
||||
if (turn.cluster_id == -1)
|
||||
{
|
||||
// Unclustered turn - and there are 2 or more turns
|
||||
// so the ring has different turns
|
||||
only_turn_on_ring = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clustered turn, check if it is the first or same as previous
|
||||
if (cluster_id == -1)
|
||||
{
|
||||
cluster_id = turn.cluster_id;
|
||||
}
|
||||
else if (turn.cluster_id != cluster_id)
|
||||
{
|
||||
only_turn_on_ring = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign result to matching operation (a turn is always on two rings)
|
||||
for (set_iterator sit = ring_turn_indices.begin();
|
||||
sit != ring_turn_indices.end(); ++sit)
|
||||
{
|
||||
turn_type& turn = m_turns[*sit];
|
||||
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.only_turn_on_ring = only_turn_on_ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void propagate_region(ring_identifier const& ring_id, int region_id)
|
||||
{
|
||||
std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it = m_turns_per_ring.find(ring_id);
|
||||
@@ -168,6 +218,7 @@ struct traversal_switch_detector
|
||||
= m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it)
|
||||
{
|
||||
create_region(it->first, it->second);
|
||||
check_turns_per_ring(it->first, it->second);
|
||||
}
|
||||
|
||||
// Now that all regions are filled, assign switch_source property
|
||||
|
||||
@@ -89,7 +89,7 @@ struct turn_info
|
||||
|
||||
Point point;
|
||||
method_type method;
|
||||
signed_size_type cluster_id;
|
||||
signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1
|
||||
bool discarded;
|
||||
bool colocated;
|
||||
bool switch_source; // For u/u turns which can either switch or not
|
||||
|
||||
@@ -25,6 +25,7 @@ test-suite boost-geometry-algorithms-overlay
|
||||
[ run get_turns_linear_linear.cpp : : : : algorithms_get_turns_linear_linear ]
|
||||
[ run get_turns_linear_linear_sph.cpp : : : : algorithms_get_turns_linear_linear_sph ]
|
||||
[ run overlay.cpp : : : : algorithms_overlay ]
|
||||
[ run sort_by_side.cpp : : : : algorithms_sort_by_side ]
|
||||
#[ run handle_touch.cpp : : : : algorithms_handle_touch ]
|
||||
[ run relative_order.cpp : : : : algorithms_relative_order ]
|
||||
[ run select_rings.cpp : : : : algorithms_select_rings ]
|
||||
|
||||
@@ -328,11 +328,15 @@ static std::string case_100_multi[2] =
|
||||
"MULTIPOLYGON(((1 1,1 2,2 2,2 1,1 1)),((1 2,0 1,0 3,1 4,1 2)))"
|
||||
};
|
||||
|
||||
static std::string case_101_multi[2] =
|
||||
static std::string case_101_multi[4] =
|
||||
{
|
||||
// interior ring / union
|
||||
"MULTIPOLYGON(((7 2,7 3,8 2,7 2)),((9 3,9 4,10 3,9 3)),((10 1,10 0,8 0,8 1,9 2,10 2,10 1)),((9 3,9 2,8 2,8 3,7 3,7 4,8 4,9 3)),((8 4,8 7,9 6,9 4,8 4)))",
|
||||
"MULTIPOLYGON(((5 1,5 2,6 3,6 4,7 5,6 5,7 6,8 6,8 5,9 5,10 5,10 1,8 1,7 0,5 0,5 1),(9 5,8 4,9 4,9 5),(8 1,8 3,7 3,7 2,6 2,7 1,8 1),(5 1,5.5 0.5,6 1,5 1),(8.5 2.5,9 2,9 3,8.5 2.5)))"
|
||||
"MULTIPOLYGON(((5 1,5 2,6 3,6 4,7 5,6 5,7 6,8 6,8 5,9 5,10 5,10 1,8 1,7 0,5 0,5 1),(9 5,8 4,9 4,9 5),(8 1,8 3,7 3,7 2,6 2,7 1,8 1),(5 1,5.5 0.5,6 1,5 1),(8.5 2.5,9 2,9 3,8.5 2.5)))",
|
||||
|
||||
// inverse versions
|
||||
"MULTIPOLYGON(((4 -1,4 8,11 8,11 -1,4 -1),(7 2,8 2,7 3,7 2),(9 3,10 3,9 4,9 3),(10 1,10 2,9 2,8 1,8 0,10 0,10 1),(9 3,8 4,7 4,7 3,8 3,8 2,9 2,9 3),(8 4,9 4,9 6,8 7,8 4)))",
|
||||
"MULTIPOLYGON(((4 -1,4 8,11 8,11 -1,4 -1),(5 1,5 0,7 0,8 1,10 1,10 5,9 5,8 5,8 6,7 6,6 5,7 5,6 4,6 3,5 2,5 1)),((9 5,9 4,8 4,9 5)),((8 1,7 1,6 2,7 2,7 3,8 3,8 1)),((5 1,6 1,5.5 0.5,5 1)),((8.5 2.5,9 3,9 2,8.5 2.5)))"
|
||||
};
|
||||
|
||||
static std::string case_102_multi[4] =
|
||||
@@ -341,7 +345,7 @@ static std::string case_102_multi[4] =
|
||||
"MULTIPOLYGON(((0 2,0 7,5 7,5 2,0 2),(4 3,4 6,1 6,2 5,1 5,1 4,3 4,4 3)),((3 4,3 5,4 5,3 4)),((2 5,3 6,3 5,2 4,2 5)))",
|
||||
"MULTIPOLYGON(((0 2,0 7,5 7,5 2,0 2),(2 4,3 5,2 5,2 4),(4 4,3 4,3 3,4 4),(4 5,4 6,3 6,4 5)))",
|
||||
|
||||
/* inverse versions (first was already having an interior, so outer ring is just removed */
|
||||
// inverse versions (first was already having an interior, so outer ring is just removed
|
||||
"MULTIPOLYGON(((4 3,3 4,1 4,1 5,2 5,1 6,4 6,4 3),(3 4,4 5,3 5,3 4),(2 5,2 4,3 5,3 6,2 5)))",
|
||||
"MULTIPOLYGON(((-1 1,-1 8,6 8,6 1,-1 1),(0 2,5 2,5 7,0 7,0 2)),((2 4,2 5,3 5,2 4)),((4 4,3 3,3 4,4 4)),((4 5,3 6,4 6,4 5)))"
|
||||
};
|
||||
@@ -381,7 +385,7 @@ static std::string case_107_multi[4] =
|
||||
"MULTIPOLYGON(((6 8,7 9,7 7,8 7,7 6,6 6,6 8)),((6.5 9.5,7 10,7 9,6 9,6 10,6.5 9.5)))",
|
||||
"MULTIPOLYGON(((5 7,6 8,6 10,7 9,8 10,8 8,7 8,6 7,6 6,5 7)))",
|
||||
|
||||
/* inverse versions */
|
||||
// inverse versions
|
||||
"MULTIPOLYGON(((5 5,5 11,9 11,9 5,5 5),(6 8,6 6,7 6,8 7,7 7,7 9,6 8),(6.5 9.5,6 10,6 9,7 9,7 10,6.5 9.5)))",
|
||||
"MULTIPOLYGON(((4 5,4 11,9 11,9 5,4 5),(5 7,6 6,6 7,7 8,8 8,8 10,7 9,6 10,6 8,5 7)))"
|
||||
};
|
||||
@@ -498,6 +502,27 @@ static std::string case_122_multi[2] =
|
||||
"MULTIPOLYGON(((7 3,7 6,9 6,9 5,11 5,11 3,7 3)),((10 6,10 9,12 9,12 6,10 6)),((7 7,7 10,10 10,9 9,9 7,7 7)))"
|
||||
};
|
||||
|
||||
static std::string case_123_multi[2] =
|
||||
{
|
||||
// Intersection: one cluster with 3 zones, intersection -> no holes
|
||||
"MULTIPOLYGON(((1 0,1 1,1.5 0.5,2 0.5,2 0,1 0)),((0 1,1 2,2 2,2 1,0 1)))",
|
||||
"MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))"
|
||||
};
|
||||
|
||||
static std::string case_124_multi[2] =
|
||||
{
|
||||
// Intersection: one cluster with 3 zones, intersection -> one hole
|
||||
"MULTIPOLYGON(((1 0,1 1,0 1,1 2,2 2,2 0,1 0),(1.5 0.5,1.75 1,1 1,1.5 0.5)))",
|
||||
"MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))"
|
||||
};
|
||||
|
||||
static std::string case_125_multi[2] =
|
||||
{
|
||||
// Intersection: one cluster with 3 zones, intersection -> one hole (filled with other polygon)
|
||||
"MULTIPOLYGON(((1 0,1 1,0 1,1 2,2 2,2 0,1 0),(1.5 0.5,1.75 1,1 1,1.5 0.5)),((1 1,1.5 0.9,1.25 0.8,1 1)))",
|
||||
"MULTIPOLYGON(((1 0,1 2,2 2,2 0,1 0)),((0 1,0 2,1 1,0 1)))"
|
||||
};
|
||||
|
||||
static std::string case_recursive_boxes_1[2] =
|
||||
{
|
||||
"MULTIPOLYGON(((1 0,0 0,0 1,1 1,1 2,0 2,0 4,2 4,2 5,3 5,3 6,1 6,1 5,0 5,0 10,9 10,9 9,7 9,7 8,6 8,6 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 0,6 0,6 1,5 1,5 0,1 0),(8 4,8 5,7 5,7 6,6 6,6 5,5 5,5 4,4 4,4 3,5 3,5 2,7 2,7 3,6 3,6 4,8 4),(8 1,9 1,9 2,8 2,8 1),(4 7,4 9,3 9,3 7,4 7)),((9 9,10 9,10 7,10 6,9 6,9 7,8 7,8 8,9 8,9 9)))",
|
||||
@@ -796,6 +821,13 @@ static std::string case_recursive_boxes_38[2] =
|
||||
"MULTIPOLYGON(((2 2,4 2,4 1,2 1,2 2)),((0 4,0 5,1 4,0 4)),((2 3,2 4,4 4,4 3,2 3)),((2 2,1 2,1 3,2 3,2 2)),((1 2,0 2,0 3,1 3,0.5 2.5,1 2)))"
|
||||
};
|
||||
|
||||
static std::string case_recursive_boxes_39[2] =
|
||||
{
|
||||
// Needs check for back at start during traversal
|
||||
"MULTIPOLYGON(((3 8,2 8,2 9,3 9,3 8)),((4 8,4 9,5 9,5 8,4 8)),((6 9,6 10,7 10,7 9,6 9)),((5 6,4 6,4 7,6 7,6 5,5 5,5 6)),((4 7,3 7,3 8,4 8,4 7)),((7 8,8 8,8 7,6 7,6 8,7 8)))",
|
||||
"MULTIPOLYGON(((3 7,3 6,2 6,2 7,3 7)),((4 8,4 7,3 7,3 8,4 8)),((6 10,7 10,7 9,5 9,5 10,6 10)),((5 8,4 8,4 9,5 9,5 8)),((5 8,6 8,6 7,5 7,5 8)))"
|
||||
};
|
||||
|
||||
static std::string pie_21_7_21_0_3[2] =
|
||||
{
|
||||
"MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))",
|
||||
|
||||
@@ -314,7 +314,9 @@ struct map_visitor
|
||||
template <typename Geometry, bg::overlay_type OverlayType>
|
||||
void test_overlay(std::string const& caseid,
|
||||
std::string const& wkt1, std::string const& wkt2,
|
||||
double expected_area)
|
||||
double expected_area,
|
||||
std::size_t expected_clip_count,
|
||||
std::size_t expected_hole_count)
|
||||
{
|
||||
Geometry g1;
|
||||
bg::read_wkt(wkt1, g1);
|
||||
@@ -386,6 +388,14 @@ void test_overlay(std::string const& caseid,
|
||||
strategy(), visitor);
|
||||
|
||||
BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001);
|
||||
BOOST_CHECK_MESSAGE((bg::num_interior_rings(result) == expected_hole_count),
|
||||
caseid
|
||||
<< " hole count: detected: " << bg::num_interior_rings(result)
|
||||
<< " expected: " << expected_hole_count);
|
||||
BOOST_CHECK_MESSAGE((result.size() == expected_clip_count),
|
||||
caseid
|
||||
<< " clip count: detected: " << result.size()
|
||||
<< " expected: " << expected_clip_count);
|
||||
|
||||
#if defined(TEST_WITH_SVG)
|
||||
mapper.map(result, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);"
|
||||
@@ -394,6 +404,20 @@ void test_overlay(std::string const& caseid,
|
||||
#endif
|
||||
}
|
||||
|
||||
#define TEST_INTERSECTION(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_intersection>) \
|
||||
( #caseid "_int", caseid[0], caseid[1], area, clips, holes)
|
||||
#define TEST_UNION(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_union>) \
|
||||
( #caseid "_union", caseid[0], caseid[1], area, clips, holes)
|
||||
#define TEST_DIFFERENCE_A(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_difference>) \
|
||||
( #caseid "_diff_a", caseid[0], caseid[1], area, clips, holes)
|
||||
#define TEST_DIFFERENCE_B(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_difference>) \
|
||||
( #caseid "_diff_b", caseid[1], caseid[0], area, clips, holes)
|
||||
|
||||
#define TEST_INTERSECTION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_intersection>) \
|
||||
( #caseid "_int_" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes)
|
||||
#define TEST_UNION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_union>) \
|
||||
( #caseid "_union" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes)
|
||||
|
||||
template <typename T>
|
||||
void test_all()
|
||||
{
|
||||
@@ -401,121 +425,37 @@ void test_all()
|
||||
typedef bg::model::polygon<point_type> polygon;
|
||||
typedef bg::model::multi_polygon<polygon> multi_polygon;
|
||||
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_multi_simplex_union",
|
||||
case_multi_simplex[0], case_multi_simplex[1],
|
||||
14.58
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_multi_simplex_intersection",
|
||||
case_multi_simplex[0], case_multi_simplex[1],
|
||||
6.42
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_difference>
|
||||
(
|
||||
"case_multi_simplex_diff_a",
|
||||
case_multi_simplex[0], case_multi_simplex[1],
|
||||
5.58
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_difference>
|
||||
(
|
||||
"case_multi_simplex_diff_b",
|
||||
case_multi_simplex[1], case_multi_simplex[0],
|
||||
2.58
|
||||
);
|
||||
TEST_UNION(case_multi_simplex, 14.58, 1, 0);
|
||||
TEST_INTERSECTION(case_multi_simplex, 6.42, 2, 0);
|
||||
|
||||
TEST_DIFFERENCE_A(case_multi_simplex, 5.58, 5, 0);
|
||||
TEST_DIFFERENCE_B(case_multi_simplex, 2.58, 4, 0);
|
||||
|
||||
// Contains 5 clusters, needing immediate selection of next turn
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_58_multi_0_3_union",
|
||||
case_58_multi[0], case_58_multi[3],
|
||||
19.8333333
|
||||
);
|
||||
TEST_UNION_WITH(case_58_multi, 0, 3, 19.8333333, 2, 0);
|
||||
|
||||
// Contains many clusters, needing to exclude u/u turns
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_recursive_boxes_3_union",
|
||||
case_recursive_boxes_3[0], case_recursive_boxes_3[1],
|
||||
56.5
|
||||
);
|
||||
TEST_UNION(case_recursive_boxes_3, 56.5, 17, 6);
|
||||
|
||||
// Contains 4 clusters, one of which having 4 turns
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_recursive_boxes_7_union",
|
||||
case_recursive_boxes_7[0], case_recursive_boxes_7[1],
|
||||
7.0
|
||||
);
|
||||
TEST_UNION(case_recursive_boxes_7, 7.0, 2, 0);
|
||||
|
||||
// Contains 5 clusters, needing immediate selection of next turn
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_89_multi_union",
|
||||
case_89_multi[0], case_89_multi[1],
|
||||
6.0
|
||||
);
|
||||
TEST_UNION(case_89_multi, 6.0, 1, 0);
|
||||
|
||||
// Needs ux/next_turn_index==-1 to be filtered out
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_77_multi_intersection",
|
||||
case_77_multi[0], case_77_multi[1],
|
||||
9.0
|
||||
);
|
||||
TEST_INTERSECTION(case_77_multi, 9.0, 5, 0);
|
||||
TEST_UNION(case_101_multi, 22.25, 1, 3);
|
||||
TEST_INTERSECTION(case_101_multi, 4.75, 4, 0);
|
||||
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_101_multi_union",
|
||||
case_101_multi[0], case_101_multi[1],
|
||||
22.25
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_101_multi_intersection",
|
||||
case_101_multi[0], case_101_multi[1],
|
||||
4.75
|
||||
);
|
||||
TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 2, 0);
|
||||
TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 4, 0);
|
||||
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_recursive_boxes_11_intersection",
|
||||
case_recursive_boxes_11[0], case_recursive_boxes_11[1],
|
||||
1.0
|
||||
);
|
||||
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_recursive_boxes_4_union",
|
||||
case_recursive_boxes_4[0], case_recursive_boxes_4[1],
|
||||
96.75
|
||||
);
|
||||
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_58_multi_b6_intersection",
|
||||
case_58_multi[6], case_58_multi[2],
|
||||
13.25
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_intersection>
|
||||
(
|
||||
"case_72_multi_intersection_inv_b",
|
||||
case_72_multi[2], case_72_multi[1],
|
||||
6.15
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_recursive_boxes_12_union",
|
||||
case_recursive_boxes_12[0], case_recursive_boxes_12[1],
|
||||
6.0
|
||||
);
|
||||
test_overlay<multi_polygon, bg::overlay_union>
|
||||
(
|
||||
"case_recursive_boxes_13_union",
|
||||
case_recursive_boxes_13[0], case_recursive_boxes_13[1],
|
||||
10.25
|
||||
);
|
||||
TEST_UNION(case_recursive_boxes_4, 96.75, 1, 2);
|
||||
TEST_INTERSECTION_WITH(case_58_multi, 2, 6, 13.25, 1, 1);
|
||||
TEST_INTERSECTION_WITH(case_72_multi, 1, 2, 6.15, 3, 1);
|
||||
TEST_UNION(case_recursive_boxes_12, 6.0, 6, 0);
|
||||
TEST_UNION(case_recursive_boxes_13, 10.25, 3, 0);
|
||||
|
||||
|
||||
// std::cout
|
||||
|
||||
@@ -514,6 +514,20 @@ static std::string case_100[2] =
|
||||
"POLYGON((4 0,8 0,8 4,4 4,4 0))"
|
||||
};
|
||||
|
||||
static std::string case_101[2] =
|
||||
{
|
||||
// Smaller adapted version of case_recursive_boxes_34, with only one cluster
|
||||
"POLYGON((0 1,0 5,5 5,5 1,0 1))",
|
||||
"POLYGON((3 6,4 5,5 4,3 2,1 4,3 6),(4 5,3 4,3.5 3.5,4 4,4 5))"
|
||||
};
|
||||
|
||||
static std::string case_102[2] =
|
||||
{
|
||||
// Smaller adapted version of case_recursive_boxes_34, with only one cluster
|
||||
"POLYGON((1.25 3.75,1.25 5,5 5,5 3.75,1.25 3.75))",
|
||||
"POLYGON((3 6,4 5,5 4,3 2,1 4,3 6),(4 5,3 4,3.5 3.5,4 4,4 5))"
|
||||
};
|
||||
|
||||
static std::string case_many_situations[2] = {
|
||||
"POLYGON((2 6,2 14,10 18,18 14,18 6,16 5,14 4,12 3,10 2,8 3,6 4,4 5,2 6))",
|
||||
"POLYGON((2 6,2 7,2 8,2 9,2 10,2 11,2 12,1 14"
|
||||
|
||||
261
test/algorithms/overlay/sort_by_side.cpp
Normal file
261
test/algorithms/overlay/sort_by_side.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
// Boost.Geometry (aka GGL, Generic Geometry Library)
|
||||
// Unit Test
|
||||
|
||||
// Copyright (c) 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)
|
||||
|
||||
#include <geometry_test_common.hpp>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
||||
#include <boost/geometry/geometries/geometries.hpp>
|
||||
|
||||
#include "multi_overlay_cases.hpp"
|
||||
|
||||
static const std::size_t default_result = 9999;
|
||||
|
||||
// Adapted copy of handle_colocations::gather_cluster_properties
|
||||
template
|
||||
<
|
||||
bool Reverse1, bool Reverse2,
|
||||
typename Turns,
|
||||
typename Clusters,
|
||||
typename Geometry1,
|
||||
typename Geometry2
|
||||
>
|
||||
std::size_t test_gather_cluster_properties(std::string const& case_id,
|
||||
Clusters& clusters, Turns& turns,
|
||||
bg::detail::overlay::operation_type for_operation,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2)
|
||||
{
|
||||
using namespace boost::geometry;
|
||||
using namespace boost::geometry::detail::overlay;
|
||||
|
||||
std::size_t result = default_result;
|
||||
|
||||
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, point_type, std::less<int>
|
||||
> sbs_type;
|
||||
|
||||
// Only test the FIRST cluster (for now)
|
||||
typename Clusters::iterator mit = clusters.begin();
|
||||
if (mit == clusters.end())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
cluster_info& cinfo = mit->second;
|
||||
std::set<signed_size_type> const& ids = cinfo.turn_indices;
|
||||
if (ids.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
sbs_type sbs;
|
||||
point_type turn_point; // should be all the same for all turns in cluster
|
||||
|
||||
bool first = true;
|
||||
for (typename 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);
|
||||
|
||||
return cinfo.open_count;
|
||||
}
|
||||
|
||||
|
||||
// Adapted copy of overlay::apply
|
||||
template
|
||||
<
|
||||
bg::overlay_type OverlayType,
|
||||
bool Reverse1, bool Reverse2, bool ReverseOut,
|
||||
typename GeometryOut,
|
||||
typename Geometry1, typename Geometry2,
|
||||
typename RobustPolicy, typename Strategy
|
||||
>
|
||||
std::size_t apply_overlay(std::string const& case_id,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
RobustPolicy const& robust_policy,
|
||||
Strategy const& )
|
||||
{
|
||||
using namespace boost::geometry;
|
||||
|
||||
typedef typename bg::point_type<GeometryOut>::type point_type;
|
||||
typedef bg::detail::overlay::traversal_turn_info
|
||||
<
|
||||
point_type,
|
||||
typename bg::segment_ratio_type<point_type, RobustPolicy>::type
|
||||
> turn_info;
|
||||
typedef std::deque<turn_info> turn_container_type;
|
||||
|
||||
typedef std::deque
|
||||
<
|
||||
typename bg::ring_type<GeometryOut>::type
|
||||
> ring_container_type;
|
||||
|
||||
// Define the clusters, mapping cluster_id -> turns
|
||||
typedef std::map
|
||||
<
|
||||
signed_size_type,
|
||||
bg::detail::overlay::cluster_info
|
||||
> cluster_type;
|
||||
|
||||
turn_container_type turns;
|
||||
|
||||
detail::get_turns::no_interrupt_policy policy;
|
||||
bg::get_turns
|
||||
<
|
||||
Reverse1, Reverse2,
|
||||
detail::overlay::assign_null_policy
|
||||
>(geometry1, geometry2, robust_policy, turns, policy);
|
||||
|
||||
typename Strategy::side_strategy_type side_strategy;
|
||||
cluster_type clusters;
|
||||
|
||||
bg::enrich_intersection_points<Reverse1, Reverse2, OverlayType>(turns,
|
||||
clusters, geometry1, geometry2,
|
||||
robust_policy,
|
||||
side_strategy);
|
||||
|
||||
if (clusters.size() == 1)
|
||||
{
|
||||
// Now gather cluster properties again, with test option
|
||||
return test_gather_cluster_properties<Reverse1, Reverse2>(case_id,
|
||||
clusters, turns, bg::detail::overlay::operation_from_overlay<OverlayType>::value,
|
||||
geometry1, geometry2);
|
||||
}
|
||||
|
||||
return default_result;
|
||||
}
|
||||
|
||||
|
||||
template <typename Geometry, bg::overlay_type OverlayType>
|
||||
void test_sort_by_side(std::string const& case_id,
|
||||
std::string const& wkt1, std::string const& wkt2,
|
||||
std::size_t expected_open_count)
|
||||
{
|
||||
// std::cout << case_id << std::endl;
|
||||
|
||||
Geometry g1;
|
||||
bg::read_wkt(wkt1, g1);
|
||||
|
||||
Geometry g2;
|
||||
bg::read_wkt(wkt2, g2);
|
||||
|
||||
// Reverse if necessary
|
||||
bg::correct(g1);
|
||||
bg::correct(g2);
|
||||
|
||||
typedef typename boost::range_value<Geometry>::type geometry_out;
|
||||
|
||||
typedef typename bg::rescale_overlay_policy_type
|
||||
<
|
||||
Geometry,
|
||||
Geometry
|
||||
>::type rescale_policy_type;
|
||||
|
||||
rescale_policy_type robust_policy
|
||||
= bg::get_rescale_policy<rescale_policy_type>(g1, g2);
|
||||
|
||||
typedef bg::intersection_strategies
|
||||
<
|
||||
typename bg::cs_tag<Geometry>::type,
|
||||
Geometry,
|
||||
Geometry,
|
||||
typename bg::point_type<Geometry>::type,
|
||||
rescale_policy_type
|
||||
> strategy;
|
||||
|
||||
std::size_t open_count = apply_overlay<OverlayType, false, false, false, geometry_out>(case_id, g1, g2,
|
||||
robust_policy,
|
||||
strategy());
|
||||
|
||||
BOOST_CHECK_MESSAGE(open_count == expected_open_count,
|
||||
" caseid=" << case_id
|
||||
<< " open count: expected=" << expected_open_count
|
||||
<< " detected=" << open_count);
|
||||
}
|
||||
|
||||
|
||||
// Define two small macro's to avoid repetitions of testcases/names etc
|
||||
#define TEST_INT(caseid, exp) { (test_sort_by_side<multi_polygon, bg::overlay_intersection>) \
|
||||
( #caseid "_int", caseid[0], caseid[1], exp); }
|
||||
|
||||
#define TEST_UNION(caseid, exp) { (test_sort_by_side<multi_polygon, bg::overlay_union>) \
|
||||
( #caseid "_union", caseid[0], caseid[1], exp); }
|
||||
|
||||
template <typename T>
|
||||
void test_all()
|
||||
{
|
||||
typedef bg::model::point<T, 2, bg::cs::cartesian> point_type;
|
||||
typedef bg::model::polygon<point_type> polygon;
|
||||
typedef bg::model::multi_polygon<polygon> multi_polygon;
|
||||
|
||||
// Selection of test cases having only one cluster
|
||||
|
||||
TEST_INT(case_62_multi, 1);
|
||||
TEST_INT(case_63_multi, 1);
|
||||
TEST_INT(case_64_multi, 1);
|
||||
TEST_INT(case_72_multi, 3);
|
||||
TEST_INT(case_107_multi, 2);
|
||||
TEST_INT(case_123_multi, 3);
|
||||
TEST_INT(case_124_multi, 3);
|
||||
TEST_INT(case_recursive_boxes_1, 2);
|
||||
TEST_INT(case_recursive_boxes_10, 2);
|
||||
TEST_INT(case_recursive_boxes_18, 0);
|
||||
TEST_INT(case_recursive_boxes_19, 0);
|
||||
TEST_INT(case_recursive_boxes_20, 2);
|
||||
TEST_INT(case_recursive_boxes_21, 1);
|
||||
TEST_INT(case_recursive_boxes_22, 0);
|
||||
TEST_INT(case_recursive_boxes_23, 1);
|
||||
|
||||
TEST_UNION(case_62_multi, 2);
|
||||
TEST_UNION(case_63_multi, 2);
|
||||
TEST_UNION(case_64_multi, 1);
|
||||
TEST_UNION(case_72_multi, 0);
|
||||
TEST_UNION(case_107_multi, 1);
|
||||
TEST_UNION(case_123_multi, 1);
|
||||
TEST_UNION(case_124_multi, 1);
|
||||
TEST_UNION(case_recursive_boxes_1, 1);
|
||||
TEST_UNION(case_recursive_boxes_10, 1);
|
||||
TEST_UNION(case_recursive_boxes_18, 3);
|
||||
TEST_UNION(case_recursive_boxes_19, 3);
|
||||
TEST_UNION(case_recursive_boxes_20, 2);
|
||||
TEST_UNION(case_recursive_boxes_21, 1);
|
||||
TEST_UNION(case_recursive_boxes_22, 1);
|
||||
TEST_UNION(case_recursive_boxes_23, 3);
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_all<double>();
|
||||
return 0;
|
||||
}
|
||||
@@ -216,8 +216,17 @@ void test_all()
|
||||
case_100[0], case_100[1],
|
||||
1, 7, 3.125,
|
||||
1, 7, 16.0,
|
||||
1, 13, 16.0 + 3.125,
|
||||
ignore_validity);
|
||||
1, 13, 16.0 + 3.125);
|
||||
|
||||
test_one<polygon, polygon, polygon>("case_101",
|
||||
case_101[0], case_101[1],
|
||||
3, 17, 13.75,
|
||||
1, 4, 1.0);
|
||||
|
||||
test_one<polygon, polygon, polygon>("case_102",
|
||||
case_102[0], case_102[1],
|
||||
4, 18, 1.5,
|
||||
3, 15, 4.0625);
|
||||
|
||||
test_one<polygon, polygon, polygon>("winded",
|
||||
winded[0], winded[1],
|
||||
@@ -406,7 +415,7 @@ void test_all()
|
||||
test_one<polygon, polygon, polygon>("ticket_9563",
|
||||
ticket_9563[0], ticket_9563[1],
|
||||
0, 0, 0,
|
||||
1, 20, 20.096189, ignore_validity);
|
||||
6, 24, 20.096189);
|
||||
#endif
|
||||
|
||||
test_one<polygon, polygon, polygon>("ticket_10108_a",
|
||||
@@ -516,26 +525,25 @@ void test_all()
|
||||
// also mysql_23023665
|
||||
test_one<polygon, polygon, polygon>("mysql_21965285",
|
||||
mysql_21965285[0], mysql_21965285[1],
|
||||
1, 2 - correction_for_invalidity, -1, 92.0,
|
||||
1, 2, -1, 92.0,
|
||||
1, 1, -1, 14.0,
|
||||
1, 2, -1, 92.0 + 14.0,
|
||||
ignore_validity);
|
||||
1, 2, -1, 92.0 + 14.0);
|
||||
|
||||
test_one<polygon, polygon, polygon>("mysql_23023665_1",
|
||||
mysql_23023665_1[0], mysql_23023665_1[1],
|
||||
1, 2 - correction_for_invalidity, -1, 92.0,
|
||||
1, 2, -1, 92.0,
|
||||
1, 1, -1, 142.5,
|
||||
ignore_validity);
|
||||
|
||||
test_one<polygon, polygon, polygon>("mysql_23023665_2",
|
||||
mysql_23023665_2[0], mysql_23023665_2[1],
|
||||
1, 2 - correction_for_invalidity, -1, 96.0,
|
||||
1, 2, -1, 96.0,
|
||||
1, 1, -1, 16.0,
|
||||
ignore_validity);
|
||||
|
||||
test_one<polygon, polygon, polygon>("mysql_23023665_3",
|
||||
mysql_23023665_3[0], mysql_23023665_3[1],
|
||||
1, 2 - correction_for_invalidity, -1, 225.0,
|
||||
1, 2, -1, 225.0,
|
||||
1, 1, -1, 66.0,
|
||||
ignore_validity);
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
|
||||
#include <boost/geometry/io/wkt/read.hpp>
|
||||
|
||||
#define TEST_DIFFERENCE(caseid, clips1, points1, area1, clips2, points2, area2) \
|
||||
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
|
||||
( #caseid, caseid[0], caseid[1], clips1, points1, area1, clips2, points2, area2)
|
||||
|
||||
|
||||
template <typename Ring, typename Polygon, typename MultiPolygon>
|
||||
void test_areal()
|
||||
{
|
||||
@@ -86,6 +91,9 @@ void test_areal()
|
||||
case_78_multi[0], case_78_multi[1],
|
||||
1, 5, 1.0, 1, 5, 1.0);
|
||||
|
||||
TEST_DIFFERENCE(case_123_multi, 1, 4, 0.25, 2, 9, 0.625);
|
||||
TEST_DIFFERENCE(case_124_multi, 1, 4, 0.25, 2, 9, 0.4375);
|
||||
|
||||
{
|
||||
ut_settings settings;
|
||||
|
||||
@@ -153,6 +161,8 @@ void test_areal()
|
||||
ignore_validity);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Regression (intersections valid): fails to output two triangles in A
|
||||
// Areas and #clips correspond with POSTGIS (except sym case)
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_101_multi",
|
||||
case_101_multi[0], case_101_multi[1],
|
||||
@@ -160,12 +170,14 @@ void test_areal()
|
||||
5, 40, 12.75,
|
||||
5, 48, 4.75 + 12.75);
|
||||
|
||||
// Regression (intersections valid): fails to output one triangle in A
|
||||
// Areas and #clips correspond with POSTGIS
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_102_multi",
|
||||
case_102_multi[0], case_102_multi[1],
|
||||
2, 8, 0.75,
|
||||
6, 25, 3.75,
|
||||
6, 27, 0.75 + 3.75);
|
||||
#endif
|
||||
|
||||
// Areas and #clips correspond with POSTGIS
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_107_multi",
|
||||
|
||||
@@ -332,6 +332,13 @@ void test_areal()
|
||||
case_81[0], case_81[1],
|
||||
0, -1, 0.0);
|
||||
|
||||
test_one<Polygon, Polygon, Polygon>("case_101",
|
||||
case_101[0], case_101[1],
|
||||
0, -1, 6.25);
|
||||
test_one<Polygon, Polygon, Polygon>("case_102",
|
||||
case_102[0], case_102[1],
|
||||
0, -1, 3.1875);
|
||||
|
||||
test_one<Polygon, Polygon, Polygon>("mysql_21964049",
|
||||
mysql_21964049[0], mysql_21964049[1],
|
||||
0, -1, 0.0);
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
|
||||
#include <boost/geometry/io/wkt/read.hpp>
|
||||
|
||||
#define TEST_INTERSECTION(caseid, clips, points, area) \
|
||||
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
|
||||
( #caseid, caseid[0], caseid[1], clips, points, area)
|
||||
|
||||
#define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \
|
||||
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
|
||||
( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity)
|
||||
|
||||
|
||||
template <typename Ring, typename Polygon, typename MultiPolygon>
|
||||
void test_areal()
|
||||
{
|
||||
@@ -102,8 +111,7 @@ void test_areal()
|
||||
3, 14, 2.85);
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_72_multi_inv_b",
|
||||
case_72_multi[1], case_72_multi[2],
|
||||
3, 16, 6.15,
|
||||
ignore_validity);
|
||||
3, 16, 6.15);
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_77_multi",
|
||||
case_77_multi[0], case_77_multi[1],
|
||||
5, 33, 9.0);
|
||||
@@ -133,9 +141,16 @@ void test_areal()
|
||||
5, 33, 7.5,
|
||||
ignore_validity);
|
||||
#endif
|
||||
|
||||
TEST_INTERSECTION(case_123_multi, 3, 13, 1.875);
|
||||
TEST_INTERSECTION(case_124_multi, 2, 13, 2.0625);
|
||||
TEST_INTERSECTION_IGNORE(case_125_multi, 3, 17, 2.1);
|
||||
|
||||
// #1 needs self-turns to make valid
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_1",
|
||||
case_recursive_boxes_1[0], case_recursive_boxes_1[1],
|
||||
8, 97, 47.0, ignore_validity);
|
||||
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_2",
|
||||
case_recursive_boxes_2[0], case_recursive_boxes_2[1],
|
||||
1, 50, 90.0); // Area from SQL Server
|
||||
@@ -149,6 +164,7 @@ void test_areal()
|
||||
ignore_validity);
|
||||
|
||||
// Fixed by replacing handle_tangencies in less_by_segment_ratio sort order
|
||||
// Should contain 6 output polygons
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_6",
|
||||
case_recursive_boxes_6[0], case_recursive_boxes_6[1],
|
||||
6, 47, 19.0);
|
||||
@@ -238,8 +254,7 @@ void test_areal()
|
||||
3, 0, 2.0);
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_34",
|
||||
case_recursive_boxes_34[0], case_recursive_boxes_34[1],
|
||||
2, 0, 17.25,
|
||||
ignore_validity);
|
||||
2, 0, 17.25);
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_35",
|
||||
case_recursive_boxes_35[0], case_recursive_boxes_35[1],
|
||||
1, 0, 20.0,
|
||||
@@ -251,6 +266,8 @@ void test_areal()
|
||||
case_recursive_boxes_37[0], case_recursive_boxes_37[1],
|
||||
2, 0, 1.0);
|
||||
|
||||
TEST_INTERSECTION(case_recursive_boxes_39, 3, 0, 3.00);
|
||||
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120915_h2_a",
|
||||
ggl_list_20120915_h2[0], ggl_list_20120915_h2[1],
|
||||
2, 10, 6.0); // Area from SQL Server
|
||||
|
||||
@@ -231,6 +231,12 @@ void test_areal()
|
||||
test_one<Polygon, Polygon, Polygon>("100",
|
||||
case_100[0], case_100[1], 1, 1, 13, 19.125);
|
||||
|
||||
test_one<Polygon, Polygon, Polygon>("101",
|
||||
case_101[0], case_101[1], 1, 0, 9, 21.0);
|
||||
|
||||
test_one<Polygon, Polygon, Polygon>("102",
|
||||
case_102[0], case_102[1], 1, 1, 17, 8.75);
|
||||
|
||||
/*
|
||||
test_one<Polygon, Polygon, Polygon>(102,
|
||||
simplex_normal[0], simplex_reversed[1],
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include <boost/geometry/io/wkt/read.hpp>
|
||||
|
||||
|
||||
#define TEST_UNION(caseid, clips, holes, points, area) \
|
||||
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
|
||||
( #caseid, caseid[0], caseid[1], clips, holes, points, area)
|
||||
|
||||
template <typename Ring, typename Polygon, typename MultiPolygon>
|
||||
void test_areal()
|
||||
{
|
||||
@@ -177,6 +181,9 @@ void test_areal()
|
||||
case_122_multi[0], case_122_multi[1],
|
||||
1, 1, 28, 29.5);
|
||||
|
||||
TEST_UNION(case_123_multi, 1, 0, 11, 2.75);
|
||||
TEST_UNION(case_124_multi, 1, 0, 9, 2.75);
|
||||
|
||||
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_1",
|
||||
case_recursive_boxes_1[0], case_recursive_boxes_1[1],
|
||||
1, 1, 36, 97.0);
|
||||
|
||||
Reference in New Issue
Block a user