Merge branch 'feature/intvalid' into develop

This commit is contained in:
barendgehrels
2016-11-30 13:32:37 +01:00
19 changed files with 824 additions and 188 deletions

View File

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

View File

@@ -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)
};

View File

@@ -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);
}
}

View File

@@ -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())
;
}
};

View File

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

View File

@@ -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++)
{

View File

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

View File

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

View File

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

View File

@@ -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)))",

View File

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

View File

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

View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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