Merge branch 'feature/intvalid' into develop

# Conflicts:
#	include/boost/geometry/algorithms/intersects.hpp
#	include/boost/geometry/algorithms/touches.hpp
This commit is contained in:
Barend Gehrels 2017-06-27 20:33:46 +02:00
commit 60c9b096b1
32 changed files with 1731 additions and 508 deletions

View File

@ -81,7 +81,7 @@ inline bool has_self_intersections(Geometry const& geometry,
std::deque<turn_info> turns;
detail::disjoint::disjoint_interrupt_policy policy;
geometry::self_turns<detail::overlay::assign_null_policy>(geometry, strategy, robust_policy, turns, policy);
detail::self_get_turn_points::self_turns<false, detail::overlay::assign_null_policy>(geometry, strategy, robust_policy, turns, policy);
#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS
bool first = true;

View File

@ -74,7 +74,7 @@ struct self_intersects
detail::disjoint::disjoint_interrupt_policy policy;
detail::self_get_turn_points::get_turns
<
turn_policy
false, turn_policy
>::apply(geometry, strategy, robust_policy, turns, policy, 0);
return policy.has_intersections;
}

View File

@ -219,7 +219,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg
detail::self_get_turn_points::get_turns
<
turn_policy
false, turn_policy
>::apply(linear,
strategy,
detail::no_rescale_policy(),

View File

@ -86,7 +86,7 @@ public:
IsAcceptableTurn
> interrupt_policy;
geometry::self_turns<turn_policy>(geometry,
detail::self_get_turn_points::self_turns<false, turn_policy>(geometry,
strategy,
robust_policy,
turns,

View File

@ -125,6 +125,34 @@ private:
typedef typename boost::range_value<MultiPolygon>::type polygon;
typedef is_acceptable_turn<polygon> base;
template <typename Operation>
static inline
bool check_int_ext(Operation const& op1,
detail::overlay::operation_type optype1,
Operation const& op2,
detail::overlay::operation_type optype2)
{
// u/i is acceptable for touch of interior ring with another exterior ring
// (but only if there is a colocated uu-turn of its exterior, TODO)
return op1.seg_id.ring_index == -1
&& op2.seg_id.ring_index >= 0
&& op1.operation == optype1
&& op2.operation == optype2;
}
template <typename Operation>
static inline
bool check_int_int(Operation const& op1,
Operation const& op2,
detail::overlay::operation_type optype)
{
// i/i is acceptable for touching interior/interior rings
return op1.seg_id.ring_index >= 0
&& op2.seg_id.ring_index >= 0
&& op1.operation == optype
&& op2.operation == optype;
}
public:
template <typename Turn>
static inline bool apply(Turn const& turn)
@ -138,10 +166,32 @@ public:
}
operation_type const op = acceptable_operation<MultiPolygon>::value;
if ( base::check_turn(turn, method_touch_interior, op)
|| base::check_turn(turn, method_touch, op))
{
return true;
}
return base::check_turn(turn, method_touch_interior, op)
|| base::check_turn(turn, method_touch, op)
;
if (turn.method != method_touch)
{
return false;
}
operation_type const reverse_op
= op == operation_union
? operation_intersection
: operation_union;
if ( check_int_int(turn.operations[0], turn.operations[1], reverse_op)
|| check_int_ext(turn.operations[0], reverse_op,
turn.operations[1], op)
|| check_int_ext(turn.operations[1], reverse_op,
turn.operations[0], op))
{
return true;
}
return false;
}
};

View File

@ -27,6 +27,9 @@ struct ring_with_direction
std::size_t turn_index;
int operation_index;
operation_type operation;
signed_size_type region_id;
bool isolated;
inline bool operator<(ring_with_direction const& other) const
{
@ -39,6 +42,9 @@ struct ring_with_direction
: direction(dir_unknown)
, turn_index(-1)
, operation_index(0)
, operation(operation_none)
, region_id(-1)
, isolated(false)
{}
};
@ -75,6 +81,89 @@ struct rank_with_rings
return all_equal(sort_by_side::dir_from);
}
inline bool has_only(operation_type op) const
{
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
it != rings.end(); ++it)
{
const ring_with_direction& rwd = *it;
if (rwd.operation != op)
{
return false;
}
}
return true;
}
//! Check if set has both op1 and op2, but no others
inline bool has_only_both(operation_type op1, operation_type op2) const
{
bool has1 = false;
bool has2 = false;
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
it != rings.end(); ++it)
{
const ring_with_direction& rwd = *it;
if (rwd.operation == op1) { has1 = true; }
else if (rwd.operation == op2) { has2 = true; }
else { return false; }
}
return has1 && has2;
}
inline bool is_isolated() const
{
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
it != rings.end(); ++it)
{
const ring_with_direction& rwd = *it;
if (! rwd.isolated)
{
return false;
}
}
return true;
}
inline bool has_unique_region_id() const
{
int region_id = -1;
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
it != rings.end(); ++it)
{
const ring_with_direction& rwd = *it;
if (region_id == -1)
{
region_id = rwd.region_id;
}
else if (rwd.region_id != region_id)
{
return false;
}
}
return true;
}
inline int region_id() const
{
int region_id = -1;
for (std::set<ring_with_direction>::const_iterator it = rings.begin();
it != rings.end(); ++it)
{
const ring_with_direction& rwd = *it;
if (region_id == -1)
{
region_id = rwd.region_id;
}
else if (rwd.region_id != region_id)
{
return -1;
}
}
return region_id;
}
template <typename Turns>
inline bool traversable(Turns const& turns) const
{
@ -87,7 +176,13 @@ struct rank_with_rings
const ring_with_direction& rwd = *it;
turn_type const& turn = turns[rwd.turn_index];
turn_operation_type const& op = turn.operations[rwd.operation_index];
if (op.visited.finalized())
// TODO: this is still necessary, but makes it order-dependent
// which should not be done.
// This would obsolete the whole function and should be solved
// in a different way
if (op.visited.finalized() || op.visited.visited())
{
return false;
}
@ -99,7 +194,8 @@ struct rank_with_rings
template <typename Sbs, typename Turns>
inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& aggregation,
Turns const& turns)
Turns const& turns,
operation_type target_operation)
{
typedef typename boost::range_value<Turns>::type turn_type;
typedef typename turn_type::turn_operation_type turn_operation_type;
@ -113,9 +209,14 @@ inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& a
turn_operation_type const& op = turn.operations[ranked_point.operation_index];
if (! (op.operation == operation_intersection || op.operation == operation_continue))
if (! ((target_operation == operation_union && ranked_point.rank == 0)
|| op.operation == target_operation
|| op.operation == operation_continue
|| (op.operation == operation_blocked && ranked_point.direction == dir_from)))
{
// Always take rank 0 (because self-turns are blocked)
// Don't consider union/blocked (aggregate is only used for intersections)
// Blocked is allowed for from
continue;
}
@ -133,6 +234,9 @@ inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& a
rwd.direction = ranked_point.direction;
rwd.turn_index = ranked_point.turn_index;
rwd.operation_index = ranked_point.operation_index;
rwd.operation = op.operation;
rwd.region_id = op.enriched.region_id;
rwd.isolated = op.enriched.isolated;
aggregation.back().rings.insert(rwd);
}

View File

@ -26,15 +26,14 @@
#include <boost/range.hpp>
#include <boost/geometry/iterators/ever_circling_iterator.hpp>
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
#include <boost/geometry/algorithms/detail/overlay/handle_colocations.hpp>
#include <boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
#include <boost/geometry/policies/robustness/robust_type.hpp>
#include <boost/geometry/strategies/side.hpp>
#ifdef BOOST_GEOMETRY_DEBUG_ENRICH
# include <boost/geometry/algorithms/detail/overlay/check_enrich.hpp>
#endif
@ -173,9 +172,7 @@ inline void enrich_assign(Operations& operations, Turns& turns)
template <typename Turns, typename MappedVector>
inline void create_map(Turns const& turns,
detail::overlay::operation_type for_operation,
MappedVector& mapped_vector)
inline void create_map(Turns const& turns, MappedVector& mapped_vector)
{
typedef typename boost::range_value<Turns>::type turn_type;
typedef typename turn_type::container_type container_type;
@ -268,6 +265,7 @@ inline void calculate_remaining_distance(Turns& turns)
}
}
}} // namespace detail::overlay
#endif //DOXYGEN_NO_DETAIL
@ -305,8 +303,13 @@ inline void enrich_intersection_points(Turns& turns,
RobustPolicy const& robust_policy,
Strategy const& strategy)
{
static const detail::overlay::operation_type for_operation
static const detail::overlay::operation_type target_operation
= detail::overlay::operation_from_overlay<OverlayType>::value;
static const detail::overlay::operation_type opposite_operation
= target_operation == detail::overlay::operation_union
? detail::overlay::operation_intersection
: detail::overlay::operation_union;
typedef typename boost::range_value<Turns>::type turn_type;
typedef typename turn_type::turn_operation_type op_type;
typedef detail::overlay::indexed_turn_operation
@ -332,34 +335,31 @@ inline void enrich_intersection_points(Turns& turns,
++it)
{
turn_type& turn = *it;
if (turn.both(detail::overlay::operation_none))
{
turn.discarded = true;
continue;
}
if (for_operation == detail::overlay::operation_intersection
&& turn.both(detail::overlay::operation_union))
if (turn.both(opposite_operation))
{
// For intersections, remove uu to avoid the need to travel
// a union (during intersection) in uu/cc clusters (e.g. #31,#32,#33)
turn.discarded = true;
turn.cluster_id = -1;
}
if (for_operation == detail::overlay::operation_union
&& turn.both(detail::overlay::operation_intersection))
{
// Also, for union, discard ii
turn.discarded = true;
turn.cluster_id = -1;
continue;
}
if (OverlayType != overlay_buffer
&& turn.cluster_id >= 0
&& turn.self_turn())
if (detail::overlay::is_self_turn<OverlayType>(turn)
&& turn.cluster_id < 0
&& ! turn.both(target_operation))
{
// Avoid interfering self-turn if there are already clustered turns
// TODO: avoid discarding if there are ONLY self-turns
// Only keep self-uu-turns or self-ii-turns
turn.discarded = true;
turn.cluster_id = -1;
continue;
}
if (! turn.discarded
@ -369,11 +369,22 @@ inline void enrich_intersection_points(Turns& turns,
}
}
detail::overlay::discard_closed_turns
<
OverlayType,
target_operation
>::apply(turns, geometry1, geometry2);
detail::overlay::discard_open_turns
<
OverlayType,
target_operation
>::apply(turns, geometry1, geometry2);
// Create a map of vectors of indexed operation-types to be able
// to sort intersection points PER RING
mapped_vector_type mapped_vector;
detail::overlay::create_map(turns, for_operation, mapped_vector);
detail::overlay::create_map(turns, mapped_vector);
// No const-iterator; contents of mapped copy is temporary,
// and changed by enrich
@ -387,7 +398,7 @@ inline void enrich_intersection_points(Turns& turns,
<< mit->first << std::endl;
#endif
detail::overlay::enrich_sort<Reverse1, Reverse2>(
mit->second, turns, for_operation,
mit->second, turns, target_operation,
geometry1, geometry2,
robust_policy, strategy);
}
@ -406,12 +417,16 @@ inline void enrich_intersection_points(Turns& turns,
if (has_colocations)
{
// First gather cluster properties (using even clusters with
// discarded turns - for open turns), then clean up clusters
detail::overlay::gather_cluster_properties
<
Reverse1,
Reverse2,
OverlayType
>(clusters, turns, for_operation, geometry1, geometry2);
>(clusters, turns, target_operation, geometry1, geometry2);
detail::overlay::cleanup_clusters(turns, clusters);
}
if (has_cc)

View File

@ -262,15 +262,6 @@ inline void handle_colocation_cluster(Turns& turns,
add_cluster_id(other_op, cluster_per_segment, ref_id);
id = ref_id;
}
// In case of colocated xx turns, all other turns may NOT be
// followed at all. xx cannot be discarded (otherwise colocated
// turns are followed).
if (ref_turn.both(operation_blocked))
{
turn.discarded = true;
// We can either set or not set colocated because it is not effective on blocked turns
}
}
else
{
@ -331,11 +322,7 @@ inline void assign_cluster_to_turns(Turns& turns,
}
}
template
<
typename Turns,
typename Clusters
>
template <typename Turns, typename Clusters>
inline void remove_clusters(Turns& turns, Clusters& clusters)
{
typename Clusters::iterator it = clusters.begin();
@ -350,13 +337,39 @@ inline void remove_clusters(Turns& turns, Clusters& clusters)
= current_it->second.turn_indices;
if (turn_indices.size() == 1)
{
signed_size_type turn_index = *turn_indices.begin();
signed_size_type const turn_index = *turn_indices.begin();
turns[turn_index].cluster_id = -1;
clusters.erase(current_it);
}
}
}
template <typename Turns, typename Clusters>
inline void cleanup_clusters(Turns& turns, Clusters& clusters)
{
// Removes discarded turns from clusters
for (typename Clusters::iterator mit = clusters.begin();
mit != clusters.end(); ++mit)
{
cluster_info& cinfo = mit->second;
std::set<signed_size_type>& ids = cinfo.turn_indices;
for (std::set<signed_size_type>::iterator sit = ids.begin();
sit != ids.end(); /* no increment */)
{
std::set<signed_size_type>::iterator current_it = sit;
++sit;
signed_size_type const turn_index = *current_it;
if (turns[turn_index].discarded)
{
ids.erase(current_it);
}
}
}
remove_clusters(turns, clusters);
}
template <typename Turn, typename IdSet>
inline void discard_ie_turn(Turn& turn, IdSet& ids, signed_size_type id)
{
@ -508,7 +521,7 @@ inline void set_colocation(Turns& turns, Clusters const& clusters)
{
has_ii = true;
}
if (turn.both(operation_union))
if (turn.both(operation_union) || turn.combination(operation_union, operation_blocked))
{
has_uu = true;
}
@ -629,7 +642,6 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters,
do_reverse<geometry::point_order<Geometry2>::value>::value != Reverse2,
OverlayType
>(turns, clusters);
remove_clusters(turns, clusters);
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
std::cout << "*** Colocations " << map.size() << std::endl;
@ -716,7 +728,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
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();
for (std::set<signed_size_type>::const_iterator sit = ids.begin();
sit != ids.end(); ++sit)
{
signed_size_type turn_index = *sit;
@ -737,6 +749,8 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
sbs.find_open();
sbs.assign_zones(for_operation);
cinfo.open_count = sbs.open_count(for_operation);
// Unset the startable flag for all 'closed' zones
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
{
@ -744,6 +758,11 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
turn_type& turn = turns[ranked.turn_index];
turn_operation_type& op = turn.operations[ranked.operation_index];
if (for_operation == operation_union && cinfo.open_count == 0)
{
op.enriched.startable = false;
}
if (ranked.direction != sort_by_side::dir_to)
{
continue;
@ -763,7 +782,6 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns,
}
}
cinfo.open_count = sbs.open_count(for_operation);
}
}

View File

@ -0,0 +1,143 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
#include <boost/range.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
#include <boost/geometry/algorithms/within.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{
struct discard_turns
{
template <typename Turns, typename Geometry0, typename Geometry1>
static inline
void apply(Turns& , Geometry0 const& , Geometry1 const& )
{}
};
template <overlay_type OverlayType, operation_type OperationType>
struct discard_closed_turns : discard_turns {};
// It is only implemented for operation_union, not in buffer
template <>
struct discard_closed_turns<overlay_union, operation_union>
{
template <typename Turns, typename Geometry0, typename Geometry1>
static inline
void apply(Turns& turns,
Geometry0 const& geometry0, Geometry1 const& geometry1)
{
typedef typename boost::range_value<Turns>::type turn_type;
for (typename boost::range_iterator<Turns>::type
it = boost::begin(turns);
it != boost::end(turns);
++it)
{
turn_type& turn = *it;
if (turn.cluster_id >= 0
|| turn.discarded
|| ! is_self_turn<overlay_union>(turn))
{
continue;
}
bool const within =
turn.operations[0].seg_id.source_index == 0
? geometry::within(turn.point, geometry1)
: geometry::within(turn.point, geometry0);
if (within)
{
// It is in the interior of the other geometry
turn.discarded = true;
}
}
}
};
struct discard_self_intersection_turns
{
template <typename Turns, typename Geometry0, typename Geometry1>
static inline
void apply(Turns& turns,
Geometry0 const& geometry0, Geometry1 const& geometry1)
{
typedef typename boost::range_value<Turns>::type turn_type;
for (typename boost::range_iterator<Turns>::type
it = boost::begin(turns);
it != boost::end(turns);
++it)
{
turn_type& turn = *it;
if (turn.cluster_id >= 0
|| turn.discarded
|| ! is_self_turn<overlay_intersection>(turn))
{
continue;
}
segment_identifier const& id0 = turn.operations[0].seg_id;
segment_identifier const& id1 = turn.operations[1].seg_id;
if (id0.multi_index != id1.multi_index
|| (id0.ring_index == -1 && id1.ring_index == -1)
|| (id0.ring_index >= 0 && id1.ring_index >= 0))
{
// Not an ii ring (int/ext) on same ring
continue;
}
// It is a non co-located ii self-turn
// Check if it is within the other geometry
// If not, it can be ignored
bool const within =
turn.operations[0].seg_id.source_index == 0
? geometry::within(turn.point, geometry1)
: geometry::within(turn.point, geometry0);
if (! within)
{
// It is not within another geometry, discard the turn
turn.discarded = true;
}
}
}
};
template <overlay_type OverlayType, operation_type OperationType>
struct discard_open_turns : discard_turns {};
// Handler it for intersection
template <>
struct discard_open_turns<overlay_intersection, operation_intersection>
: discard_self_intersection_turns {};
// For difference, it should be done in a different way (TODO)
}} // namespace detail::overlay
#endif //DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP

View File

@ -0,0 +1,68 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2017-2017 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{
template <overlay_type OverlayType>
struct is_self_turn_check
{
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return turn.operations[0].seg_id.source_index
== turn.operations[1].seg_id.source_index;
}
};
template <>
struct is_self_turn_check<overlay_buffer>
{
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return false;
}
};
template <>
struct is_self_turn_check<overlay_dissolve>
{
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return false;
}
};
template <overlay_type OverlayType, typename Turn>
bool is_self_turn(Turn const& turn)
{
return is_self_turn_check<OverlayType>::apply(turn);
}
}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_IS_SELF_TURN_HPP

View File

@ -28,6 +28,7 @@
#include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp>
#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
#include <boost/geometry/algorithms/detail/overlay/traverse.hpp>
#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
@ -117,6 +118,7 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C
? turn.colocated_uu : turn.colocated_ii;
bool const colocated_opp = target_operation == operation_union
? turn.colocated_ii : turn.colocated_uu;
bool const both_opposite = turn.both(opposite_operation);
bool const traversed
= turn.operations[0].visited.finalized()
@ -126,6 +128,16 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C
|| turn.both(operation_blocked)
|| turn.combination(opposite_operation, operation_blocked);
bool is_closed = false;
if (turn.cluster_id >= 0 && target_operation == operation_union)
{
typename Clusters::const_iterator mit = clusters.find(turn.cluster_id);
BOOST_ASSERT(mit != clusters.end());
cluster_info const& cinfo = mit->second;
is_closed = cinfo.open_count == 0;
}
for (typename boost::range_iterator<container_type const>::type
op_it = boost::begin(turn.operations);
op_it != boost::end(turn.operations);
@ -138,54 +150,32 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C
op_it->seg_id.ring_index
);
if (traversed)
if (traversed || is_closed || ! op_it->enriched.startable)
{
turn_info_map[ring_id].has_traversed_turn = true;
continue;
}
if (turn.both(opposite_operation) && colocated_target)
else if (both_opposite && colocated_target)
{
// For union: ii, colocated with a uu
// For example, two interior rings touch where two exterior rings also touch.
// The interior rings are not yet traversed, and should be taken from the input
// For intersection: uu, colocated with an ii
continue;
}
// unless it is two interior inner rings colocated with a uu
// unless it is two interior inner rings colocated with a uu
if (turn.both(opposite_operation))
// So don't set has_traversed_turn here
}
else if (both_opposite && ! is_self_turn<OverlayType>(turn))
{
// For union, mark any ring with a ii turn as traversed
// For intersection, any uu
// For intersection, any uu - but not if it is a self-turn
turn_info_map[ring_id].has_traversed_turn = true;
continue;
}
else if (colocated_opp && ! colocated_target)
{
// For union, a turn colocated with ii and NOT with uu
// For union, a turn colocated with ii and NOT with uu/ux
// For intersection v.v.
turn_info_map[ring_id].has_traversed_turn = true;
continue;
}
if (turn.cluster_id >= 0)
{
// Check other turns in same cluster
typename Clusters::const_iterator mit = clusters.find(turn.cluster_id);
BOOST_ASSERT(mit != clusters.end());
cluster_info const& cinfo = mit->second;
if (target_operation == operation_union && cinfo.open_count == 0)
{
// It is not traversed, and there is no way out for a union,
// so register it as traversed to avoid including the ring
turn_info_map[ring_id].has_traversed_turn = true;
continue;
}
}
}
}
@ -308,10 +298,10 @@ std::cout << "get turns" << std::endl;
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
{
geometry::self_turns<assign_null_policy>(geometry1, strategy, robust_policy,
turns, policy, 0);
geometry::self_turns<assign_null_policy>(geometry2, strategy, robust_policy,
turns, policy, 1);
self_get_turn_points::self_turns<Reverse1, assign_null_policy>(geometry1,
strategy, robust_policy, turns, policy, 0);
self_get_turn_points::self_turns<Reverse2, assign_null_policy>(geometry2,
strategy, robust_policy, turns, policy, 1);
}
#endif

View File

@ -23,12 +23,14 @@
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
#include <boost/geometry/algorithms/detail/partition.hpp>
#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp>
@ -60,6 +62,7 @@ struct no_interrupt_policy
template
<
bool Reverse,
typename Geometry,
typename Turns,
typename TurnPolicy,
@ -101,7 +104,7 @@ struct self_section_visitor
return detail::get_turns::get_turns_in_sections
<
Geometry, Geometry,
false, false,
Reverse, Reverse,
Section, Section,
TurnPolicy
>::apply(m_source_index, m_geometry, sec1,
@ -119,7 +122,7 @@ struct self_section_visitor
template<typename TurnPolicy>
template <bool Reverse, typename TurnPolicy>
struct get_turns
{
template <typename Geometry, typename IntersectionStrategy, typename RobustPolicy, typename Turns, typename InterruptPolicy>
@ -145,12 +148,12 @@ struct get_turns
typedef boost::mpl::vector_c<std::size_t, 0> dimensions;
sections_type sec;
geometry::sectionalize<false, dimensions>(geometry, robust_policy, sec,
geometry::sectionalize<Reverse, dimensions>(geometry, robust_policy, sec,
intersection_strategy.get_envelope_strategy());
self_section_visitor
<
Geometry,
Reverse, Geometry,
Turns, TurnPolicy, IntersectionStrategy, RobustPolicy, InterruptPolicy
> visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index);
@ -177,6 +180,7 @@ namespace dispatch
template
<
bool Reverse,
typename GeometryTag,
typename Geometry,
typename TurnPolicy
@ -188,26 +192,28 @@ struct self_get_turn_points
template
<
bool Reverse,
typename Ring,
typename TurnPolicy
>
struct self_get_turn_points
<
ring_tag, Ring,
Reverse, ring_tag, Ring,
TurnPolicy
>
: detail::self_get_turn_points::get_turns<TurnPolicy>
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
{};
template
<
bool Reverse,
typename Box,
typename TurnPolicy
>
struct self_get_turn_points
<
box_tag, Box,
Reverse, box_tag, Box,
TurnPolicy
>
{
@ -227,29 +233,31 @@ struct self_get_turn_points
template
<
bool Reverse,
typename Polygon,
typename TurnPolicy
>
struct self_get_turn_points
<
polygon_tag, Polygon,
Reverse, polygon_tag, Polygon,
TurnPolicy
>
: detail::self_get_turn_points::get_turns<TurnPolicy>
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
{};
template
<
bool Reverse,
typename MultiPolygon,
typename TurnPolicy
>
struct self_get_turn_points
<
multi_polygon_tag, MultiPolygon,
Reverse, multi_polygon_tag, MultiPolygon,
TurnPolicy
>
: detail::self_get_turn_points::get_turns<TurnPolicy>
: detail::self_get_turn_points::get_turns<Reverse, TurnPolicy>
{};
@ -257,6 +265,45 @@ struct self_get_turn_points
#endif // DOXYGEN_NO_DISPATCH
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace self_get_turn_points
{
// Version where Reverse can be specified manually. TODO:
// can most probably be merged with self_get_turn_points::get_turn
template
<
bool Reverse,
typename AssignPolicy,
typename Geometry,
typename IntersectionStrategy,
typename RobustPolicy,
typename Turns,
typename InterruptPolicy
>
inline void self_turns(Geometry const& geometry,
IntersectionStrategy const& strategy,
RobustPolicy const& robust_policy,
Turns& turns,
InterruptPolicy& interrupt_policy,
std::size_t source_index = 0)
{
concepts::check<Geometry const>();
typedef detail::overlay::get_turn_info<detail::overlay::assign_null_policy> turn_policy;
dispatch::self_get_turn_points
<
Reverse,
typename tag<Geometry>::type,
Geometry,
turn_policy
>::apply(geometry, strategy, robust_policy, turns, interrupt_policy, source_index);
}
}} // namespace detail::self_get_turn_points
#endif // DOXYGEN_NO_DETAIL
/*!
\brief Calculate self intersections of a geometry
\ingroup overlay
@ -288,14 +335,16 @@ inline void self_turns(Geometry const& geometry,
{
concepts::check<Geometry const>();
typedef detail::overlay::get_turn_info<detail::overlay::assign_null_policy> turn_policy;
static bool const reverse = detail::overlay::do_reverse
<
geometry::point_order<Geometry>::value
>::value;
dispatch::self_get_turn_points
detail::self_get_turn_points::self_turns
<
typename tag<Geometry>::type,
Geometry,
turn_policy
>::apply(geometry, strategy, robust_policy, turns, interrupt_policy, source_index);
reverse,
AssignPolicy
>(geometry, strategy, robust_policy, turns, interrupt_policy, source_index);
}

View File

@ -14,7 +14,9 @@
#include <boost/range.hpp>
#include <boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
#include <boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/assert.hpp>
@ -195,8 +197,8 @@ struct traversal
}
inline bool select_source(signed_size_type turn_index,
segment_identifier const& seg_id1,
segment_identifier const& seg_id2) const
segment_identifier const& candidate_seg_id,
segment_identifier const& previous_seg_id) const
{
// For uu/ii, only switch sources if indicated
turn_type const& turn = m_turns[turn_index];
@ -205,14 +207,16 @@ struct traversal
{
// Buffer does not use source_index (always 0)
return turn.switch_source
? seg_id1.multi_index != seg_id2.multi_index
: seg_id1.multi_index == seg_id2.multi_index;
? candidate_seg_id.multi_index != previous_seg_id.multi_index
: candidate_seg_id.multi_index == previous_seg_id.multi_index;
}
// If it is a self-turn, always switch source
if (turn.self_turn())
if (is_self_turn<OverlayType>(turn))
{
return true;
// Also, if it is a self-turn, stay on same ring (multi/ring)
return turn.switch_source
? candidate_seg_id.multi_index != previous_seg_id.multi_index
: candidate_seg_id.multi_index == previous_seg_id.multi_index;
}
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
@ -226,8 +230,8 @@ struct traversal
}
#endif
return turn.switch_source
? seg_id1.source_index != seg_id2.source_index
: seg_id1.source_index == seg_id2.source_index;
? candidate_seg_id.source_index != previous_seg_id.source_index
: candidate_seg_id.source_index == previous_seg_id.source_index;
}
inline bool traverse_possible(signed_size_type turn_index) const
@ -295,7 +299,7 @@ struct traversal
inline
bool select_noncc_operation(turn_type const& turn,
signed_size_type turn_index,
segment_identifier const& seg_id,
segment_identifier const& previous_seg_id,
int& selected_op_index) const
{
bool result = false;
@ -306,7 +310,7 @@ struct traversal
if (op.operation == target_operation
&& ! op.visited.finished()
&& (! result || select_source(turn_index, op.seg_id, seg_id)))
&& (! result || select_source(turn_index, op.seg_id, previous_seg_id)))
{
selected_op_index = i;
debug_traverse(turn, op, "Candidate");
@ -369,65 +373,56 @@ struct traversal
}
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
int& op_index, sbs_type& sbs) const
{
std::vector<sort_by_side::rank_with_rings> aggregation;
sort_by_side::aggregate_operations(sbs, aggregation, m_turns, operation_union);
sort_by_side::rank_with_rings const& incoming = aggregation.front();
// Take the first one outgoing for the incoming region
std::size_t selected_rank = 0;
std::size_t min_rank = 0;
bool result = false;
for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
for (std::size_t i = 1; i < aggregation.size(); i++)
{
sort_by_side::rank_with_rings const& rwr = aggregation[i];
if (rwr.all_to()
&& rwr.region_id() == incoming.region_id())
{
selected_rank = rwr.rank;
break;
}
}
for (std::size_t i = 1; i < sbs.m_ranked_points.size(); i++)
{
typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i];
if (result && ranked_point.rank > selected_rank)
if (ranked_point.rank == selected_rank
&& ranked_point.direction == sort_by_side::dir_to)
{
return result;
}
turn_index = ranked_point.turn_index;
op_index = ranked_point.operation_index;
turn_type const& ranked_turn = m_turns[ranked_point.turn_index];
turn_operation_type const& ranked_op = ranked_turn.operations[ranked_point.operation_index];
turn_type const& turn = m_turns[turn_index];
turn_operation_type const& op = turn.operations[op_index];
if (result && ranked_op.visited.finalized())
{
// One of the arcs in the same direction as the selected result
// is already traversed.
return false;
}
if (! is_touching && ranked_op.visited.finalized())
{
// Skip this one, go to next
min_rank = ranked_point.rank;
continue;
}
if (ranked_point.direction == sort_by_side::dir_to
&& (ranked_point.rank > min_rank
|| ranked_turn.both(operation_continue)))
{
if (ranked_op.enriched.count_left == 0
&& ranked_op.enriched.count_right > 0)
if (op.enriched.count_left == 0
&& op.enriched.count_right > 0
&& ! op.visited.finalized())
{
if (result && ranked_point.turn_index != start_turn_index)
{
// Don't override - only override if arrive at start
continue;
}
// In some cases interior rings might be generated with polygons
// on both sides
turn_index = ranked_point.turn_index;
op_index = ranked_point.operation_index;
result = true;
selected_rank = ranked_point.rank;
}
else if (! is_touching)
{
return result;
// TODO: this should be finetuned such that checking
// finalized is not necessary
return true;
}
}
}
return result;
return false;
}
inline bool all_operations_of_type(sort_by_side::rank_with_rings const& rwr,
operation_type op_type,
sort_by_side::direction_type dir) const
@ -461,65 +456,78 @@ struct traversal
int& op_index, sbs_type const& sbs) const
{
std::vector<sort_by_side::rank_with_rings> aggregation;
sort_by_side::aggregate_operations(sbs, aggregation, m_turns);
sort_by_side::aggregate_operations(sbs, aggregation, m_turns, operation_intersection);
std::size_t selected_rank = 0;
int incoming_region_id = 0;
std::set<int> outgoing_region_ids;
for (std::size_t i = 0; i < aggregation.size(); i++)
// Detect specific pattern(s)
bool const detected
= intersection_pattern_common_interior1(selected_rank, aggregation)
|| intersection_pattern_common_interior2(selected_rank, aggregation)
|| intersection_pattern_common_interior3(selected_rank, aggregation)
|| intersection_pattern_common_interior4(selected_rank, aggregation)
;
if (! detected)
{
sort_by_side::rank_with_rings const& rwr = aggregation[i];
int incoming_region_id = 0;
std::set<int> outgoing_region_ids;
if (rwr.all_to()
&& rwr.traversable(m_turns)
&& selected_rank == 0)
for (std::size_t i = 0; i < aggregation.size(); i++)
{
// Take the first (= right) where segments leave,
// having the polygon on the right side
selected_rank = rwr.rank;
}
if (rwr.all_from()
&& selected_rank > 0
&& outgoing_region_ids.empty())
{
// Incoming
break;
}
sort_by_side::rank_with_rings const& rwr = aggregation[i];
if (incoming_region_id == 0)
{
sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin();
turn_type const& turn = m_turns[rwd.turn_index];
incoming_region_id = turn.operations[rwd.operation_index].enriched.region_id;
}
else
{
if (rwr.rings.size() == 1)
if (rwr.all_to()
&& rwr.traversable(m_turns)
&& selected_rank == 0)
{
// Take the first (= right) where segments leave,
// having the polygon on the right side
selected_rank = rwr.rank;
}
if (rwr.all_from()
&& selected_rank > 0
&& outgoing_region_ids.empty())
{
// Incoming
break;
}
if (incoming_region_id == 0)
{
sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin();
turn_type const& turn = m_turns[rwd.turn_index];
if (rwd.direction == sort_by_side::dir_to
&& turn.both(operation_intersection))
incoming_region_id = turn.operations[rwd.operation_index].enriched.region_id;
}
else
{
if (rwr.rings.size() == 1)
{
sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin();
turn_type const& turn = m_turns[rwd.turn_index];
if (rwd.direction == sort_by_side::dir_to
&& turn.both(operation_intersection))
{
turn_operation_type const& op = turn.operations[rwd.operation_index];
if (op.enriched.region_id != incoming_region_id
&& op.enriched.isolated)
{
outgoing_region_ids.insert(op.enriched.region_id);
}
}
else if (! outgoing_region_ids.empty())
{
for (int i = 0; i < 2; i++)
{
int const region_id = turn.operations[i].enriched.region_id;
if (outgoing_region_ids.count(region_id) == 1)
turn_operation_type const& op = turn.operations[rwd.operation_index];
if (op.enriched.region_id != incoming_region_id
&& op.enriched.isolated)
{
selected_rank = 0;
outgoing_region_ids.erase(region_id);
outgoing_region_ids.insert(op.enriched.region_id);
}
}
else if (! outgoing_region_ids.empty())
{
for (int i = 0; i < 2; i++)
{
int const region_id = turn.operations[i].enriched.region_id;
if (outgoing_region_ids.count(region_id) == 1)
{
selected_rank = 0;
outgoing_region_ids.erase(region_id);
}
}
}
}
@ -564,7 +572,7 @@ struct traversal
}
inline bool select_turn_from_cluster(signed_size_type& turn_index,
int& op_index, bool& is_touching,
int& op_index,
signed_size_type start_turn_index,
segment_identifier const& previous_seg_id) const
{
@ -612,31 +620,7 @@ struct traversal
if (is_union)
{
#if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
is_touching = cinfo.open_count > 1;
if (is_touching)
{
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
is_touching = cinfo.open_count > 1 && ! cinfo.switch_source;
#endif
if (is_touching)
{
sbs.reverse();
}
result = select_from_cluster_union(turn_index, op_index, start_turn_index, sbs,
is_touching);
result = select_from_cluster_union(turn_index, op_index, sbs);
}
else
{
@ -768,7 +752,6 @@ struct traversal
bool select_turn(signed_size_type start_turn_index, int start_op_index,
signed_size_type& turn_index,
int& op_index,
bool& is_touching,
int previous_op_index,
signed_size_type previous_turn_index,
segment_identifier const& previous_seg_id,
@ -803,7 +786,7 @@ struct traversal
if (current_turn.cluster_id >= 0)
{
if (! select_turn_from_cluster(turn_index, op_index, is_touching,
if (! select_turn_from_cluster(turn_index, op_index,
start_turn_index, previous_seg_id))
{
return false;

View File

@ -0,0 +1,306 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2017 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INTERSECTION_PATTERNS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INTERSECTION_PATTERNS_HPP
#include <cstddef>
#include <vector>
#include <boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp>
#include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{
inline bool check_pairs(std::vector<sort_by_side::rank_with_rings> const& aggregation,
signed_size_type incoming_region_id,
std::size_t first, std::size_t last)
{
// Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy
for (std::size_t i = first; i <= last; i += 2)
{
sort_by_side::rank_with_rings const& curr = aggregation[i];
sort_by_side::rank_with_rings const& next = aggregation[i + 1];
int const curr_id = curr.region_id();
int const next_id = next.region_id();
bool const possible =
curr.rings.size() == 2
&& curr.is_isolated()
&& curr.has_unique_region_id()
&& next.rings.size() == 2
&& next.is_isolated()
&& next.has_unique_region_id()
&& curr_id == next_id
&& curr_id != incoming_region_id;
if (! possible)
{
return false;
}
}
return true;
}
inline bool intersection_pattern_common_interior1(std::size_t& selected_rank,
std::vector<sort_by_side::rank_with_rings> const& aggregation)
{
// Pattern: coming from exterior ring, encountering an isolated
// parallel interior ring, which should be skipped, and the first
// left (normally intersection takes first right) should be taken.
// Solves cases #case_133_multi
// and #case_recursive_boxes_49
std::size_t const n = aggregation.size();
if (n < 4)
{
return false;
}
sort_by_side::rank_with_rings const& incoming = aggregation.front();
sort_by_side::rank_with_rings const& outgoing = aggregation.back();
bool const incoming_ok =
incoming.all_from()
&& incoming.rings.size() == 1
&& incoming.has_only(operation_intersection);
if (! incoming_ok)
{
return false;
}
bool const outgoing_ok =
outgoing.all_to()
&& outgoing.rings.size() == 1
&& outgoing.has_only(operation_intersection)
&& outgoing.region_id() == incoming.region_id();
if (! outgoing_ok)
{
return false;
}
if (check_pairs(aggregation, incoming.region_id(), 1, n - 2))
{
selected_rank = n - 1;
return true;
}
return false;
}
inline bool intersection_pattern_common_interior2(std::size_t& selected_rank,
std::vector<sort_by_side::rank_with_rings> const& aggregation)
{
// Pattern: coming from two exterior rings, encountering two isolated
// equal interior rings
// See (for example, for ii) #case_recursive_boxes_53:
// INCOMING:
// Rank 0 {11[0] (s:0, m:0) i F rgn: 1 ISO} {13[1] (s:1, m:0) i F rgn: 1 ISO}
// PAIR:
// Rank 1 {13[0] (s:0, r:1, m:0) i T rgn: 3 ISO ->16} {11[1] (s:1, r:5, m:0) i T rgn: 3 ISO ->16}
// Rank 2 {13[0] (s:0, r:1, m:0) i F rgn: 3 ISO} {11[1] (s:1, r:5, m:0) i F rgn: 3 ISO}
// LEAVING (in the same direction, take last one)
// Rank 3 {11[0] (s:0, m:0) i T rgn: 1 ISO ->10} {13[1] (s:1, m:0) i T rgn: 1 ISO ->10}
std::size_t const n = aggregation.size();
if (n < 4)
{
return false;
}
sort_by_side::rank_with_rings const& incoming = aggregation.front();
sort_by_side::rank_with_rings const& outgoing = aggregation.back();
bool const incoming_ok =
incoming.all_from()
&& incoming.rings.size() == 2
&& incoming.has_unique_region_id();
if (! incoming_ok)
{
return false;
}
bool const outgoing_ok =
outgoing.all_to()
&& outgoing.rings.size() == 2
&& outgoing.has_unique_region_id()
&& outgoing.region_id() == incoming.region_id();
if (! outgoing_ok)
{
return false;
}
bool const operation_ok =
(incoming.has_only(operation_continue) && outgoing.has_only(operation_continue))
|| (incoming.has_only(operation_intersection) && outgoing.has_only(operation_intersection));
if (! operation_ok)
{
return false;
}
// Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy
if (check_pairs(aggregation, incoming.region_id(), 1, n - 2))
{
selected_rank = n - 1;
return true;
}
return false;
}
inline bool intersection_pattern_common_interior3(std::size_t& selected_rank,
std::vector<sort_by_side::rank_with_rings> const& aggregation)
{
// Pattern: approaches colocated turn (exterior+interior) from two
// different directions, and both leaves in the same direction
// See #case_136_multi:
// INCOMING:
//Rank 0 {10[0] (s:0, m:0) c F rgn: 1 ISO}
// PAIR:
//Rank 1 {14[0] (s:0, r:0, m:0) i T rgn: 2 ISO ->16} {11[1] (s:1, r:1, m:0) i T rgn: 2 ISO ->16}
//Rank 2 {14[0] (s:0, r:0, m:0) i F rgn: 2 ISO} {11[1] (s:1, r:1, m:0) i F rgn: 2 ISO}
// LEAVING (select this one):
//Rank 3 {10[0] (s:0, m:0) c T rgn: 1 ISO ->12} {10[1] (s:1, m:0) c T rgn: 1 ISO ->12}
// ADDITIONALLY: (other polygon coming in)
//Rank 4 {10[1] (s:1, m:0) c F rgn: 1 ISO}
std::size_t const n = aggregation.size();
if (n < 4)
{
return false;
}
sort_by_side::rank_with_rings const& incoming = aggregation.front();
sort_by_side::rank_with_rings const& outgoing = aggregation[n - 2];
sort_by_side::rank_with_rings const& last = aggregation.back();
bool const incoming_ok =
incoming.all_from()
&& incoming.rings.size() == 1
&& incoming.has_only(operation_continue);
if (! incoming_ok)
{
return false;
}
bool const outgoing_ok =
outgoing.all_to()
&& outgoing.rings.size() == 2
&& outgoing.has_only(operation_continue)
&& outgoing.has_unique_region_id()
&& outgoing.region_id() == incoming.region_id()
&& last.all_from()
&& last.rings.size() == 1
&& last.region_id() == incoming.region_id()
&& last.all_from();
if (! outgoing_ok)
{
return false;
}
// Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy
if (check_pairs(aggregation, incoming.region_id(), 1, n - 3))
{
selected_rank = n - 2;
return true;
}
return false;
}
inline bool intersection_pattern_common_interior4(std::size_t& selected_rank,
std::vector<sort_by_side::rank_with_rings> const& aggregation)
{
// Pattern: approaches colocated turn (exterior+interior) from same
// direction, but leaves in two different directions
// See #case_137_multi:
// INCOMING:
//Rank 0 {11[0] (s:0, m:0) i F rgn: 1 ISO} {10[1] (s:1, m:0) i F rgn: 1 ISO}
// PAIR:
//Rank 1 {13[0] (s:0, r:0, m:0) i T rgn: 2 ISO ->15} {11[1] (s:1, r:1, m:0) i T rgn: 2 ISO ->15}
//Rank 2 {13[0] (s:0, r:0, m:0) i F rgn: 2 ISO} {11[1] (s:1, r:1, m:0) i F rgn: 2 ISO}
// LEAVING (in two different directions, take last one)
//Rank 3 {10[1] (s:1, m:0) i T rgn: 1 ISO ->0}
//Rank 4 {11[0] (s:0, m:0) i T rgn: 1 ISO ->12}
std::size_t const n = aggregation.size();
if (n < 4)
{
return false;
}
sort_by_side::rank_with_rings const& incoming = aggregation.front();
sort_by_side::rank_with_rings const& extra = aggregation[n - 2];
sort_by_side::rank_with_rings const& outgoing = aggregation.back();
bool const incoming_ok =
incoming.all_from()
&& incoming.rings.size() == 2
&& incoming.has_unique_region_id()
&& incoming.has_only(operation_intersection);
if (! incoming_ok)
{
return false;
}
bool const outgoing_ok =
outgoing.all_to()
&& outgoing.rings.size() == 1
&& outgoing.has_only(operation_intersection)
&& outgoing.region_id() == incoming.region_id()
&& extra.all_to()
&& extra.rings.size() == 1
&& extra.has_only(operation_intersection)
&& extra.region_id() == incoming.region_id();
if (! outgoing_ok)
{
return false;
}
// Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy
if (check_pairs(aggregation, incoming.region_id(), 1, n - 3))
{
selected_rank = n - 1;
return true;
}
return false;
}
}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INTERSECTION_PATTERNS_HPP

View File

@ -127,10 +127,8 @@ struct traversal_ring_creator
m_visitor.visit_traverse(m_turns, previous_turn, previous_op, "Start");
}
bool is_touching = false;
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))
{

View File

@ -16,6 +16,7 @@
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/assert.hpp>
@ -52,7 +53,8 @@ struct traversal_switch_detector
typedef typename boost::range_value<Turns>::type turn_type;
typedef typename turn_type::turn_operation_type turn_operation_type;
// For convenience
// Per ring, first turns are collected (in turn_indices), and later
// a region_id is assigned
struct merged_ring_properties
{
signed_size_type region_id;
@ -63,11 +65,26 @@ struct traversal_switch_detector
{}
};
struct connection_properties
{
std::size_t count;
std::set<signed_size_type> cluster_indices;
connection_properties()
: count(0)
{}
};
typedef std::map<signed_size_type, connection_properties> connection_map;
// Per region, a set of properties is maintained, including its connections
// to other regions
struct region_properties
{
signed_size_type region_id;
isolation_type isolated;
std::map<signed_size_type, std::size_t> connected_region_counts;
// Maps from connected region_id to their properties
connection_map connected_region_counts;
region_properties()
: region_id(-1)
@ -75,8 +92,10 @@ struct traversal_switch_detector
{}
};
// Keeps turn indices per ring
typedef std::map<ring_identifier, merged_ring_properties > merge_map;
typedef std::map<signed_size_type, region_properties> region_connection_map;
typedef std::set<signed_size_type>::const_iterator set_iterator;
inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2,
@ -100,15 +119,47 @@ struct traversal_switch_detector
return properties.isolated;
}
bool all_colocated = true;
int unique_cluster_id = -1;
for (typename connection_map::const_iterator it = properties.connected_region_counts.begin();
all_colocated && it != properties.connected_region_counts.end(); ++it)
{
connection_properties const& cprop = it->second;
if (cprop.cluster_indices.size() != 1)
{
// Either no cluster (non colocated point), or more clusters
all_colocated = false;
}
int const cluster_id = *cprop.cluster_indices.begin();
if (cluster_id == -1)
{
all_colocated = false;
}
else if (unique_cluster_id == -1)
{
unique_cluster_id = cluster_id;
}
else if (unique_cluster_id != cluster_id)
{
all_colocated = false;
}
}
if (all_colocated)
{
return isolation_yes;
}
// It is isolated if there is only one connection, or if there are more connections but all
// of them are isolated themselves
// of them are isolated themselves, or if there are more connections
// but they are all colocated
std::size_t non_isolation_count = 0;
bool child_not_isolated = false;
for (std::map<signed_size_type, std::size_t>::const_iterator it = properties.connected_region_counts.begin();
for (typename connection_map::const_iterator it = properties.connected_region_counts.begin();
it != properties.connected_region_counts.end(); ++it)
{
signed_size_type const region_id = it->first;
std::size_t const count = it->second;
connection_properties const& cprop = it->second;
if (region_id == parent_region_id)
{
@ -120,7 +171,7 @@ struct traversal_switch_detector
// Find one of its ancestors again, this is a ring. Not isolated.
return isolation_no;
}
if (count > 1)
if (cprop.count > 1)
{
return isolation_no;
}
@ -215,9 +266,32 @@ struct traversal_switch_detector
// Force insertion
m_connected_regions[id0].region_id = id0;
m_connected_regions[id1].region_id = id1;
// Add reference to connection
m_connected_regions[id0].connected_region_counts[id1]++;
m_connected_regions[id1].connected_region_counts[id0]++;
connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1];
connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0];
if (turn.cluster_id < 0)
{
// Turn is not colocated, add reference to connection
prop0.count++;
prop1.count++;
}
else
{
// Turn is colocated, only add region reference if it was not yet registered
if (prop0.cluster_indices.count(turn.cluster_id) == 0)
{
prop0.count++;
}
if (prop1.cluster_indices.count(turn.cluster_id) == 0)
{
prop1.count++;
}
}
// Insert cluster-id (also -1 is inserted - reinsertion of
// same cluster id is OK)
prop0.cluster_indices.insert(turn.cluster_id);
prop1.cluster_indices.insert(turn.cluster_id);
}
}
}
@ -225,6 +299,13 @@ struct traversal_switch_detector
inline bool connects_same_region(turn_type const& turn) const
{
if (turn.discarded)
{
// Discarded turns don't connect same region (otherwise discarded colocated uu turn
// could make a connection)
return false;
}
if (turn.cluster_id == -1)
{
// If it is a uu/ii-turn (non clustered), it is never same region
@ -248,7 +329,7 @@ struct traversal_switch_detector
}
cluster_info const& cinfo = it->second;
for (std::set<signed_size_type>::const_iterator sit = cinfo.turn_indices.begin();
for (set_iterator sit = cinfo.turn_indices.begin();
sit != cinfo.turn_indices.end(); ++sit)
{
turn_type const& cluster_turn = m_turns[*sit];
@ -343,6 +424,13 @@ struct traversal_switch_detector
{
turn_type const& turn = m_turns[turn_index];
if (turn.discarded
&& operation_from_overlay<OverlayType>::value == operation_intersection)
{
// Discarded turn (union currently still needs it to determine regions)
continue;
}
for (int op_index = 0; op_index < 2; op_index++)
{
turn_operation_type const& op = turn.operations[op_index];
@ -388,6 +476,10 @@ struct traversal_switch_detector
{
signed_size_type turn_index = *sit;
turn_type const& turn = m_turns[turn_index];
if (turn.colocated_ii && ! turn.colocated_uu)
{
continue;
}
for (int oi = 0; oi < 2; oi++)
{
int const region = get_region_id(turn.operations[oi]);
@ -395,7 +487,7 @@ struct traversal_switch_detector
}
}
// Switch source if this cluster connects the same region
cinfo.switch_source = regions.size() == 1;
cinfo.switch_source = regions.size() <= 1;
}
// Iterate through all uu/ii turns (non-clustered)

View File

@ -91,8 +91,10 @@ struct turn_info
method_type method;
signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1
bool discarded;
bool colocated_ii;
bool colocated_uu;
// TODO: move this to enriched
bool colocated_ii; // Colocated with a ii turn (TODO: or a ix turn)
bool colocated_uu; // Colocated with a uu turn or a ux turn
bool switch_source; // For u/u turns which can either switch or not
Container operations;
@ -134,12 +136,6 @@ struct turn_info
{
return has(operation_blocked);
}
inline bool self_turn() const
{
return this->operations[0].seg_id.source_index
== this->operations[1].seg_id.source_index;
}
private :
inline bool has12(operation_type type1, operation_type type2) const

View File

@ -445,7 +445,7 @@ struct self_touches
rescale_policy_type robust_policy;
detail::self_get_turn_points::get_turns
<
policy_type
false, policy_type
>::apply(geometry, strategy, robust_policy, turns, policy, 0);
return policy.result();

View File

@ -811,6 +811,8 @@ BOOST_AUTO_TEST_CASE( test_is_valid_polygon )
template <typename Point, bool AllowDuplicates>
inline void test_open_multipolygons()
{
// WKT of multipolygons should be defined as ccw, open
#ifdef BOOST_GEOMETRY_TEST_DEBUG
std::cout << std::endl << std::endl;
std::cout << "************************************" << std::endl;
@ -949,6 +951,18 @@ inline void test_open_multipolygons()
test::apply("mpg20", open_mpgn, false);
}
// Interior ring touches exterior ring, which at that same point touches another exterior ring in (1 2)
test::apply
("mpg21",
"MULTIPOLYGON(((2 3,1 2,1 0,2 0,2 1,4 3),(2 2,1.5 1.5,1 2)),((0 3,0 2,1 2)))",
true);
// Two interior rings touch each other in (10 5)
test::apply
("mpg22",
"MULTIPOLYGON(((10 5,5 10,0 5,5 0),(10 5,5 4,4 6)),((10 5,10 0,20 0,20 10,10 10),(10 5,18 8,15 3)))",
true);
// MySQL report 12.06.2015
{
std::string wkt = "MULTIPOLYGON("

View File

@ -558,6 +558,77 @@ static std::string case_130_multi[2] =
"MULTIPOLYGON(((0 0,0 7,7 7,7 0,0 0),(4 3,5 4,4 5,3 4,4 3),(1 2,2 1,3 2,2 3,1 2)))"
};
static std::string case_131_multi[2] =
{
// For validity, interior ring connections
"MULTIPOLYGON(((1 2,1 3,2 3,2 2,1 2)),((2 1,2 2,3 2,3 1,2 1)))",
"MULTIPOLYGON(((0 0,0 4,4 4,4 0,0 0),(3 3,1 3,1 1,3 1,3 3)))"
};
static std::string case_132_multi[2] =
{
// For validity, interior ring connections including cluster
"MULTIPOLYGON(((2 4,2 6,4 6,4 4,2 4)),((4 2,4 4,6 4,6 2,4 2)))",
"MULTIPOLYGON(((0 0,0 8,8 8,8 0,0 0),(6 6,2 6,2 2,6 2,6 6)),((2 2,2.5 3.5,4 4,3.5 2.5,2 2)),((4 4,4.5 5.5,6 6,5.5 4.5,4 4)))"
};
static std::string case_133_multi[2] =
{
// Zoomed in version of case_recursive_boxes_49 with in both interiors an extra polygon (same for both)
"MULTIPOLYGON(((0 0,0 4,2 4,2 6,4 6,4 8,10 8,10 4,8 4,8 0,0 0),(4 4,4 6,6 6,6 4,4 4)),((5 4.5,4 6,5.5 5, 5 4.5)))",
"MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))"
};
static std::string case_134_multi[2] =
{
// Zoomed in version of case_recursive_boxes_49 with two interior rings
"MULTIPOLYGON(((0 0,0 4,2 4,2 6,4 6,4 8,10 8,10 4,8 4,8 0,0 0),(3 4,4 6,4 4,3 4),(6 6,4 6,6 7,6 6)))",
"MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(3 4,4 6,4 4,3 4),(6 6,4 6,6 7,6 6)))"
};
static std::string case_135_multi[2] =
{
// Contains two equal interior rings, both touching to their exterior rings
// Needs detection of isolated interior ring pattern
"MULTIPOLYGON(((5 8,4 8,4 7,3 7,3 6,2 6,2 7,1 7,1 10,3 10,3 9,5 9,5 8),(3 8,3 9,2 9,2 8,3 8)))",
"MULTIPOLYGON(((5 4,4 4,3 4,3 7,1 7,1 8,0 8,0 9,1 9,1 10,3 10,3 9,4 9,4 8,6 8,6 9,7 9,7 5,6 5,6 4,5 4),(5 5,5 6,6 6,6 7,4 7,4 5,5 5),(3 8,3 9,2 9,2 8,3 8)))"
};
static std::string case_136_multi[2] =
{
// Variant of 135, but approaching cluster is non equal
"MULTIPOLYGON(((5 8,4 8,4 7,3 7,3 6,2 6,2 7,1 7,1 10,2 10,3 9,5 9,5 8),(3 8,3 9,2 9,2 8,3 8)))",
"MULTIPOLYGON(((5 4,4 4,3 4,3 7,1 7,1 8,0 8,0 9,1 9,1 10,3 10,3 9,4 9,4 8,6 8,6 9,7 9,7 5,6 5,6 4,5 4),(5 5,5 6,6 6,6 7,4 7,4 5,5 5),(3 8,3 9,2 9,2 8,3 8)))"
};
static std::string case_137_multi[2] =
{
// Variant of 135, but leaving cluster is non equal
"MULTIPOLYGON(((5 8,4 8,4 7,3 7,3 6,2 6,2 7,1 7,1 10,3 10,3 9,5 9,5 8),(3 8,3 9,2 9,2 8,3 8)))",
"MULTIPOLYGON(((5 4,4 4,3 4,3 7,1 7,1 8,0 8,0 9,1 9,1 10,3 10,3 9,4 8,6 8,6 9,7 9,7 5,6 5,6 4,5 4),(5 5,5 6,6 6,6 7,4 7,4 5,5 5),(3 8,3 9,2 9,2 8,3 8)))"
};
static std::string case_138_multi[2] =
{
// Zoomed in version of case_recursive_boxes_49 with in both interiors an extra polygon (different for both)
"MULTIPOLYGON(((0 0,0 4,2 4,2 6,4 6,4 8,10 8,10 4,8 4,8 0,0 0),(4 4,4 6,6 6,6 4,4 4)),((4.5 4.5,4 6,5.5 5.5,4.5 4.5)))",
"MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))"
};
static std::string case_139_multi[2] =
{
// Another zoomed in version of case_recursive_boxes_49 with different interior polygons
"MULTIPOLYGON(((0 0,0 4,2 4,2 6,4 6,4 8,10 8,10 4,8 4,8 0,0 0),(4 4,4 6,6 6,6 4,4 4)),((4.5 4.5,4 6,5.5 5,4.5 4.5)))",
"MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))"
};
static std::string case_140_multi[2] =
{
// Another zoomed in version of case_recursive_boxes_49 with different interior polygons
"MULTIPOLYGON(((0 0,0 4,2 4,2 6,4 6,4 8,10 8,10 4,8 4,8 0,0 0),(4 4,4 6,6 6,6 4,4 4)),((5 4.5,4 6,5.5 5.5,5 4.5)))",
"MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))"
};
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)))",
@ -918,6 +989,97 @@ static std::string case_recursive_boxes_47[2] =
"MULTIPOLYGON(((4 5,4 4,1 4,1 5,4 5)),((5 6,5 7,6 7,6 6,5 6)),((5 6,5 5,4 5,4 6,5 6)),((5 3,5 5,6 5,6 3,5 3)),((5 3,5 2,4 2,4 3,5 3)),((6 5,6 6,7 6,7 5,6 5)),((3 7,2 7,2 9,3 9,3 8,4 8,4 7,3 7)))"
};
static std::string case_recursive_boxes_48[2] =
{
// Needs discarding self-ii turns not located within the other geometry
"MULTIPOLYGON(((6 7,6 8,7 8,7 7,6 7)))",
"MULTIPOLYGON(((9 9,10 9,10 7,9 7,9 5,8 5,8 6,7 6,7 7,6 7,6 8,7 8,7 9,8 9,8 10,9 10,9 9),(9 8,8 8,8 7,9 7,9 8)))"
};
static std::string case_recursive_boxes_49[2] =
{
// Needs specific handling for cc interior rings touching one exterior ring
"MULTIPOLYGON(((5 2,5 3,4 3,4 4,5 4,5 5,6 5,6 2,7 2,7 1,0 1,0 3,1 3,1 2,5 2)),((5 7,5 8,6 8,6 9,7 9,7 10,10 10,10 8,9 8,9 6,10 6,10 4,9 4,9 3,8 3,8 4,7 4,7 6,5 6,5 7),(7 6,8 6,8 7,7 7,7 6),(8 9,7 9,7 8,8 8,8 9)),((2 6,4 6,4 5,3 5,3 4,1 4,1 5,2 5,2 6)),((3 10,4 10,4 7,2 7,2 10,3 10)))",
"MULTIPOLYGON(((5 1,4 1,4 3,3 3,3 6,2 6,2 9,5 9,5 7,4 7,4 4,5 4,5 3,6 3,6 2,8 2,8 1,9 1,9 0,5 0,5 1),(4 7,4 8,3 8,3 7,4 7)),((8 2,8 3,9 3,9 2,8 2)),((9 5,9 6,10 6,10 5,9 5)),((9 5,9 4,8 4,8 5,9 5)),((7 5,7 4,6 4,6 5,7 5)),((7 10,9 10,9 9,10 9,10 7,8 7,8 6,6 6,6 10,7 10),(7 9,7 8,8 8,8 9,7 9)))"
};
static std::string case_recursive_boxes_50[2] =
{
// Same as 49 but needs the second variant
"MULTIPOLYGON(((6 9,6 10,7 10,7 9,6 9)),((5 6,6 6,6 5,4 5,4 7,3 7,3 8,4 8,4 9,2 9,2 10,5 10,5 8,6 8,6 7,5 7,5 6)),((4 0,1 0,1 1,2 1,2 2,3 2,3 3,5 3,5 0,4 0)),((3 5,3 3,1 3,1 4,0 4,0 6,1 6,1 7,0 7,0 8,2 8,2 6,3 6,3 5),(2 5,1 5,1 4,2 4,2 5)),((1 1,0 1,0 2,1 2,1 1)),((6 5,7 5,7 4,6 4,6 5)),((10 5,8 5,8 6,9 6,9 7,10 7,10 5)),((6 0,6 1,8 1,8 2,10 2,10 0,6 0)),((9 7,7 7,7 8,8 8,8 9,9 9,9 7)),((8 2,6 2,6 3,8 3,8 2)),((9 9,9 10,10 10,10 9,9 9)))",
"MULTIPOLYGON(((5 3,5 4,6 4,6 3,5 3)),((5 7,6 7,6 5,4 5,4 6,3 6,3 7,4 7,4 8,5 8,5 7)),((3 6,3 4,4 4,4 3,1 3,1 2,0 2,0 9,1 9,1 10,3 10,3 9,2 9,2 8,3 8,3 7,1 7,1 6,3 6),(1 5,1 4,2 4,2 5,1 5)),((4 0,0 0,0 1,1 1,1 2,3 2,3 1,5 1,5 0,4 0)),((7 6,7 7,8 7,8 8,9 8,9 6,8 6,8 5,7 5,7 6)),((9 0,7 0,7 1,9 1,9 2,10 2,10 0,9 0)),((7 9,5 9,5 10,7 10,7 9)),((7 9,8 9,8 8,7 8,7 9)),((8 3,8 2,7 2,7 3,8 3)),((8 3,8 4,9 4,9 3,8 3)),((8 9,8 10,9 10,9 9,8 9)),((9 8,9 9,10 9,10 8,9 8)))"
};
static std::string case_recursive_boxes_51[2] =
{
// Needs keeping colocated turns with a xx turn to properly generate interior rings. It also needs self-turns for validity
"MULTIPOLYGON(((0 4,0 5,1 5,1 4,0 4)),((2 1,3 1,3 0,2 0,2 1)),((3 3,4 3,4 2,3 2,3 3)),((5 4,6 4,6 3,5 3,5 4)),((2 3,2 4,3 4,3 3,2 3)),((5 6,4 6,4 5,3 5,3 10,4 10,4 9,5 9,5 8,4 8,4 7,6 7,6 9,7 9,7 10,8 10,8 8,7 8,7 7,8 7,8 4,7 4,7 5,6 5,6 6,5 6)),((5 0,4 0,4 1,5 1,5 2,6 2,6 3,7 3,7 1,8 1,8 0,5 0)),((0 2,1 2,1 1,0 1,0 2)),((1 2,1 3,2 3,2 2,1 2)),((1 10,2 10,2 8,1 8,1 9,0 9,0 10,1 10)),((1 8,1 7,0 7,0 8,1 8)),((10 1,10 0,9 0,9 1,8 1,8 4,9 4,9 6,10 6,10 1)))",
"MULTIPOLYGON(((3 1,4 1,4 0,2 0,2 1,3 1)),((1 9,0 9,0 10,1 10,1 9)),((8 2,9 2,9 1,10 1,10 0,8 0,8 2)),((5 8,4 8,4 9,5 9,5 10,7 10,7 8,8 8,8 7,7 7,7 6,8 6,8 7,10 7,10 6,9 6,9 3,8 3,8 2,6 2,6 1,5 1,5 3,7 3,7 4,4 4,4 5,5 5,5 6,6 6,6 8,5 8),(7 5,7 4,8 4,8 5,7 5)),((4 4,4 3,2 3,2 4,1 4,1 5,0 5,0 7,1 7,1 9,2 9,2 8,4 8,4 6,3 6,3 4,4 4),(1 5,2 5,2 6,1 6,1 5)),((0 3,1 3,1 1,0 1,0 3)),((4 9,3 9,3 10,4 10,4 9)),((9 10,10 10,10 9,9 9,9 8,8 8,8 10,9 10)))"
};
static std::string case_recursive_boxes_52[2] =
{
// Needs checking for isolated in handling of cc_ii clusters for intersection
"MULTIPOLYGON(((0 0,0 1,1 1,1 0,0 0)),((5 1,2 1,2 3,1 3,1 4,0 4,0 8,1 8,1 9,2 9,2 8,5 8,5 5,6 5,6 7,7 7,7 9,9 9,9 10,10 10,10 7,9 7,9 6,10 6,10 5,9 5,9 1,8 1,8 2,7 2,7 3,6 3,6 4,5 4,5 2,6 2,6 0,5 0,5 1),(4 4,4 5,3 5,3 6,4 6,4 7,1 7,1 6,2 6,2 5,3 5,3 4,4 4),(7 5,7 4,8 4,8 6,7 6,7 5),(2 5,1 5,1 4,2 4,2 5)),((5 8,5 9,6 9,6 8,5 8)),((8 1,8 0,7 0,7 1,8 1)))",
"MULTIPOLYGON(((1 1,2 1,2 0,1 0,1 1)),((9 10,10 10,10 9,9 9,9 10)),((3 9,3 10,4 10,4 9,3 9)),((1 10,2 10,2 9,0 9,0 10,1 10)),((5 4,4 4,4 3,3 3,3 2,1 2,1 1,0 1,0 3,2 3,2 4,1 4,1 5,5 5,5 7,6 7,6 6,7 6,7 8,9 8,9 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 2,8 2,8 1,7 1,7 0,6 0,6 2,7 2,7 3,6 3,6 4,5 4),(8 5,7 5,7 4,8 4,8 5)),((5 9,5 10,8 10,8 9,6 9,6 8,5 8,5 9)),((4 0,3 0,3 1,4 1,4 2,5 2,5 0,4 0)),((3 8,4 8,4 6,2 6,2 8,3 8)),((9 1,9 0,8 0,8 1,9 1)),((9 7,10 7,10 6,9 6,9 7)))"
};
static std::string case_recursive_boxes_53[2] =
{
// Needs checking for intersection patterns intersection_pattern_common_interior2 with ii
"MULTIPOLYGON(((2 0,0 0,0 5,5 5,4 4,5 4,5 0,2 0),(2.5 4.5,3 4,3 5,2.5 4.5),(3 1,4 0,4 1,3 1),(3 3,4 2,4 3,3 3)))",
"MULTIPOLYGON(((2 0,0 0,0 5,1 5,0.5 4.5,1 4,2 5,5 5,5 0,2 0),(2 3,2 4,1 4,2 3),(3 1,3 2,2 2,2 1,3 1),(1 3,0 2,1 2,1 3),(1 0,1 1,0.5 0.5,1 0),(5 3,4 3,4 2,5 3),(4 1,3.5 0.5,4 0,4 1)))"
};
static std::string case_recursive_boxes_54[2] =
{
// Needs including blocked from-ranks/operations in aggregations
"MULTIPOLYGON(((2 2,2 3,1 3,1 5,2 4,2 5,5 5,5 4,4 3,5 3,5 2,4 2,4 1,5 0,2 0,2 1,1 1,1 2,2 2),(3 2,2.5 1.5,3 1,4 2,3 2),(3 2,3 3,2.5 2.5,3 2),(3 4,3 3,4 4,3 4)),((1 1,1 0,0 0,0 1,1 1)),((1 3,1 2,0 1,0 3,1 3)))",
"MULTIPOLYGON(((2 2,3 3,2 3,2 5,5 5,5 3,4 3,3 2,5 2,5 1,4 1,3 0,1 0,1 1,0 1,0 2,2 2)),((0 4,0 5,1 5,1 4,0 3,0 4)))"
};
static std::string case_recursive_boxes_55[2] =
{
// Needs correct handling for union clusters with 3 open spaces
"MULTIPOLYGON(((3 4,3 5,4 5,3 4)),((0 4,1 4,1 3,0 3,0 4)),((2 1,1.5 0.5,2 0,1 0,1 1,0 1,0 2,2 2,2 1)),((3 2,3 3,4 2,4 3,5 2,4 1,2 1,3 2)),((2 2,2 3,3 3,2 2)))",
"MULTIPOLYGON(((2 1,2 0,1 0,2 1)),((2 1,2 2,3 2,3 1,2 1)),((1 3,1 4,2 5,2 2,1 2,0 1,0 4,1 3)),((5 4,4 3,3 3,4 4,3 4,3 5,5 5,5 4)),((1 0,0 0,1 1,1 0)),((0 4,0 5,1 5,0 4)))"
};
static std::string case_recursive_boxes_56[2] =
{
// Needs not discarding clustered self-turns if the cluster does not contain anything else
"MULTIPOLYGON(((3 4,3 5,4 5,3 4)),((3 3,4 3,2 1,2 3,3 3)),((2 1,2 0,1 0,1 1,2 1)),((1 4,1 3,0 3,0 4,1 5,1 4)))",
"MULTIPOLYGON(((2 4,2 5,3 4,2 4)),((1 5,1 4,0 4,0 5,1 5)),((1 2,0 2,0 3,1 2)),((2 2,1 2,2 3,2 2)),((2 2,2 1,1 1,1 2,1.5 1.5,2 2)))"
};
static std::string case_recursive_boxes_57[2] =
{
// Needs handling of 3 open spaces in union to form interior ring
"MULTIPOLYGON(((2 0,0 0,0 1,0.5 0.5,1 1,1 2,2 1,4 1,4 4,5 3,5 0,2 0)),((2 5,2 4,1 4,1 5,2 5)),((1 2,0 2,0 3,1 2)),((1 2,1 3,2 3,2 2,1 2)),((0 1,0 2,1 1,0 1)),((4 3,3 3,3 4,4 3)),((3 4,3 5,5 5,5 4,3 4)))",
"MULTIPOLYGON(((3 3,2 3,2 5,3 4,3 5,5 5,5 4,4 4,4 3,3 3)),((3 3,4 2,2 2,3 3)),((0 0,0 4,1 4,1 3,2 3,2 2,1 2,1 0,0 0)),((3 1,2 1,2 2,3 1)),((1 5,0 4,0 5,1 5)),((1 5,2 5,1 4,1 5)),((5 2,5 1,4 1,4 3,5 4,5 2)),((4 1,4 0,3 0,3 1,4 1)),((2 1,2 0,1 0,2 1)))",
};
static std::string case_recursive_boxes_58[2] =
{
// Needs correct handling for union clusters with 3 open spaces
"MULTIPOLYGON(((0 0,1 1,2 1,1 0,0 0)),((1 1,1 2,2 2,1 1)),((3 1,4 2,4 1,3 1)),((3 2,3 3,4 3,3 2)))",
"MULTIPOLYGON(((0 1,0 2,1 1,0 1)),((1 1,1 2,2 1,1 1)),((1 2,1 3,2 2,1 2)),((2 2,2 3,3 2,2 2)),((4 2,4 3,5 3,4 2)),((1 0,2 1,2 2,3 1,2 0,1 0)))",
};
static std::string case_recursive_boxes_59[2] =
{
// Needs correct handling for union clusters with 3 open spaces
"MULTIPOLYGON(((2 3,2 2,3 2,3 1,2 1,1.5 0.5,2 0,0 0,0 1,1 1,1 4,2 4,3 5,5 5,5 4,3 4,3 3,2 3)),((3 2,3 3,4 3,4 2,3 2)),((2 0,3 1,5 1,5 0,2 0)),((0 3,0 5,1 5,1 4,0 3)),((0 3,1 3,0 2,0 3)))",
"MULTIPOLYGON(((3 5,3 4,4 4,4 3,5 3,5 2,4 2,4 1,3.5 0.5,4 0,1 0,1 1,2 2,1 2,1 4,0 4,1 5,3 5),(2 3,2 4,1 3,2 3),(2 2,3 2,3 3,2 2)),((1 1,0 1,0 3,1 2,1 1)))",
};
static std::string case_recursive_boxes_60[2] =
{
// Needs checking left_count in union clusters
"MULTIPOLYGON(((0 4,0 5,1 5,0 4)),((1 2,1 4,2 4,2 5,3 4,4 5,5 5,5 4,4 4,3.5 3.5,4 3,4 2,3 2,2 1,2 2,1 2)),((1 2,2 1,1 1,1 2)),((2 1,3 1,4 2,5 1,4 1,4 0,2 0,2 1)),((0 0,0 2,2 0,0 0)),((0 2,0 3,1 3,0 2)),((5 4,5 3,4 3,5 4)))",
"MULTIPOLYGON(((2 3,1 2,1 4,0 4,0 5,3 5,3 3,2 3)),((1 2,3 2,3 3,4 4,5 4,5 3,4 3,3.5 2.5,4 2,4 0,0 0,0 1,1 1,1 2)),((4 2,5 3,5 2,4 2)))",
};
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

@ -347,10 +347,21 @@ void test_overlay(std::string const& caseid,
bg::correct(g2);
#if defined(TEST_WITH_SVG)
bool const ccw = bg::point_order<Geometry>::value == bg::counterclockwise;
bool const open = bg::closure<Geometry>::value == bg::open;
std::ostringstream filename;
filename << "overlay"
<< "_" << caseid
<< "_" << string_from_type<typename bg::coordinate_type<Geometry>::type>::name()
<< (ccw ? "_ccw" : "")
<< (open ? "_open" : "")
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
<< "_self"
#endif
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
<< "_no_rob"
#endif
<< ".svg";
std::ofstream svg(filename.str().c_str());
@ -372,8 +383,13 @@ void test_overlay(std::string const& caseid,
typedef typename boost::range_value<Geometry>::type geometry_out;
typedef bg::detail::overlay::overlay
<
Geometry, Geometry, false, OverlayType == bg::overlay_difference,
false, geometry_out,
Geometry, Geometry,
bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
OverlayType == bg::overlay_difference
? ! bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value
: bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
geometry_out,
OverlayType
> overlay;
@ -434,11 +450,11 @@ void test_overlay(std::string const& caseid,
#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>
template <typename T, bool Clockwise>
void test_all()
{
typedef bg::model::point<T, 2, bg::cs::cartesian> point_type;
typedef bg::model::polygon<point_type> polygon;
typedef bg::model::polygon<point_type, Clockwise> polygon;
typedef bg::model::multi_polygon<polygon> multi_polygon;
TEST_UNION(case_multi_simplex, 14.58, 1, 0);
@ -482,6 +498,7 @@ void test_all()
int test_main(int, char* [])
{
test_all<double>();
test_all<double, true>();
// test_all<double, false>();
return 0;
}

View File

@ -35,9 +35,16 @@
#endif
#define TEST_DIFFERENCE(caseid, clips1, points1, area1, clips2, points2, area2) \
// Convenience macros (points are not checked)
#define TEST_DIFFERENCE(caseid, clips1, area1, clips2, area2, clips3) \
(test_one<polygon, polygon, polygon>) \
( #caseid, caseid[0], caseid[1], clips1, points1, area1, clips2, points2, area2)
( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \
clips3, -1, area1 + area2)
#define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \
(test_one<polygon, polygon, polygon>) \
( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \
clips3, -1, area1 + area2, ignore_validity)
template <typename P>
void test_all()
@ -232,9 +239,9 @@ void test_all()
4, 18, 1.5,
3, 15, 4.0625);
TEST_DIFFERENCE(case_105, 4, 20, 8.0, 1, 9, 16.0);
TEST_DIFFERENCE(case_106, 1, 12, 17.5, 2, 9, 32.5);
TEST_DIFFERENCE(case_107, 2, 16, 18.0, 2, 16, 29.0);
TEST_DIFFERENCE(case_105, 4, 8.0, 1, 16.0, 5);
TEST_DIFFERENCE(case_106, 1, 17.5, 2, 32.5, 3);
TEST_DIFFERENCE(case_107, 2, 18.0, 2, 29.0, 4);
test_one<polygon, polygon, polygon>("winded",
winded[0], winded[1],
@ -523,57 +530,36 @@ void test_all()
***/
// Should have 2 outputs
int const correction_for_invalidity = 1; // should be 0
test_one<polygon, polygon, polygon>("mysql_21977775",
mysql_21977775[0], mysql_21977775[1],
2 - correction_for_invalidity, -1, 160.856568913,
2, -1, 92.3565689126,
ignore_validity);
// also mysql_23023665
test_one<polygon, polygon, polygon>("mysql_21965285",
mysql_21965285[0], mysql_21965285[1],
1, 2, -1, 92.0,
1, 1, -1, 14.0,
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, -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, -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, -1, 225.0,
1, 1, -1, 66.0,
ignore_validity);
test_one<polygon, polygon, polygon>("mysql_23023665_5",
mysql_23023665_5[0], mysql_23023665_5[1],
2 - correction_for_invalidity, 2 - correction_for_invalidity, -1, 165.23735,
2, 2, -1, 105.73735,
ignore_validity);
test_one<polygon, polygon, polygon>("mysql_23023665_6",
mysql_23023665_6[0], mysql_23023665_6[1],
2, 2, -1, 105.68756,
3, 3, -1, 10.18756
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
, ignore_validity
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(mysql_21977775,
2, 160.856568913, 2, 92.3565689126, 4);
#else
TEST_DIFFERENCE_IGNORE(mysql_21977775,
1, 160.856568913, 2, 92.3565689126, 3);
#endif
TEST_DIFFERENCE(mysql_21965285, 1, 92.0, 1, 14.0, 1);
TEST_DIFFERENCE_IGNORE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2);
TEST_DIFFERENCE_IGNORE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2);
TEST_DIFFERENCE_IGNORE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(mysql_23023665_5, 2, 165.23735, 2, 105.73735, 4);
#else
TEST_DIFFERENCE_IGNORE(mysql_23023665_5, 1, 165.23735, 2, 105.73735, 3);
#endif
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
TEST_DIFFERENCE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5);
#else
TEST_DIFFERENCE_IGNORE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5);
#endif
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(mysql_23023665_13, 3, 99.74526, 3, 37.74526, 6);
#else
TEST_DIFFERENCE_IGNORE(mysql_23023665_13, 2, 99.74526, 3, 37.74526, 5);
#endif
);
test_one<polygon, polygon, polygon>("mysql_23023665_13",
mysql_23023665_13[0], mysql_23023665_13[1],
3 - correction_for_invalidity, 3 - correction_for_invalidity, -1, 99.74526,
3, 3, -1, 37.74526,
ignore_validity);
}
@ -599,21 +585,17 @@ void test_specific()
2, 8, 489763.5,
1, 4, 6731652.0);
{
ut_settings settings;
settings.test_validity = false;
#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS
settings.test_validity = true;
#endif
// Generates spikes, both a-b and b-a
test_one<polygon, polygon, polygon>("ticket_11676",
ticket_11676[0], ticket_11676[1],
1, 18, 2537992.5,
2, 11, 294963.5,
2, -1, 2537992.5 + 294963.5,
settings);
}
// Generates spikes, both a-b and b-a
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(ticket_11676, 2, 2537992.5, 2, 294963.5, 3);
#else
ut_settings ignore_validity;
ignore_validity.test_validity = false;
TEST_DIFFERENCE_IGNORE(ticket_11676, 1, 2537992.5, 2, 294963.5, 2);
#endif
}

View File

@ -23,13 +23,24 @@
#include <boost/geometry/io/wkt/read.hpp>
#define TEST_DIFFERENCE(caseid, clips1, points1, area1, clips2, points2, area2) \
// Convenience macros (points are not checked)
#define TEST_DIFFERENCE(caseid, clips1, area1, clips2, area2, clips3) \
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
( #caseid, caseid[0], caseid[1], clips1, points1, area1, clips2, points2, area2)
( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \
clips3, -1, area1 + area2)
#define TEST_DIFFERENCE_IGNORE(caseid, clips1, points1, area1, clips2, points2, area2) \
#define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
( #caseid, caseid[0], caseid[1], clips1, points1, area1, clips2, points2, area2, ignore_validity)
( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \
clips3, -1, area1 + area2, ignore_validity)
#define TEST_DIFFERENCE_WITH(index1, index2, caseid, clips1, area1, \
clips2, area2, clips3) \
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
( #caseid "_" #index1 "_" #index2, caseid[index1], caseid[index2], \
clips1, -1, area1, \
clips2, -1, area2, \
clips3, -1, area1 + area2, settings)
template <typename Ring, typename Polygon, typename MultiPolygon>
@ -95,10 +106,16 @@ 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);
TEST_DIFFERENCE_IGNORE(case_125_multi, 1, 4, 0.25, 2, 12, 0.400);
TEST_DIFFERENCE_IGNORE(case_126_multi, 3, 22, 16.0, 4, 27, 27.0); // A should have 3 clips, B should have 5 clips
TEST_DIFFERENCE(case_123_multi, 1, 0.25, 2, 0.625, 3);
TEST_DIFFERENCE(case_124_multi, 1, 0.25, 2, 0.4375, 3);
TEST_DIFFERENCE_IGNORE(case_125_multi, 1, 0.25, 2, 0.400, 3);
// A should have 3 clips, B should have 5 clips
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_126_multi, 4, 16.0, 5, 27.0, 9);
#else
TEST_DIFFERENCE_IGNORE(case_126_multi, 3, 16.0, 4, 27.0, 7);
#endif
{
ut_settings settings;
@ -144,12 +161,13 @@ void test_areal()
{
ut_settings settings;
settings.percentage = 0.001;
settings.test_validity = false;
test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120221_volker",
ggl_list_20120221_volker[0], ggl_list_20120221_volker[1],
2, 12, 7962.66, 1, 18, 2775258.93,
settings);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 2, 2775258.93, 4);
#else
settings.test_validity = false;
TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 1, 2775258.93, 3);
#endif
}
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
@ -188,14 +206,19 @@ void test_areal()
3, 14, 3.0,
4, 21, 5.25);
TEST_DIFFERENCE(case_133_multi, 3, 16.0, 2, 8.0, 5);
TEST_DIFFERENCE(case_134_multi, 3, 16.0, 2, 8.0, 5);
TEST_DIFFERENCE(case_135_multi, 2, 2.0, 2, 13.0, 2);
TEST_DIFFERENCE(case_136_multi, 2, 2.0, 3, 13.5, 3);
TEST_DIFFERENCE(case_137_multi, 2, 2.5, 2, 13.0, 2);
// Areas correspond with POSTGIS,
// #clips in PostGIS is 11,11,5 but should most probably be be 12,12,6
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_1",
case_recursive_boxes_1[0], case_recursive_boxes_1[1],
11, 75, 26.0,
12, 77, 24.0,
5, 98, 50.0,
ignore_validity);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_1, 12, 26.0, 12, 24.0, 6);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_1, 11, 26.0, 12, 24.0, 5);
#endif
// Areas and #clips correspond with POSTGIS
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_2",
@ -213,19 +236,18 @@ void test_areal()
// 4, input is not valid
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_5",
case_recursive_boxes_5[0], case_recursive_boxes_5[1],
15, -1, 22.0, // #clips should be 16
11, -1, 27.0, // #clips should be 12
8, -1, 49.0,
ignore_validity);
// Should have 16,12 clips in a,b
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_5, 16, 22.0, 12, 27.0, 10);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_5, 15, 22.0, 11, 27.0, 8);
#endif
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_6",
case_recursive_boxes_6[0], case_recursive_boxes_6[1],
6, -1, 3.5,
3, -1, 1.5,
8, -1, 5.0,
ignore_validity);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_6, 7, 3.5, 3, 1.5, 9);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_6, 6, 3.5, 3, 1.5, 8);
#endif
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_7",
case_recursive_boxes_7[0], case_recursive_boxes_7[1],
@ -257,6 +279,89 @@ void test_areal()
3, -1, 4.5,
3, -1, 7.0);
TEST_DIFFERENCE(case_recursive_boxes_12, 4, 2.75, 3, 2.75, 6);
TEST_DIFFERENCE(case_recursive_boxes_13, 4, 4.75, 3, 5.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_14, 3, 2.0, 4, 2.5, 5);
TEST_DIFFERENCE(case_recursive_boxes_15, 3, 3.0, 2, 2.5, 3);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_16, 8, 6.5, 3, 5.5, 9);
TEST_DIFFERENCE(case_recursive_boxes_17, 10, 7.75, 7, 5.5, 13);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_16, 7, 6.5, 3, 5.5, 8);
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_17, 9, 7.75, 6, 5.5, 11);
#endif
TEST_DIFFERENCE(case_recursive_boxes_18, 2, 1.0, 1, 1.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_19, 2, 1.0, 2, 1.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_20, 2, 1.0, 0, 0.0, 2);
TEST_DIFFERENCE(case_recursive_boxes_21, 2, 1.0, 1, 1.0, 1);
TEST_DIFFERENCE(case_recursive_boxes_22, 2, 1.25, 2, 2.0, 2);
TEST_DIFFERENCE(case_recursive_boxes_23, 2, 0.75, 1, 0.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_24, 3, 2.5, 2, 2.0, 5);
TEST_DIFFERENCE(case_recursive_boxes_25, 2, 2.5, 3, 2.5, 2);
TEST_DIFFERENCE(case_recursive_boxes_26, 2, 1.5, 3, 2.0, 4);
TEST_DIFFERENCE(case_recursive_boxes_27, 1, 1.5, 3, 2.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_28, 3, 2.5, 2, 3.0, 4);
TEST_DIFFERENCE(case_recursive_boxes_29, 5, 7.25, 5, 4.5, 5);
TEST_DIFFERENCE(case_recursive_boxes_30, 6, 4.25, 3, 7.25, 7);
TEST_DIFFERENCE(case_recursive_boxes_31, 2, 2.0, 1, 0.5, 2);
TEST_DIFFERENCE(case_recursive_boxes_32, 2, 2.75, 2, 1.25, 2);
TEST_DIFFERENCE(case_recursive_boxes_33, 4, 3.0, 3, 6.0, 4);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_34, 7, 7.25, 1, 0.5, 8);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_34, 5, 7.25, 1, 0.5, 6);
#endif
TEST_DIFFERENCE(case_recursive_boxes_35, 5, 1.75, 5, 2.75, 10);
TEST_DIFFERENCE(case_recursive_boxes_36, 2, 1.0, 2, 1.5, 3);
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_37, 3, 2.5, 2, 4.25, 2);
TEST_DIFFERENCE(case_recursive_boxes_38, 5, 7.75, 4, 3.5, 3);
TEST_DIFFERENCE(case_recursive_boxes_39, 3, 6.0, 3, 3.0, 4);
TEST_DIFFERENCE(case_recursive_boxes_40, 11, 14.0, 9, 13.0, 11);
TEST_DIFFERENCE(case_recursive_boxes_41, 1, 0.5, 1, 0.5, 2);
#ifndef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
// 42.a Fails with self-turns
TEST_DIFFERENCE(case_recursive_boxes_42, 1, 1.0, 4, 4.0, 5);
#endif
TEST_DIFFERENCE(case_recursive_boxes_43, 1, 0.5, 3, 2.0, 4);
TEST_DIFFERENCE(case_recursive_boxes_44, 3, 5.0, 0, 0.0, 3);
TEST_DIFFERENCE(case_recursive_boxes_45, 6, 20.0, 7, 20.0, 3);
TEST_DIFFERENCE(case_recursive_boxes_46, 4, 14.0, 5, 12.0, 5);
TEST_DIFFERENCE(case_recursive_boxes_47, 4, 10.0, 7, 11.0, 1);
TEST_DIFFERENCE(case_recursive_boxes_48, 0, 0.0, 1, 9.0, 1);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_49, 10, 22.0, 10, 17.0, 11);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_49, 9, 22.0, 10, 17.0, 10);
#endif
TEST_DIFFERENCE(case_recursive_boxes_50, 14, 21.0, 16, 21.0, 14);
TEST_DIFFERENCE(case_recursive_boxes_51, 14, 25.0, 12, 31.0, 7);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_52, 13, 30.0, 15, 25.0, 8);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_52, 13, 30.0, 15, 25.0, 8);
#endif
TEST_DIFFERENCE(case_recursive_boxes_53, 6, 3.5, 4, 1.5, 9);
TEST_DIFFERENCE(case_recursive_boxes_54, 6, 6.5, 8, 6.0, 7);
TEST_DIFFERENCE(case_recursive_boxes_55, 4, 5.5, 6, 7.75, 4);
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_56, 4, 4.5, 5, 2.75, 6);
TEST_DIFFERENCE(case_recursive_boxes_57, 5, 3.75, 9, 6.5, 10);
TEST_DIFFERENCE(case_recursive_boxes_58, 4, 2.25, 6, 3.75, 7);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_59, 8, 6.5, 7, 7.0, 12);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_59, 8, 6.5, 6, 7.0, 11);
#endif
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE(case_recursive_boxes_60, 6, 5.25, 7, 5.25, 11);
#else
TEST_DIFFERENCE_IGNORE(case_recursive_boxes_60, 5, 5.25, 5, 5.25, 8);
#endif
test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_21965285_b",
mysql_21965285_b[0],
mysql_21965285_b[1],
@ -277,12 +382,9 @@ void test_all()
// Test cases for integer coordinates / ccw / open
template <typename Point, bool ClockWise, bool Closed>
void test_specific()
template <typename Polygon, typename MultiPolygon>
void test_specific_areal()
{
typedef bg::model::polygon<Point, ClockWise, Closed> polygon;
typedef bg::model::multi_polygon<polygon> multi_polygon;
{
// Spikes in a-b and b-a, failure in symmetric difference
@ -294,12 +396,7 @@ void test_specific()
settings.sym_difference = true;
#endif
test_one<polygon, multi_polygon, multi_polygon>("ticket_11674",
ticket_11674[0], ticket_11674[1],
3, 27, 9105781.5,
5, 22, 119059.5,
2, -1, -1,
settings);
TEST_DIFFERENCE_WITH(0, 1, ticket_11674, 3, 9105781.5, 5, 119059.5, -1);
}
{
@ -307,25 +404,25 @@ void test_specific()
// Spikes in a-b and b-a, failure in symmetric difference
ut_settings settings;
settings.remove_spikes = true;
#if ! defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)
settings.sym_difference = false;
settings.test_validity = false;
settings.remove_spikes = true;
#endif
std::string a_min_b =
test_one<polygon, multi_polygon, multi_polygon>("ticket_12751_1",
ticket_12751[0], ticket_12751[1],
1, 14, 2781965.0,
1, 4, 597.0,
settings);
TEST_DIFFERENCE_WITH(0, 1, ticket_12751, 1, 2781965.0, 1, 597.0, 2);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_DIFFERENCE_WITH(2, 3, ticket_12751, 2, 2537992.5, 2, 294963.5, 3);
#else
// Testing consistency of testcase itself
BOOST_CHECK_EQUAL(a_min_b, ticket_12751[2]);
test_one<polygon, multi_polygon, multi_polygon>("ticket_12751_2",
ticket_12751[2], ticket_12751[3],
1, 18, 2537992.5,
2, 11, 294963.5,
settings);
TEST_DIFFERENCE_WITH(2, 3, ticket_12751, 1, 2537992.5, 2, 294963.5, 3);
#endif
}
{
@ -336,36 +433,29 @@ void test_specific()
settings.sym_difference = false;
settings.test_validity = false;
test_one<polygon, multi_polygon, multi_polygon>("ticket_12752",
ticket_12752[0], ticket_12752[1],
3, 22, 2776692.0,
3, 11, 7893.0,
settings);
TEST_DIFFERENCE_WITH(0, 1, ticket_12752, 3, 2776692.0, 3, 7893.0, 6);
}
{
ut_settings settings;
settings.test_validity = true;
std::string a_min_b =
test_one<polygon, multi_polygon, multi_polygon>("ticket_10661_1",
ticket_10661[0], ticket_10661[1],
2, 11, 1441632.5,
2, 7, 13167454,
settings);
TEST_DIFFERENCE(ticket_10661, 2, 1441632.5, 2, 13167454, 4);
settings.test_validity = false;
#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS
settings.test_validity = true;
#endif
test_one<polygon, multi_polygon, multi_polygon>("ticket_10661_2",
test_one<Polygon, MultiPolygon, MultiPolygon>("ticket_10661_2",
a_min_b, ticket_10661[2],
1, 8, 825192.0,
1, 10, 27226370.5,
1, -1, 825192.0 + 27226370.5,
settings);
1, -1, 825192.0 + 27226370.5);
}
}
template <typename Point, bool ClockWise, bool Closed>
void test_specific()
{
typedef bg::model::polygon<Point, ClockWise, Closed> polygon;
typedef bg::model::multi_polygon<polygon> multi_polygon;
test_specific_areal<polygon, multi_polygon>();
}
int test_main(int, char* [])
{

View File

@ -91,10 +91,22 @@ void difference_output(std::string const& caseid, G1 const& g1, G2 const& g2, Ou
typedef typename bg::coordinate_type<G1>::type coordinate_type;
typedef typename bg::point_type<G1>::type point_type;
bool const ccw =
bg::point_order<G1>::value == bg::counterclockwise
|| bg::point_order<G2>::value == bg::counterclockwise;
bool const open =
bg::closure<G1>::value == bg::open
|| bg::closure<G2>::value == bg::open;
std::ostringstream filename;
filename << "difference_"
<< caseid << "_"
<< string_from_type<coordinate_type>::name()
<< (ccw ? "_ccw" : "")
<< (open ? "_open" : "")
#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)
<< "_self"
#endif
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
<< "_no_rob"
#endif
@ -187,7 +199,8 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g
std::string message;
bool const valid = bg::is_valid(result, message);
BOOST_CHECK_MESSAGE(valid,
"difference: " << caseid << " not valid " << message);
"difference: " << caseid << " not valid " << message
<< " type: " << (type_for_assert_message<G1, G2>()));
}
#endif

View File

@ -154,6 +154,16 @@ void test_areal()
TEST_INTERSECTION(case_129_multi, 1, 20, 20.5);
TEST_INTERSECTION(case_130_multi, 2, 30, 39.0);
TEST_INTERSECTION(case_133_multi, 2, 23, 40.625);
TEST_INTERSECTION(case_134_multi, 1, 23, 42.0);
TEST_INTERSECTION(case_135_multi, 1, 17, 7.0);
TEST_INTERSECTION(case_136_multi, 1, 17, 6.5);
TEST_INTERSECTION(case_137_multi, 1, 17, 6.5);
TEST_INTERSECTION(case_138_multi, 2, 23, 40.4);
TEST_INTERSECTION(case_139_multi, 2, 23, 40.546875);
TEST_INTERSECTION(case_140_multi, 2, 23, 40.546875);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_INTERSECTION(case_recursive_boxes_1, 10, 97, 47.0);
#else
@ -282,6 +292,21 @@ void test_areal()
TEST_INTERSECTION(case_recursive_boxes_43, 2, 0, 22.5);
TEST_INTERSECTION(case_recursive_boxes_44, 2, 0, 3.0);
TEST_INTERSECTION(case_recursive_boxes_45, 7, 0, 12.0);
TEST_INTERSECTION(case_recursive_boxes_46, 6, -1, 7.0);
TEST_INTERSECTION(case_recursive_boxes_47, 1, 5, 1.0);
TEST_INTERSECTION(case_recursive_boxes_48, 1, 5, 1.0);
TEST_INTERSECTION(case_recursive_boxes_49, 7, 57, 20.0);
TEST_INTERSECTION(case_recursive_boxes_50, 9, 71, 26.0);
TEST_INTERSECTION(case_recursive_boxes_51, 14, 79, 19.0);
TEST_INTERSECTION(case_recursive_boxes_52, 8, -1, 22.0);
TEST_INTERSECTION(case_recursive_boxes_53, 1, -1, 19.75);
TEST_INTERSECTION(case_recursive_boxes_54, 3, -1, 10.0);
TEST_INTERSECTION(case_recursive_boxes_55, 5, -1, 2.25);
TEST_INTERSECTION(case_recursive_boxes_56, 1, -1, 0.5);
TEST_INTERSECTION(case_recursive_boxes_57, 10, -1, 9.5);
TEST_INTERSECTION(case_recursive_boxes_58, 1, -1, 0.25);
TEST_INTERSECTION(case_recursive_boxes_59, 8, -1, 8.25);
TEST_INTERSECTION(case_recursive_boxes_60, 8, -1, 10.0);
test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120915_h2_a",
ggl_list_20120915_h2[0], ggl_list_20120915_h2[1],

View File

@ -102,7 +102,8 @@ check_result(
std::string message;
bool const valid = bg::is_valid(*it, message);
BOOST_CHECK_MESSAGE(valid,
"intersection: " << caseid << " not valid " << message);
"intersection: " << caseid << " not valid: " << message
<< " type: " << (type_for_assert_message<G1, G2>()));
}
}
@ -240,6 +241,9 @@ typename bg::default_area_result<G1>::type test_intersection(std::string const&
<< string_from_type<CalculationType>::name()
<< (ccw ? "_ccw" : "")
<< (open ? "_open" : "")
#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)
<< "_self"
#endif
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
<< "_no_rob"
#endif

View File

@ -21,6 +21,7 @@
#include "../setop_output_type.hpp"
#include <boost/core/ignore_unused.hpp>
#include <boost/foreach.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/geometry/algorithms/union.hpp>
@ -70,6 +71,44 @@ inline void check_input_validity(std::string const& caseid, int case_index,
}
#endif
template
<
typename Geometry,
typename Tag = typename bg::tag<Geometry>::type
>
struct check_validity
{
static inline
bool apply(Geometry const& geometry, std::string& message)
{
if (! bg::is_valid(geometry, message))
{
std::cout << bg::wkt(geometry) << std::endl;
}
return bg::is_valid(geometry, message);
}
};
// Specialization for vector of <geometry> (e.g. rings)
template <typename Geometry>
struct check_validity<Geometry, void>
{
static inline
bool apply(Geometry const& geometry, std::string& message)
{
typedef typename boost::range_value<Geometry>::type single_type;
BOOST_FOREACH(single_type const& element, geometry)
{
if (! bg::is_valid(element, message))
{
return false;
}
}
return true;
}
};
template <typename Range>
inline std::size_t num_points(Range const& rng, bool add_for_open = false)
{
@ -121,6 +160,16 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2,
}
#endif
if (settings.test_validity)
{
std::string message;
bool const valid = check_validity<result_type>::apply(clip, message);
BOOST_CHECK_MESSAGE(valid,
"union: " << caseid << " not valid: " << message
<< " type: " << (type_for_assert_message<G1, G2>()));
}
typename bg::default_area_result<OutputType>::type area = 0;
std::size_t n = 0;
std::size_t holes = 0;
@ -130,16 +179,6 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2,
area += bg::area(*it);
holes += bg::num_interior_rings(*it);
n += bg::num_points(*it, true);
if (settings.test_validity)
{
// Check validity (currently on separate clips only)
// std::cout << bg::dsv(*it) << std::endl;
std::string message;
bool const valid = bg::is_valid(*it, message);
BOOST_CHECK_MESSAGE(valid,
"union: " << caseid << " not valid " << message);
}
}
@ -219,6 +258,9 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2,
<< string_from_type<coordinate_type>::name()
<< (ccw ? "_ccw" : "")
<< (open ? "_open" : "")
#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)
<< "_self"
#endif
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
<< "_no_rob"
#endif

View File

@ -32,9 +32,6 @@ void test_areal()
{
typedef typename bg::coordinate_type<Polygon>::type ct;
ut_settings ignore_validity;
ignore_validity.test_validity = false;
test_one<Polygon, Polygon, Polygon>("simplex_normal",
simplex_normal[0], simplex_normal[1],
1, 0, 13, 11.526367);
@ -381,9 +378,13 @@ void test_areal()
test_one<Polygon, Polygon, Polygon>("ticket_11725", ticket_11725[0], ticket_11725[1],
1, 1, 10, 7.5);
test_one<Polygon, Polygon, Polygon>("geos_1", geos_1[0], geos_1[1],
1, 0, -1, 3461.3203125,
ignore_validity);
{
ut_settings ignore_validity;
ignore_validity.test_validity = false;
test_one<Polygon, Polygon, Polygon>("geos_1", geos_1[0], geos_1[1],
1, 0, -1, 3461.3203125,
ignore_validity);
}
test_one<Polygon, Polygon, Polygon>("geos_2", geos_2[0], geos_2[1],
1, 0, -1, 350.55102539);
test_one<Polygon, Polygon, Polygon>("geos_3", geos_3[0], geos_3[1],
@ -397,24 +398,18 @@ void test_areal()
// Robustness issues, followed out buffer-robustness-tests, test them also reverse
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
test_one<Polygon, Polygon, Polygon>("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1],
1, 0, if_typed<ct, double>(18, 23), 4.60853,
ignore_validity);
1, 0, if_typed<ct, double>(18, 23), 4.60853);
test_one<Polygon, Polygon, Polygon>("buffer_rt_f_rev", buffer_rt_f[1], buffer_rt_f[0],
1, 0, if_typed<ct, double>(18, 23), 4.60853,
ignore_validity);
#endif
1, 0, if_typed<ct, double>(18, 23), 4.60853);
test_one<Polygon, Polygon, Polygon>("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1],
1, 0, if_typed<ct, float>(18, 17), 16.571);
test_one<Polygon, Polygon, Polygon>("buffer_rt_g_rev", buffer_rt_g[1], buffer_rt_g[0],
1, 0, if_typed<ct, float>(18, 17), 16.571);
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
test_one<Polygon, Polygon, Polygon>("buffer_rt_i", buffer_rt_i[0], buffer_rt_i[1],
1, 0, if_typed<ct, float>(14, 13), 13.6569);
#endif
test_one<Polygon, Polygon, Polygon>("buffer_rt_i_rev", buffer_rt_i[1], buffer_rt_i[0],
1, 0, 13, 13.6569);
#endif
test_one<Polygon, Polygon, Polygon>("buffer_rt_j", buffer_rt_j[0], buffer_rt_j[1],
1, 0, -1, 16.5711);
@ -438,12 +433,11 @@ void test_areal()
test_one<Polygon, Polygon, Polygon>("buffer_rt_m2_rev", buffer_rt_m2[1], buffer_rt_m2[0],
1, 0, if_typed_tt<ct>(20, 19), 21.4853);
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
test_one<Polygon, Polygon, Polygon>("buffer_rt_q", buffer_rt_q[0], buffer_rt_q[1],
1, 0, 18, 18.5710);
test_one<Polygon, Polygon, Polygon>("buffer_rt_q_rev", buffer_rt_q[1], buffer_rt_q[0],
1, 0, 18, 18.5710);
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
test_one<Polygon, Polygon, Polygon>("buffer_rt_r", buffer_rt_r[0], buffer_rt_r[1],
1, 0, 19, 21.07612);
test_one<Polygon, Polygon, Polygon>("buffer_rt_r_rev", buffer_rt_r[1], buffer_rt_r[0],
@ -462,7 +456,7 @@ void test_areal()
test_one<Polygon, Polygon, Polygon>("buffer_mp2", buffer_mp2[0], buffer_mp2[1],
#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
1, 0, 217, 36.752837, ignore_validity);
1, 0, 217, 36.752837);
#else
1, 1, 217, 36.752837);
#endif

View File

@ -33,15 +33,16 @@
(test_one<Polygon, MultiPolygon, MultiPolygon>) \
( #caseid, caseid[0], caseid[1], clips, holes, points, area)
#define TEST_UNION_IGNORE(caseid, clips, holes, points, area) \
{ ut_settings ignore_validity; ignore_validity.test_validity = false; \
test_one<Polygon, MultiPolygon, MultiPolygon> \
(#caseid, caseid[0], caseid[1], \
clips, holes, points, area, ignore_validity); }
template <typename Ring, typename Polygon, typename MultiPolygon>
void test_areal()
{
ut_settings ignore_validity;
ignore_validity.test_validity = false;
// Some output is only invalid for CCW
bool const ccw = bg::point_order<Polygon>::value == bg::counterclockwise;
test_one<Polygon, MultiPolygon, MultiPolygon>("simplex_multi",
case_multi_simplex[0], case_multi_simplex[1],
1, 0, 20, 14.58);
@ -72,12 +73,9 @@ void test_areal()
test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_a",
case_58_multi[0], case_58_multi[3],
2, 0, 21, 19.83333333);
// Was valid before but is invalid now that ii turns are discarded
// It should be validated in another way
test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b",
case_58_multi[1], case_58_multi[2],
1, 1, 17, 48.333333, ignore_validity);
1, 3, 17, 48.333333);
// Constructed cases for multi/touch/equal/etc
test_one<Polygon, MultiPolygon, MultiPolygon>("case_61_multi",
@ -132,14 +130,12 @@ void test_areal()
case_108_multi[0], case_108_multi[1],
1, 1, 20, 22.75);
// Should have 2 holes
// To make it valid, it is necessary to calculate and use self turns
// for each input. Now the two holes are connected because a turn is missing
// there.
test_one<Polygon, MultiPolygon, MultiPolygon>("case_109_multi",
case_109_multi[0], case_109_multi[1],
1, 1, 14, 1400,
ignore_validity);
// Should have 2 holes. Needs self turns
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_109_multi, 1, 2, 14, 1400);
#else
TEST_UNION_IGNORE(case_109_multi, 1, 1, 14, 1400);
#endif
// Should have 9 holes, they are all separate and touching
test_one<Polygon, MultiPolygon, MultiPolygon>("case_110_multi",
@ -188,6 +184,27 @@ void test_areal()
TEST_UNION(case_125_multi, 1, 0, 9, 2.75);
TEST_UNION(case_126_multi, 1, 2, 27, 52.0);
// Should have 2 holes. Needs self turns
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_131_multi, 1, 2, 15, 14.0);
#else
TEST_UNION_IGNORE(case_131_multi, 1, 1, 15, 14.0);
#endif
// SQL Server returns: MULTIPOLYGON (((4 4, 5.5 4.5, 6 6, 4.5 5.5, 4 4)), ((2 2, 3.5 2.5, 4 4, 2.5 3.5, 2 2)), ((0 0, 8 0, 8 8, 0 8, 0 0), (2 2, 2 4, 4 4, 4 6, 6 6, 6 4, 4 4, 4 2, 2 2)))
// Which is one self-connected hole with two island polygons in both parts, basically identical to what Boost.Geometry delivers
// PostGIS returns: MULTIPOLYGON(((0 0,0 8,8 8,8 0,0 0),(4 6,4 4,6 4,6 6,4 6)),((2 2,2.5 3.5,4 4,3.5 2.5,2 2),(4 4,2 4,2 2,4 2,4 4)),((4 4,4.5 5.5,6 6,5.5 4.5,4 4)))
// Which seems wrong because the second hole is part of a smaller polygon (?)
// ("POSTGIS="2.1.7 r13414" GEOS="3.5.0dev-CAPI-1.9.0 r4057")
TEST_UNION(case_132_multi, 3, 2, 26, 60.0);
TEST_UNION(case_133_multi, 2, 1, -1, 64.625);
TEST_UNION(case_134_multi, 1, 2, -1, 66.0);
TEST_UNION(case_135_multi, 1, 2, -1, 22.0);
TEST_UNION(case_136_multi, 1, 2, -1, 22.0);
TEST_UNION(case_137_multi, 1, 2, -1, 22.0);
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_1",
case_recursive_boxes_1[0], case_recursive_boxes_1[1],
1, 1, 36, 97.0);
@ -202,14 +219,12 @@ void test_areal()
case_recursive_boxes_4[0], case_recursive_boxes_4[1],
1, 2, 42, 96.75);
// Should have 10 holes.
// For making #5 valid, it is necessary to calculate and use self turns
// for each input. Now one hole is connected to another hole because a turn
// missing there.
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_5",
case_recursive_boxes_5[0], case_recursive_boxes_5[1],
3, 9, 115, 70.0,
ignore_validity);
// Should have 10 holes. Needs self turns
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_recursive_boxes_5, 3, 10, 118, 70.0);
#else
TEST_UNION_IGNORE(case_recursive_boxes_5, 3, 9, 115, 70.0);
#endif
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_6",
case_recursive_boxes_6[0], case_recursive_boxes_6[1],
@ -244,30 +259,15 @@ void test_areal()
case_recursive_boxes_14[0], case_recursive_boxes_14[1],
5, 0, -1, 4.5);
// Invalid versions of 12/13/14
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_12_invalid",
case_recursive_boxes_12_invalid[0], case_recursive_boxes_12_invalid[1],
6, 0, -1, 6.0);
if (! ccw)
{
// Handling this invalid input delivers invalid results for CCW
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_13_invalid",
case_recursive_boxes_13_invalid[0], case_recursive_boxes_13_invalid[1],
3, 0, -1, 10.25);
}
else
{
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_13_invalid",
case_recursive_boxes_13_invalid[0], case_recursive_boxes_13_invalid[1],
2, 0, -1, 10.25,
ignore_validity);
}
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_14_invalid",
case_recursive_boxes_14_invalid[0], case_recursive_boxes_14_invalid[1],
5, 0, -1, 4.5);
// 12, 13, 14 with invalid input. To make then valid it is necessary
// to break regions at self-intersection points (postponed)
TEST_UNION_IGNORE(case_recursive_boxes_12_invalid, 5, 0, -1, 6.0);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
// Without self-turns, a whole part is missed
TEST_UNION_IGNORE(case_recursive_boxes_13_invalid, 2, 0, -1, 10.25);
#endif
TEST_UNION_IGNORE(case_recursive_boxes_14_invalid, 4, 0, -1, 4.5);
test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_15",
case_recursive_boxes_15[0], case_recursive_boxes_15[1],
@ -344,6 +344,44 @@ void test_areal()
TEST_UNION(case_recursive_boxes_46, 1, 4, 51, 33.0);
TEST_UNION(case_recursive_boxes_47, 1, 0, -1, 22.0);
TEST_UNION(case_recursive_boxes_48, 1, 1, -1, 10.0);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_recursive_boxes_49, 1, 3, -1, 59.0);
#else
TEST_UNION_IGNORE(case_recursive_boxes_49, 1, 2, -1, 59.0);
#endif
TEST_UNION(case_recursive_boxes_50, 7, 4, -1, 68.0);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_recursive_boxes_51, 2, 6, -1, 75.0);
#else
TEST_UNION_IGNORE(case_recursive_boxes_51, 2, 5, -1, 75.0);
#endif
TEST_UNION(case_recursive_boxes_52, 2, 6, -1, 77.0);
TEST_UNION(case_recursive_boxes_53, 1, 1, -1, 24.75);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_recursive_boxes_54, 1, 2, -1, 22.5);
#else
TEST_UNION_IGNORE(case_recursive_boxes_54, 1, 1, -1, 22.5);
#endif
TEST_UNION(case_recursive_boxes_55, 3, 1, -1, 15.5);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(case_recursive_boxes_56, 5, 1, -1, 7.75);
#else
TEST_UNION_IGNORE(case_recursive_boxes_56, 5, 0, -1, 7.75);
#endif
TEST_UNION(case_recursive_boxes_57, 3, 4, -1, 19.75);
TEST_UNION(case_recursive_boxes_58, 6, 1, -1, 6.25);
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
// If there are no self-turns, an interior ring is missed
TEST_UNION(case_recursive_boxes_59, 1, 3, -1, 21.75);
#endif
TEST_UNION(case_recursive_boxes_60, 3, 0, -1, 20.5);
test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120915_h2_a",
ggl_list_20120915_h2[0], ggl_list_20120915_h2[1],
@ -383,17 +421,19 @@ void test_areal()
1, 0, -1, 575.831180350007);
#endif
// TODO: solve validity, it needs calculating self-turns
// Should have 1 hole
test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_23023665_7",
mysql_23023665_7[0], mysql_23023665_7[1],
1, 0, -1, 99.19494,
ignore_validity);
// Should have 2 holes
test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_23023665_8",
mysql_23023665_8[0], mysql_23023665_8[1],
1, 1, -1, 1400.0,
ignore_validity);
// Should have 1 hole. Needs self turns.
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(mysql_23023665_7, 1, 1, -1, 99.19494);
#else
TEST_UNION_IGNORE(mysql_23023665_7, 1, 0, -1, 99.19494);
#endif
// Should have 2 holes. Needs self turns.
#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS
TEST_UNION(mysql_23023665_8, 1, 2, -1, 1400.0);
#else
TEST_UNION_IGNORE(mysql_23023665_8, 1, 1, -1, 1400.0);
#endif
test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_23023665_9",
mysql_23023665_9[0], mysql_23023665_9[1],

View File

@ -7,6 +7,8 @@
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_GEOMETRY_INCLUDE_SELF_TURNS
#define BOOST_GEOMETRY_REPORT_OVERLAY_ERROR
#define BOOST_GEOMETRY_NO_BOOST_TEST
@ -160,6 +162,7 @@ int main(int argc, char** argv)
("seed", po::value<int>(&seed), "Initialization seed for random generator")
("count", po::value<int>(&count)->default_value(1), "Number of tests")
("diff", po::value<bool>(&settings.also_difference)->default_value(false), "Include testing on difference")
("validity", po::value<bool>(&settings.validity)->default_value(true), "Include testing on validity")
("level", po::value<int>(&level)->default_value(3), "Level to reach (higher->slower)")
("size", po::value<int>(&field_size)->default_value(10), "Size of the field")
("form", po::value<std::string>(&form)->default_value("box"), "Form of the polygons (box, triangle)")

View File

@ -34,18 +34,21 @@
#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
#include <boost/geometry/algorithms/intersects.hpp>
#include <boost/geometry/algorithms/is_valid.hpp>
#include <boost/geometry/algorithms/touches.hpp>
struct p_q_settings
{
bool svg;
bool also_difference;
bool validity;
bool wkt;
double tolerance;
p_q_settings()
: svg(false)
, also_difference(false)
, validity(false)
, wkt(false)
, tolerance(1.0e-3) // since rescaling to integer the tolerance should be less. Was originally 1.0e-6
{}
@ -74,7 +77,7 @@ static bool test_overlay_p_q(std::string const& caseid,
typedef typename bg::coordinate_type<G1>::type coordinate_type;
typedef typename bg::point_type<G1>::type point_type;
bg::model::multi_polygon<OutputType> out_i, out_u, out_d, out_d2;
bg::model::multi_polygon<OutputType> out_i, out_u, out_d1, out_d2;
CalculationType area_p = p_q_area(p);
CalculationType area_q = p_q_area(q);
@ -92,9 +95,9 @@ static bool test_overlay_p_q(std::string const& caseid,
if (settings.also_difference)
{
bg::difference(p, q, out_d);
bg::difference(p, q, out_d1);
bg::difference(q, p, out_d2);
area_d1 = p_q_area(out_d);
area_d1 = p_q_area(out_d1);
area_d2 = p_q_area(out_d2);
double sum_d1 = (area_u - area_q) - area_d1;
double sum_d2 = (area_u - area_p) - area_d2;
@ -107,6 +110,34 @@ static bool test_overlay_p_q(std::string const& caseid,
}
}
if (settings.validity)
{
std::string message;
if (! bg::is_valid(out_u, message))
{
std::cout << "Union is not valid: " << message << std::endl;
wrong = true;
}
if (! bg::is_valid(out_i, message))
{
std::cout << "Intersection is not valid: " << message << std::endl;
wrong = true;
}
if (settings.also_difference)
{
if (! bg::is_valid(out_d1, message))
{
std::cout << "Difference (p-q) is not valid: " << message << std::endl;
wrong = true;
}
if (! bg::is_valid(out_d2, message))
{
std::cout << "Difference (q-p) is not valid: " << message << std::endl;
wrong = true;
}
}
}
if (true)
{
if ((area_i > 0 && bg::touches(p, q))
@ -181,7 +212,7 @@ static bool test_overlay_p_q(std::string const& caseid,
if (settings.also_difference)
{
for (BOOST_AUTO(it, out_d.begin()); it != out_d.end(); ++it)
for (BOOST_AUTO(it, out_d1.begin()); it != out_d1.end(); ++it)
{
mapper.map(*it,
"opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
@ -194,16 +225,10 @@ static bool test_overlay_p_q(std::string const& caseid,
}
else
{
for (BOOST_AUTO(it, out_i.begin()); it != out_i.end(); ++it)
{
mapper.map(*it, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,0);"
"stroke:rgb(255,0,0);stroke-width:4");
}
for (BOOST_AUTO(it, out_u.begin()); it != out_u.end(); ++it)
{
mapper.map(*it, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,0);"
"stroke:rgb(255,0,255);stroke-width:4");
}
mapper.map(out_i, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,128);"
"stroke:rgb(255,0,0);stroke-width:4");
mapper.map(out_u, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,0);"
"stroke:rgb(255,0,255);stroke-width:4");
}
}
return result;

View File

@ -299,7 +299,7 @@ inline void to_svg(G const& g, std::string const& filename, bool /*sort*/ = true
bg::detail::self_get_turn_points::get_turns
<
TurnPolicy
false, TurnPolicy
>::apply(g, bg::detail::no_rescale_policy(), turns, interrupt_policy);
turns_to_svg<G>(turns, mapper);