diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index dd64261b05..4a47318f38 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1338,6 +1339,11 @@ struct buffered_piece_collection traverser::apply(offsetted_rings, offsetted_rings, detail::overlay::operation_union, m_robust_policy, m_turns, traversed_rings); + + overlay::split_rings + < + overlay_union + >::apply(traversed_rings, m_robust_policy); } inline void reverse() diff --git a/include/boost/geometry/algorithms/detail/overlay/insert_touch_interior_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/insert_touch_interior_turns.hpp new file mode 100644 index 0000000000..bf1becf659 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/insert_touch_interior_turns.hpp @@ -0,0 +1,375 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INSERT_TOUCH_INTERIOR_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INSERT_TOUCH_INTERIOR_TURNS_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace detail { namespace overlay +{ + + +struct self_turns_no_interrupt_policy +{ + static bool const enabled = false; + static bool const has_intersections = false; + + template + static inline bool apply(Range const&) + { + return false; + } +}; + + +template +static inline +typename Turn::turn_operation_type get_correct_op(Turn const& t) +{ + return + (t.operations[0].fraction.is_zero() + || t.operations[0].fraction.is_one()) + ? + t.operations[1] + : + t.operations[0] + ; +} + + +template +struct maa_turn_less +{ + bool operator()(MAA_Turn const& t1, MAA_Turn const& t2) const + { + BOOST_GEOMETRY_ASSERT(t1.method == method_touch_interior); + BOOST_GEOMETRY_ASSERT(t2.method == method_touch_interior); + + typename MAA_Turn::turn_operation_type op1 = get_correct_op(t1); + typename MAA_Turn::turn_operation_type op2 = get_correct_op(t2); + + BOOST_GEOMETRY_ASSERT(! op1.fraction.is_zero() + && ! op1.fraction.is_one()); + BOOST_GEOMETRY_ASSERT(! op2.fraction.is_zero() + && ! op2.fraction.is_one()); + + + if (op1.seg_id.multi_index != op2.seg_id.multi_index) + { + return op1.seg_id.multi_index < op2.seg_id.multi_index; + } + if (op1.seg_id.ring_index != op2.seg_id.ring_index) + { + return op1.seg_id.ring_index < op2.seg_id.ring_index; + } + if (op1.seg_id.segment_index != op2.seg_id.segment_index) + { + return op1.seg_id.segment_index < op2.seg_id.segment_index; + } + return op1.fraction < op2.fraction; + } +}; + + +template ::type> +struct insert_maa_turns +{}; + +template +struct insert_maa_turns +{ + template + static inline TurnIterator apply(Box const& box, + TurnIterator first, + TurnIterator, + BoxOut& box_out, + int = -1, + int = -1) + { + geometry::convert(box, box_out); + return first; + } +}; + + +template +struct insert_maa_turns +{ + template + static inline TurnIterator apply(Ring const& ring, + TurnIterator first, + TurnIterator last, + RingOut& ring_out, + int ring_index = -1, + int multi_index = -1) + { + typedef typename boost::range_iterator + < + Ring const + >::type iterator_type; + + typedef typename std::iterator_traits + < + TurnIterator + >::value_type::turn_operation_type operation_type; + + typename boost::range_size::type point_index = 0; + for (iterator_type it = boost::begin(ring); + it != boost::end(ring); + ++it, ++point_index) + { + geometry::append(ring_out, ring[point_index]); + while (first != last) + { + operation_type op = get_correct_op(*first); + if (op.seg_id.multi_index == multi_index + && + op.seg_id.ring_index == ring_index + && + op.seg_id.segment_index + == static_cast(point_index)) + { + geometry::append(ring_out, first->point); + ++first; + } + else + { + break; + } + } + } + return first; + } +}; + + +template +struct insert_maa_turns +{ + template + < + typename Ring, + typename RingIterator, + typename PolygonOut, + typename TurnIterator + > + static inline + TurnIterator do_interior_rings(RingIterator rings_first, + RingIterator rings_last, + TurnIterator first, + TurnIterator last, + PolygonOut& polygon_out, + int multi_index) + { + typename geometry::ring_type::type ring_out; + int ring_index = 0; + for (RingIterator rit = rings_first; + rit != rings_last; + ++rit, ++ring_index) + { + geometry::clear(ring_out); + first = insert_maa_turns::apply(*rit, + first, + last, + ring_out, + ring_index, + multi_index); + geometry::traits::push_back + < + typename boost::remove_reference + < + typename traits::interior_mutable_type + < + PolygonOut + >::type + >::type + >::apply(geometry::interior_rings(polygon_out), ring_out); + } + return first; + } + + template + < + typename Ring, + typename InteriorRings, + typename PolygonOut, + typename TurnIterator + > + static inline + TurnIterator do_interior_rings(InteriorRings const& irings, + TurnIterator first, + TurnIterator last, + PolygonOut& polygon_out, + int multi_index) + { + return do_interior_rings(boost::begin(irings), + boost::end(irings), + first, + last, + polygon_out, + multi_index); + } + + template + static inline TurnIterator apply(Polygon const& polygon, + TurnIterator first, + TurnIterator last, + PolygonOut& polygon_out, + int multi_index = -1) + { + typedef typename geometry::ring_type::type ring_type; + + typename geometry::ring_type::type ring_out; + + // do exterior ring + first = insert_maa_turns + < + ring_type + >::apply(exterior_ring(polygon), + first, + last, + ring_out, + -1, + multi_index); + geometry::append(polygon_out, ring_out, -1); + + return do_interior_rings(interior_rings(polygon), + first, + last, + polygon_out, + multi_index); + } +}; + +template +struct insert_maa_turns +{ + template + static inline TurnIterator apply(MultiPolygon const& multi_polygon, + TurnIterator first, + TurnIterator last, + MultiPolygonOut& multi_polygon_out) + { + typedef typename boost::range_iterator + < + MultiPolygon const + >::type iterator_type; + + typename boost::range_value::type polygon_out; + + int polygon_index = 0; + for (iterator_type it = boost::begin(multi_polygon); + it != boost::end(multi_polygon); + ++it, ++polygon_index) + { + geometry::clear(polygon_out); + + first = insert_maa_turns + < + typename boost::range_value::type + >::apply(*it, first, last, polygon_out, polygon_index); + + geometry::traits::push_back + < + MultiPolygonOut + >::apply(multi_polygon_out, polygon_out); + } + return first; + } +}; + + +// returns true if the input geometry has been modified (in which case +// the modified geometry is stored in geometry_out), false otherwise +template +inline bool insert_touch_interior_turns(GeometryIn const& geometry_in, + GeometryOut& geometry_out, + RobustPolicy const& robust_policy) +{ + typedef turn_info + < + typename point_type::type, + typename geometry::segment_ratio_type + < + typename point_type::type, RobustPolicy + >::type + > turn_type; + + typedef std::deque turns_container_type; + + self_turns_no_interrupt_policy interrupt_policy; + + // compute self turns + turns_container_type turns; + geometry::self_turns + < + get_turn_info + >(geometry_in, robust_policy, turns, interrupt_policy); + + // select touch interior turns + typedef std::set > maa_turn_set; + + maa_turn_set maa_turns; + for (typename turns_container_type::const_iterator it = turns.begin(); + it != turns.end(); + ++it) + { + if (it->method == method_touch_interior) + { + maa_turns.insert(*it); + } + } + + // if not such turns indicate that the input geometry need not change + if (maa_turns.empty()) + { + return false; + } + + // insert the touch interior turns + insert_maa_turns + < + GeometryIn + >::apply(geometry_in, maa_turns.begin(), maa_turns.end(), geometry_out); + + // return that the input geometry needed change + return true; +} + + +}} // detail::overlay + +}} // boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INSERT_TOUCH_INTERIOR_TURNS_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index baf9d4777d..0a923ed380 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -38,8 +39,11 @@ #include #include +#include #include #include +#include +#include #include #include @@ -157,31 +161,16 @@ template typename GeometryOut, overlay_type Direction > -struct overlay +class overlay { +private: template - static inline OutputIterator apply( + static inline OutputIterator do_overlay( Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { - bool const is_empty1 = geometry::is_empty(geometry1); - bool const is_empty2 = geometry::is_empty(geometry2); - - if (is_empty1 && is_empty2) - { - return out; - } - - if (is_empty1 || is_empty2) - { - return return_if_one_input_is_empty - < - GeometryOut, Direction, ReverseOut - >(geometry1, geometry2, out); - } - typedef typename geometry::point_type::type point_type; typedef detail::overlay::traversal_turn_info < @@ -249,6 +238,9 @@ std::cout << "traverse" << std::endl; select_rings(geometry1, geometry2, turn_info_per_ring, selected_ring_properties); + // split the rings into simple rings + split_rings::apply(rings, robust_policy); + // Add rings created during traversal { ring_identifier id(2, 0, -1); @@ -267,6 +259,62 @@ std::cout << "traverse" << std::endl; return add_rings(selected_ring_properties, geometry1, geometry2, rings, out); } + +public: + template + static inline OutputIterator apply( + Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + bool const is_empty1 = geometry::is_empty(geometry1); + bool const is_empty2 = geometry::is_empty(geometry2); + + if (is_empty1 && is_empty2) + { + return out; + } + + if (is_empty1 || is_empty2) + { + return return_if_one_input_is_empty + < + GeometryOut, Direction, ReverseOut + >(geometry1, geometry2, out); + } + + Geometry1 modified_geometry1; + bool modified1 = insert_touch_interior_turns(geometry1, + modified_geometry1, + robust_policy); + + Geometry2 modified_geometry2; + bool modified2 = insert_touch_interior_turns(geometry2, + modified_geometry2, + robust_policy); + + if (modified1 && modified2) + { + return do_overlay(modified_geometry1, modified_geometry2, + robust_policy, out, strategy); + } + else if (! modified1 && modified2) + { + return do_overlay(geometry1, modified_geometry2, + robust_policy, out, strategy); + } + else if (modified1 && ! modified2) + { + return do_overlay(modified_geometry1, geometry2, + robust_policy, out, strategy); + } + else + { + return do_overlay(geometry1, geometry2, + robust_policy, out, strategy); + } + } }; diff --git a/include/boost/geometry/algorithms/detail/overlay/split_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/split_rings.hpp new file mode 100644 index 0000000000..5833665ba7 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/split_rings.hpp @@ -0,0 +1,491 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SPLIT_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SPLIT_RINGS_HPP + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace detail { namespace overlay +{ + + +// the ring_as_dcl class implements a ring using a doubly-connected list; +// it is a model of a bidirectional-traversal Boost.Range and +// supports: +// (1) constant-time push_back(), size() and empty() operations; +// (2) splitting at two vertices (in order to create two rings out of +// the initial ring) also in constant time. +// +// The second operation cannot be done in constant time using typical +// random access ranges, which is precisely the reason why this class +// has been introduced and used. +template +< + typename Point, + closure_selector Closure, + typename List = std::list +> +class ring_as_dcl +{ +public: + typedef Point point_type; + typedef List list_type; + typedef typename List::size_type size_type; + typedef typename List::iterator iterator; + typedef typename List::const_iterator const_iterator; + + ring_as_dcl() + : m_list() + {} + + inline void push_back(Point const& point) + { + m_list.push_back(point); + } + + inline void split_at(iterator pos1, iterator pos2, ring_as_dcl& other) + { + // for this method to work, I think that the iterator pos1 + // must preceed iterator pos2 in the original list node sequence + other.m_list.splice(other.m_list.end(), m_list, pos1, pos2); + + if (BOOST_GEOMETRY_CONDITION(Closure == closed)) + { + other.m_list.push_back(*other.m_list.begin()); + } + } + + inline iterator begin() { return m_list.begin(); } + inline const_iterator begin() const { return m_list.begin(); } + + inline iterator end() { return m_list.end(); } + inline const_iterator end() const { return m_list.end(); } + + inline void swap(ring_as_dcl& other) + { + m_list.swap(other.m_list); + } +private: + List m_list; +}; + + +template +class find_duplicate_points +{ + static bool const is_closed = Closure == closed; + + struct point_iterator_less + { + template + inline bool operator()(PointIterator it1, PointIterator it2) const + { + return geometry::less + < + typename std::iterator_traits::value_type + >()(*it1, *it2); + } + }; + +public: + template + static inline bool apply(Ring const& ring, Iterator& pos1, Iterator& pos2) + { + typedef typename Ring::iterator iterator_type; + typedef std::set point_set_type; + point_set_type point_set; + + Ring& nc_ring = const_cast(ring); + iterator_type last + = is_closed ? --boost::end(nc_ring) : boost::end(nc_ring); + for (iterator_type it = boost::begin(nc_ring); it != last; ++it) + { + std::pair res + = point_set.insert(it); + + if (! res.second) + { + pos1 = *res.first; + pos2 = it; + return true; + } + } + + // initialize pos1 and pos2 with something + pos1 = boost::begin(nc_ring); + pos2 = pos1; + return false; + } +}; + + +template +struct split_ring +{ + template + static inline void apply(Ring const& ring, + RingCollection& collection, + RobustPolicy const& robust_policy) + { + split_ring + < + overlay_union, Ring, RobustPolicy + >::apply(ring, collection, robust_policy); + } +}; + +// specialization for union +// TODO: add another specialization for intersection once implemented +template +class split_ring +{ + typedef turn_info + < + typename point_type::type, + typename geometry::segment_ratio_type + < + typename point_type::type, RobustPolicy + >::type + > turn_type; + + typedef std::deque turns_container_type; + + struct no_interrupt_policy + { + static bool const enabled = false; + static bool const has_intersections = false; + + template + static inline bool apply(Range const&) + { + return false; + } + }; + + template + static inline + typename Turn::turn_operation_type get_correct_op(Turn const& t) + { + return + (t.operations[0].fraction.is_zero() + || t.operations[0].fraction.is_one()) + ? + t.operations[1] + : + t.operations[0] + ; + } + + template + struct maa_turn_less + { + bool operator()(MAA_Turn const& t1, MAA_Turn const& t2) const + { +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + if (t1.method != method_touch_interior + || + (! t1.both(operation_union) + && ! t1.both(operation_intersection)) + || + t2.method != method_touch_interior + || + (! t2.both(operation_union) + && ! t2.both(operation_intersection)) + ) + { + throw inconsistent_turns_exception(); + } +#else + BOOST_GEOMETRY_ASSERT(t1.method == method_touch_interior); + BOOST_GEOMETRY_ASSERT(t1.both(operation_union) + || + t1.both(operation_intersection)); + BOOST_GEOMETRY_ASSERT(t2.method == method_touch_interior); + BOOST_GEOMETRY_ASSERT(t2.both(operation_union) + || + t2.both(operation_intersection)); +#endif + + typename MAA_Turn::turn_operation_type op1 = get_correct_op(t1); + typename MAA_Turn::turn_operation_type op2 = get_correct_op(t2); + + BOOST_GEOMETRY_ASSERT(! op1.fraction.is_zero() + && ! op1.fraction.is_one()); + BOOST_GEOMETRY_ASSERT(! op2.fraction.is_zero() + && ! op2.fraction.is_one()); + + + if (op1.seg_id.segment_index != op2.seg_id.segment_index) + { + return op1.seg_id.segment_index < op2.seg_id.segment_index; + } + return op1.fraction < op2.fraction; + } + }; + + template + static inline void get_self_turns(Ring const& ring, + turns_container_type& turns, + RobustPolicy const& robust_policy, + InterruptPolicy const& policy) + { + geometry::self_turns + < + get_turn_info + >(ring, robust_policy, turns, policy); + } + + static inline void get_self_turns(Ring const& ring, + turns_container_type& turns, + RobustPolicy const& robust_policy) + { + get_self_turns(ring, turns, robust_policy, no_interrupt_policy()); + } + + template + static inline void insert_maa_turns(Ring const& ring, + MAA_Turns const& maa_turns, + RingOut& ring_out) + { + typedef typename boost::range_size::type size_type; + typedef typename boost::range_iterator + < + MAA_Turns const + >::type iterator_type; + + size_type point_index = 0; + for (iterator_type it = maa_turns.begin(); it != maa_turns.end(); ++it) + { + signed_size_type segment_index + = get_correct_op(*it).seg_id.segment_index; + + while (point_index <= static_cast(segment_index)) + { + ring_out.push_back(ring[point_index]); + ++point_index; + } + + ring_out.push_back(it->point); + } + + while (point_index < ring.size()) + { + ring_out.push_back(ring[point_index]); + ++point_index; + } + } + + template + static inline void copy_to_collection(RingIn const& ring, + Collection& collection) + { + typedef typename boost::range_value::type ring_out_type; + + ring_out_type tmp; + for (typename boost::range_iterator::type + it = ring.begin(); + it != ring.end(); + ++it) + { + geometry::traits::push_back::apply(tmp, *it); + } + collection.push_back(tmp); + } + + template + static inline void move_to_top(Stack& stack, + typename Stack::value_type& value) + { + typedef typename Stack::value_type value_type; + stack.push(value_type()); + stack.top().swap(value); + } + + template + static inline void split_one_ring(RingType& ring, Collection& collection) + { + // create and initialize a stack with the input ring + std::stack stack; + move_to_top(stack, ring); + + // while the stack is not empty: + // look for duplicates, split and push to stack; + // otherwise, copy to output collection + while (! stack.empty()) + { + RingType& top_ring = stack.top(); + + typename boost::range_iterator::type pos1, pos2; + bool duplicate_found = find_duplicate_points + < + Closure + >::apply(top_ring, pos1, pos2); + + if (duplicate_found) + { + RingType other_ring; + top_ring.split_at(pos1, pos2, other_ring); + move_to_top(stack, other_ring); + } + else + { + copy_to_collection(top_ring, collection); + stack.pop(); + } + } + } + +public: + template + static inline void apply(Ring const& ring, + RingCollection& collection, + RobustPolicy const& robust_policy) + { + typedef std::set > maa_turn_set; + typedef ring_as_dcl + < + typename point_type::type, closure::value + > ring_dcl_type; + + + // compute the ring's self turns + turns_container_type turns; + get_self_turns(ring, turns, robust_policy); + + // collect the ring's m:u/u and m:i/i turns (the latter can + // appear when we perform an intersection and the intersection + // result consists of a multipolygon whose polygons touch each + // other); + // notice the use of std::set; we want to record coinciding + // m:u/u and m:i/i turns only once + maa_turn_set maa_turns; + for (typename turns_container_type::const_iterator it = turns.begin(); + it != turns.end(); + ++it) + { + if (it->method == method_touch_interior) + { +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + if (! it->both(operation_union) + && + ! it->both(operation_intersection)) + { + throw inconsistent_turns_exception(); + } +#else + BOOST_GEOMETRY_ASSERT(it->both(operation_union) + || + it->both(operation_intersection)); +#endif + maa_turns.insert(*it); + } + } + + // insert the m:u/u turns as points in the original ring + ring_dcl_type output; + insert_maa_turns(ring, maa_turns, output); + + // split the ring into simple rings + split_one_ring::value>(output, collection); + } +}; + + +template +struct split_rings +{ + template + static inline void apply(RingCollection& collection, + RobustPolicy const& robust_policy) + { + typedef typename boost::range_iterator + < + RingCollection + >::type ring_iterator_type; + + RingCollection new_collection; + for (ring_iterator_type rit = boost::begin(collection); + rit != boost::end(collection); + ++rit) + { + split_ring + < + OverlayType, + typename boost::range_value::type, + RobustPolicy + >::apply(*rit, new_collection, robust_policy); + } + collection.swap(new_collection); + } +}; + +// specialization for union +// TODO: add another specialization for intersection once implemented +template <> +struct split_rings +{ + template + static inline void apply(RingCollection& collection, + RobustPolicy const& robust_policy) + { + typedef typename boost::range_iterator + < + RingCollection + >::type ring_iterator_type; + + RingCollection new_collection; + for (ring_iterator_type rit = boost::begin(collection); + rit != boost::end(collection); + ++rit) + { + split_ring + < + overlay_union, + typename boost::range_value::type, + RobustPolicy + >::apply(*rit, new_collection, robust_policy); + } + collection.swap(new_collection); + } +}; + + +}} // namespace detail::overlay + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SPLIT_RINGS_HPP diff --git a/test/algorithms/buffer/buffer_multi_linestring.cpp b/test/algorithms/buffer/buffer_multi_linestring.cpp index 6501d131ad..20fcb18d85 100644 --- a/test/algorithms/buffer/buffer_multi_linestring.cpp +++ b/test/algorithms/buffer/buffer_multi_linestring.cpp @@ -139,6 +139,8 @@ void test_all() mysql_2015_09_08b, join_round32, end_round32, 1.32832149026508268e+19, 2061380362.0, same_distance, true, 1.0e12); + + test_one("mysql_report_2015_09_21", "MULTILINESTRING((-5 15,7 15,19 -10,-11 -2),(2 13,2 -9))", join_round32, end_round32, 186.550431076137, 1.0); } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index fb9dd8b824..e040338dfa 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -345,6 +345,23 @@ static std::string case_107_multi[2] = "MULTIPOLYGON(((5 7,6 8,6 10,7 9,8 10,8 8,7 8,6 7,6 6,5 7)))" }; +static std::string case_108_multi[2] = + { + "MULTIPOLYGON(((0 0,0 40,40 40,40 0,0 0),(10 10,30 10,30 30,10 30,10 10)))", + "MULTIPOLYGON(((10 10,10 20,20 10,10 10)),((20 10,30 20,30 10,20 10)),((10 20,10 30,20 20,10 20)),((20 20,30 30,30 20,20 20)))" + }; + +static std::string case_109_multi[2] = + { + "MULTIPOLYGON(((0 0,0 40,40 40,40 0,0 0),(10 10,30 10,30 30,10 30,10 10)))", + "MULTIPOLYGON(((15 10,10 15,10 17,15 10)),((15 10,10 20,10 22,15 10)),((15 10,10 25,10 27,15 10)),((25 10,30 17,30 15,25 10)),((25 10,30 22,30 20,25 10)),((25 10,30 27,30 25,25 10)),((18 10,20 30,19 10,18 10)),((21 10,20 30,22 10,21 10)))" + }; + +static std::string case_110_multi[2] = + { + "MULTIPOLYGON(((4 5,12 11,-12 -3,4 5)))", + "MULTIPOLYGON(((5 4,-14 0,1 0,5 4)),((1 6,13 0,10 12,1 6)))" + }; static std::string case_recursive_boxes_1[2] = { diff --git a/test/algorithms/overlay/overlay_cases.hpp b/test/algorithms/overlay/overlay_cases.hpp index 1a28e0c067..a82a2165ed 100644 --- a/test/algorithms/overlay/overlay_cases.hpp +++ b/test/algorithms/overlay/overlay_cases.hpp @@ -180,6 +180,7 @@ static std::string case_35[2] = { "POLYGON((2 2,4 2,4 1,2 2))" }; static std::string case_36[2] = { + // union as one polygon with a hole touching the exterior ring "POLYGON((1 0,0 3,4 2,1 0))", "POLYGON((1 5,5 5,4 2,3 3,2 1,1 2,1 5))" }; @@ -353,6 +354,159 @@ static std::string case_79[2] = { "POLYGON((0 0,0 5,5 5,5 0,2 0,2 2,1 2,1 0,0 0))" }; +static std::string case_80[2] = + { + // union has one polygon with two holes; one of them is + // touching the exterior ring + // reported by MySQL QA on Aug 19, 2015 + "POLYGON((0 6,-11 -6,6 0,0 6),(3 1,5 0,-2 0,3 1))", + "POLYGON((5 4,6 0,9 12,-7 -12,5 -19,5 4))" + }; + +static std::string case_81[2] = + { + // union has a polygon with one hole touching the exterior ring + "POLYGON((0 0,10 10,20 0,0 0))", + "POLYGON((10 5,30 10,20 0,20 5,10 5))" + }; + +static std::string case_82[2] = + { + "POLYGON((0 0,10 10,20 0,0 0))", + "POLYGON((10 10,30 10,20 0,20 5,10 10))" + }; + +static std::string case_83[2] = + { + // union as a single polygon and two holes both touching the + // exterior ring at vertices + "POLYGON((0 0,10 10,20 0,0 0))", + "POLYGON((10 5,20 7,10 10,30 10,20 0,20 5,10 5))" + }; + +static std::string case_84[2] = + { + "POLYGON((0 0,10 10,20 0,0 0))", + "POLYGON((15 5,20 7,10 10,30 10,20 0,20 5,15 5))" + }; + +static std::string case_85[2] = + { + // union has a single polygon and two holes that touch each + // other at a vertex + "POLYGON((0 0,0 40,40 40,40 0,0 0),(10 10,30 10,30 30,10 30,10 10))", + "POLYGON((5 15,5 30,30 15,5 15))" + }; + +static std::string case_86[2] = + { + "POLYGON((0 0,0 40,40 40,40 0,20 0,0 0),(10 10,20 0,30 10,30 30,10 30,10 10))", + "POLYGON((10 10,10 30,30 30,30 10,10 10))" + }; + +static std::string case_87[2] = + { + "POLYGON((0 5,-6 -17,12 17,0 5),(4 6,5 5,0 1,4 6))", + "POLYGON((3 9,-15 -5,13 -11,3 9))" + }; + +static std::string case_88[2] = + { + "POLYGON((5 6,-15 -13,1 -8,5 6))", + "POLYGON((0 8,-19 6,18 -17,20 8,11 17,0 8),(3 2,3 -1,1 0,3 2),(1 3,4 4,0 -1,1 3))" + }; + +static std::string case_89[2] = + { + "POLYGON((0 0,0 40,40 40,40 0,0 0),(10 10,20 19,20 20,10 10),(20 20,30 30,20 21,20 20))", + "POLYGON((10 10,10 30,30 30,30 10,10 10))" + }; + + +static std::string case_90[2] = + { + "POLYGON((1 8,-17 -13,19 -9,1 8))", + "POLYGON((8 6,5 7,-1 4,-8 -7,0 -17,8 6),(3 6,5 5,0 -2,3 6))" + }; + +static std::string case_91[2] = + { + "POLYGON((0 0,50 0,50 50,25 50,0 50,0 0),(25 50,40 40,10 40,25 50))", + "POLYGON((20 20,20 60,30 60,30 20,20 20))" + }; + +static std::string case_92[2] = + { + "POLYGON((0 0,50 0,50 50,25 50,0 50,0 0),(25 50,40 40,10 40,25 50))", + "POLYGON((5 20,5 60,30 60,30 20,5 20))" + }; + +static std::string case_93[2] = + { + "POLYGON((0 0,50 0,50 50,24 50,26 50,0 50,0 0),(24 50,24 40,10 40,24 50),(26 50,40 40,26 40,26 50))", + "POLYGON((20 20,20 60,30 60,30 20,20 20))" + }; + +static std::string case_94[2] = + { + "POLYGON((0 0,50 0,50 50,24 50,26 50,0 50,0 0),(24 50,24 40,10 40,24 50),(26 50,40 40,26 40,26 50))", + "POLYGON((5 20,5 60,30 60,30 20,5 20))" + }; + +static std::string case_95[2] = + { + "POLYGON((0 0,50 0,50 50,25 50,0 50,0 0),(25 50,24 40,10 40,25 50),(25 50,40 40,26 40,25 50))", + "POLYGON((20 20,20 60,30 60,30 20,20 20))" + }; + +static std::string case_96[2] = + { + "POLYGON((0 0,50 0,50 50,25 50,0 50,0 0),(24 50,25 40,10 40,24 50),(26 50,40 40,25 40,26 50))", + "POLYGON((20 20,20 60,30 60,30 20,20 20))" + }; + +static std::string case_97[2] = + { + "POLYGON((6 7,18 14,-8 1,0 0,18 -8,6 7),(6 0,-4 3,5 3,6 0))", + "POLYGON((2 3,-3 5,-10 -1,2 3))" + }; + +static std::string case_98[2] = + { + "POLYGON((6 7,18 14,-8 1,0 0,18 -8,6 7),(6 0,-4 3,5 3,6 0))", + "POLYGON((0 7,-5 6,11 -13,0 7))" + }; + +static std::string case_99[2] = + { + "POLYGON((1 1,1 2398046010000,30000 2398046010000,30000 1,1 1))", + "POLYGON((8000 10000,8000 12000,120115188075850000 12000,120115188075850000 10000,8000 10000))" + }; + +static std::string case_100[2] = + { + "POLYGON((-28011 -32449,-28011 4652.2100,2667 4652.2100,2667 -32449,-28011 -32449))", + "POLYGON((-8560 68,-8560 2305843009213693948,-2087 2305843009213693948,-2087 68,-8560 68))" + }; + +static std::string case_101[2] = + { + "POLYGON((2 5514.6191,2 28951,17237 28951,17237 5514.6191,2 5514.6191))", + "POLYGON((8128 -20054,8128 144115188075855874,11192 144115188075855874,11192 -20054,8128 -20054))" + }; + +static std::string case_102[2] = + { + "POLYGON((-15032 -2251799813685244,-15032 8388611,16781 8388611,16781 -2251799813685244,-15032 -2251799813685244))", + "POLYGON((-21087 20243,-21087 31851,18514 31851,18514 20243,-21087 20243))" + }; + +static std::string case_103[2] = + { + "POLYGON((8 8,-14 1,-6 3,8 8))", + "POLYGON((6 6,3 14,-9 7,6 6))" + }; + static std::string case_many_situations[2] = { "POLYGON((2 6,2 14,10 18,18 14,18 6,16 5,14 4,12 3,10 2,8 3,6 4,4 5,2 6))", "POLYGON((2 6,2 7,2 8,2 9,2 10,2 11,2 12,1 14" diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index dd20682310..d6d233baa3 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -411,6 +411,50 @@ void test_all() 8, 36, 2.43452380952381, 7, 33, 3.18452380952381); + test_one( + "case97", case_97[0], case_97[1], + 2, 15, 105.6875598, + 3, 14, 10.18755981); + test_validity("case97", case_97[0], case_97[1]); + +#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + test_one( + "case98", case_98[0], case_98[1], + 3, 20, 99.7452558, + 3, 14, 37.7452558); +#else + test_one( + "case98", case_98[0], case_98[1], + 3, 20, 99.7452558, + 3, 14, 37.7452558, + 5, 33, 99.7452558 + 37.7452558); +#endif + test_validity("case98", case_98[0], case_98[1]); + +#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + test_one( + "case99", case_99[0], case_99[1], + 1, 9, 71938982209960000, + 1, 5, 2.40230376e+20); + test_one( + "case100", case_100[0], case_100[1], + 1, 9, 1108087364.695, + 1, 5, 1.49257218e+22, + 1, 10, 1108087364.695); + test_one( + "case101", case_101[0], case_101[1], + 2, 10, 332066650.1372, + 2, 10, 4.41568936e+20, + 2, 15, 410392630.5984); + test_one( + "case102", case_102[0], case_102[1], + 2, 10, 7.16365077392662e19, + 2, 10, 90403104.0, + 2, 18, 7.16365077392662e19); + test_validity( + "case102", case_102[0], case_102[1]); +#endif // BOOST_GEOMETRY_NO_ROBUSTNESS + test_one("winded", winded[0], winded[1], 3, 37, 61, diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 09ba7f74e3..9c95430777 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -91,6 +91,12 @@ void test_areal() case_78_multi[0], case_78_multi[1], 1, 5, 1.0, 1, 5, 1.0); + test_one("case_110_multi", + case_110_multi[0], case_110_multi[1], + 3, 13, 6.194942132, 4, 20, 83.194942); + test_validity("case_110_multi", + case_110_multi[0], case_110_multi[1]); + // Ticket on GGL list 2011/10/25 // to mix polygon/multipolygon in call to difference test_one("ggl_list_20111025_vd_pp", @@ -136,7 +142,7 @@ void test_areal() test_one("ggl_list_20120221_volker", ggl_list_20120221_volker[0], ggl_list_20120221_volker[1], - 2, 12, 7962.66, 1, 18, 2775258.93, + 2, 12, 7962.66, 2, 18, 2775258.93, 0.001); #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/difference/difference_multi_spike.cpp b/test/algorithms/set_operations/difference/difference_multi_spike.cpp index 7ec3d2ce0a..0fbf3dcf60 100644 --- a/test/algorithms/set_operations/difference/difference_multi_spike.cpp +++ b/test/algorithms/set_operations/difference/difference_multi_spike.cpp @@ -38,26 +38,26 @@ void test_spikes_in_ticket_8364() test_one("ticket_8364_step3", "MULTIPOLYGON(((3232 2532,2136 2790,1032 1764,1032 1458,1032 1212,2136 2328,3232 2220,3232 1056,1031 1056,1031 2856,3232 2856,3232 2532)))", "MULTIPOLYGON(((1032 2130,2052 2712,1032 1764,1032 2130)),((3234 2580,3234 2532,2558 2690,3234 2580)),((2558 2690,2136 2760,2052 2712,2136 2790,2558 2690)))", - 2, + if_typed(2, 3), if_typed(19, 22), if_typed(2775595.5, 2775256.487954), // SQL Server: 2775256.47588724 3, -1, // don't check point-count if_typed(7893.0, 7810.487954), // SQL Server: 7810.48711165739 - if_typed(1, 5), + if_typed(1, 6), -1, if_typed(2783349.5, 2775256.487954 + 7810.487954)); test_one("ticket_8364_step4", "MULTIPOLYGON(((2567 2688,2136 2790,2052 2712,1032 2130,1032 1764,1032 1458,1032 1212,2136 2328,3232 2220,3232 1056,1031 1056,1031 2856,3232 2856,3232 2580,2567 2688)))", "MULTIPOLYGON(((1032 2556,1778 2556,1032 2130,1032 2556)),((3234 2580,3234 2556,1778 2556,2136 2760,3234 2580)))", - 1, + if_typed(1, 2), if_typed(17, 20), if_typed(2615783.5, 2616029.559567), // SQL Server: 2616029.55616044 1, if_typed(9, 11), if_typed(161133.5, 161054.559567), // SQL Server: 161054.560110092 - if_typed(1, 2), + if_typed(1, 3), if_typed(25, 31), if_typed(2776875.5, 2616029.559567 + 161054.559567)); } diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index 72eb218a63..29894483cb 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -346,6 +347,51 @@ void test_one_lp(std::string const& caseid, difference_output(lp + caseid, g1, g2, pieces); } - +template +inline void test_validity(std::string const& caseid, + std::string const& wkt1, + std::string const& wkt2, + bool check_sym_difference = true) +{ + Areal1 a1; + Areal2 a2; + bg::read_wkt(wkt1, a1); + bg::read_wkt(wkt2, a2); + bg::correct(a1); + bg::correct(a2); + + bg::model::multi_polygon df12; + bg::difference(a1, a2, df12); + std::string reason12; + bool b12 = bg::is_valid(df12, reason12); + BOOST_CHECK_MESSAGE(b12, + caseid << "_a; g1: " << bg::wkt(a1) + << "; g2: " << bg::wkt(a2) + << "; df12: " << bg::wkt(df12) + << "; reason: " << reason12); + + bg::model::multi_polygon df21; + bg::difference(a1, a2, df21); + std::string reason21; + bool b21 = bg::is_valid(df21, reason21); + BOOST_CHECK_MESSAGE(b21, + caseid << "_b; g2: " << bg::wkt(a2) + << "; g1: " << bg::wkt(a1) + << "; df21: " << bg::wkt(df21) + << "; reason: " << reason21); + + if (check_sym_difference) + { + bg::model::multi_polygon sdf; + bg::sym_difference(a1, a2, sdf); + std::string reason_s; + bool b_s = bg::is_valid(sdf, reason_s); + BOOST_CHECK_MESSAGE(b_s, + caseid << "_s; g1: " << bg::wkt(a1) + << "; g2: " << bg::wkt(a2) + << "; sdf: " << bg::wkt(sdf) + << "; reason: " << reason_s); + } +} #endif diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index 58db253d8f..d805384b69 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -324,6 +324,64 @@ void test_areal() test_one("buffer_mp2", buffer_mp2[0], buffer_mp2[1], 1, 29, 0.457126); + test_one_with_holes("case87", + case_87[0], case_87[1], + 1, 1, 11, 54.70134); + + test_one_with_holes("case88", + case_88[0], case_88[1], + 1, 1, 13, 35.933385); + +#ifdef BOOST_GEOMETRY_INCLUDE_FAILING_TESTS + test_one_with_holes("case89", + case_89[0], case_89[1], + 2, 0, 13, 390); +#endif // BOOST_GEOMETRY_INCLUDE_FAILING_TESTS + + test_one_with_holes("case90", + case_90[0], case_90[1], + 2, 0, 13, 143.067882); + test_validity("case90", case_90[0], case_90[1]); + + test_one_with_holes("case91", + case_91[0], case_91[1], + 3, 0, 13, 216.6667); + test_validity("case91", case_91[0], case_91[1]); + + test_one_with_holes("case92", + case_92[0], case_92[1], + 2, 0, 13, 633.3333); + test_validity("case92", case_92[0], case_92[1]); + + test_one_with_holes("case93", + case_93[0], case_93[1], + 3, 0, 17, 231.42857); + test_validity("case93", case_93[0], case_93[1]); + + test_one_with_holes("case94", + case_94[0], case_94[1], + 2, 1, 16, 645.71429); + test_validity("case94", case_94[0], case_94[1]); + + test_one_with_holes("case95", + case_95[0], case_95[1], + 3, 0, 16, 226.66667); + test_validity("case95", case_95[0], case_95[1]); + + test_one_with_holes("case96", + case_96[0], case_96[1], + 4, 0, 19, 221.42857); + test_validity("case96", case_96[0], case_96[1]); + + test_one_with_holes("case97", + case_97[0], case_97[1], + 2, 0, 10, 11.81244); + test_validity("case97", case_97[0], case_97[1]); + + test_one_with_holes("case98", + case_98[0], case_98[1], + 2, 0, 10, 17.754744); + test_validity("case98", case_98[0], case_98[1]); return; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 2399935cb3..8c9668e0d0 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -90,20 +90,30 @@ void test_areal() test_one("case_107_multi", case_107_multi[0], case_107_multi[1], 2, 10, 1.5); + + test_one("case_110_multi", + case_110_multi[0], case_110_multi[1], + 2, 11, 9.8050578678287668); + test_validity("case_110_multi", + case_110_multi[0], case_110_multi[1]); + test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 10, 97, 47.0); - test_one("case_recursive_boxes_2", + + test_one_with_holes( + "case_recursive_boxes_2", case_recursive_boxes_2[0], case_recursive_boxes_2[1], - 1, 47, 90.0); // Area from SQL Server + 1, 6, 52, 90.0); // Area from SQL Server test_one("case_recursive_boxes_3", case_recursive_boxes_3[0], case_recursive_boxes_3[1], 19, 87, 12.5); // Area from SQL Server - test_one("case_recursive_boxes_4", + test_one_with_holes( + "case_recursive_boxes_4", case_recursive_boxes_4[0], case_recursive_boxes_4[1], - 13, 157, 67.0); // Area from SQL Server + 13, 7, 169, 67.0); // Area from SQL Server test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 512bc9c95e..1184dd416f 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,9 @@ typename bg::default_area_result::type check_result( std::vector const& intersection_output, std::string const& caseid, - std::size_t expected_count = 0, int expected_point_count = 0, + std::size_t expected_count = 0, + int expected_hole_count = -1, + int expected_point_count = 0, double expected_length_or_area = 0, double percentage = 0.0001, bool debug = false) @@ -48,6 +51,7 @@ check_result( typename bg::default_area_result::type length_or_area = 0; int n = 0; + int holes = 0; for (typename std::vector::const_iterator it = intersection_output.begin(); it != intersection_output.end(); ++it) @@ -56,6 +60,7 @@ check_result( { // here n should rather be of type std::size_t, but expected_point_count // is set to -1 in some test cases so type int was left for now + holes += static_cast(bg::num_interior_rings(*it)); n += static_cast(bg::num_points(*it, true)); } @@ -72,6 +77,14 @@ check_result( #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST) #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + + BOOST_CHECK_MESSAGE(expected_hole_count < 0 || holes == expected_hole_count, + "intersection: " << caseid + << " #holes expected: " << expected_hole_count + << " detected: " << holes + << " type: " << (type_for_assert_message()) + ); + if (expected_point_count > 0) { BOOST_CHECK_MESSAGE(bg::math::abs(n - expected_point_count) < 3, @@ -111,9 +124,11 @@ check_result( template -typename bg::default_area_result::type test_intersection(std::string const& caseid, +typename bg::default_area_result::type test_intersection_with_holes(std::string const& caseid, G1 const& g1, G2 const& g2, - std::size_t expected_count = 0, int expected_point_count = 0, + std::size_t expected_count = 0, + int expected_hole_count = -1, + int expected_point_count = 0, double expected_length_or_area = 0, double percentage = 0.0001, bool debug = false) @@ -147,26 +162,30 @@ typename bg::default_area_result::type test_intersection(std::string const& std::vector intersection_output; bg::intersection(g1, g2, intersection_output); - check_result(intersection_output, caseid, expected_count, expected_point_count, + check_result(intersection_output, caseid, expected_count, + expected_hole_count, expected_point_count, expected_length_or_area, percentage, debug); // Check variant behaviour intersection_output.clear(); bg::intersection(boost::variant(g1), g2, intersection_output); - check_result(intersection_output, caseid, expected_count, expected_point_count, + check_result(intersection_output, caseid, expected_count, + expected_hole_count, expected_point_count, expected_length_or_area, percentage, debug); intersection_output.clear(); bg::intersection(g1, boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, expected_point_count, + check_result(intersection_output, caseid, expected_count, + expected_hole_count, expected_point_count, expected_length_or_area, percentage, debug); intersection_output.clear(); bg::intersection(boost::variant(g1), boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, expected_point_count, + check_result(intersection_output, caseid, expected_count, + expected_hole_count, expected_point_count, expected_length_or_area, percentage, debug); #if defined(TEST_WITH_SVG) @@ -225,10 +244,29 @@ typename bg::default_area_result::type test_intersection(std::string const& return length_or_area; } +template +typename bg::default_area_result::type test_intersection(std::string const& caseid, + G1 const& g1, G2 const& g2, + std::size_t expected_count = 0, + int expected_point_count = 0, + double expected_length_or_area = 0, + double percentage = 0.0001, + bool debug = false) +{ + return test_intersection_with_holes + < + OutputType, CalculationType, G1, G2 + >(caseid, g1, g2, expected_count, -1, expected_point_count, + expected_length_or_area, percentage, debug); +} + + template -typename bg::default_area_result::type test_one(std::string const& caseid, +typename bg::default_area_result::type test_one_with_holes(std::string const& caseid, std::string const& wkt1, std::string const& wkt2, - std::size_t expected_count = 0, int expected_point_count = 0, + std::size_t expected_count = 0, + int expected_hole_count = -1, + int expected_point_count = 0, double expected_length_or_area = 0, double percentage = 0.0001, bool debug = false) @@ -243,12 +281,28 @@ typename bg::default_area_result::type test_one(std::string const& caseid, bg::correct(g1); bg::correct(g2); - return test_intersection(caseid, g1, g2, - expected_count, expected_point_count, + return test_intersection_with_holes(caseid, g1, g2, + expected_count, expected_hole_count, expected_point_count, expected_length_or_area, percentage, debug); } +template +typename bg::default_area_result::type test_one(std::string const& caseid, + std::string const& wkt1, std::string const& wkt2, + std::size_t expected_count = 0, + int expected_point_count = 0, + double expected_length_or_area = 0, + double percentage = 0.0001, + bool debug = false) +{ + return test_one_with_holes + < + OutputType, G1, G2 + >(caseid, wkt1, wkt2, expected_count, -1, expected_point_count, + expected_length_or_area, percentage, debug); +} + template void test_one_lp(std::string const& caseid, std::string const& wkt_areal, std::string const& wkt_linear, @@ -299,4 +353,28 @@ void test_point_output(std::string const& wkt1, std::string const& wkt2, unsigne } +template +inline void test_validity(std::string const& caseid, + std::string const& wkt1, + std::string const& wkt2) +{ + Areal1 a1; + Areal2 a2; + bg::read_wkt(wkt1, a1); + bg::read_wkt(wkt2, a2); + bg::correct(a1); + bg::correct(a2); + + bg::model::multi_polygon out; + bg::intersection(a1, a2, out); + + std::string reason; + bool b = bg::is_valid(out, reason); + BOOST_CHECK_MESSAGE(b, + "caseid: " << caseid << "; g1: " << bg::wkt(a1) + << "; g2: " << bg::wkt(a2) + << "; i: " << bg::wkt(out) + << "; reason: " << reason); +} + #endif diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 3d582d2ca6..53c779d011 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -202,6 +203,27 @@ void test_one(std::string const& caseid, std::string const& wkt1, std::string co expected_area, percentage); } - +template +inline void test_validity(std::string const& caseid, + std::string const& wkt1, + std::string const& wkt2) +{ + Areal1 a1; + Areal2 a2; + bg::read_wkt(wkt1, a1); + bg::read_wkt(wkt2, a2); + bg::correct(a1); + bg::correct(a2); + + bg::model::multi_polygon out; + bg::union_(a1, a2, out); + + std::string reason; + bool b = bg::is_valid(out, reason); + BOOST_CHECK_MESSAGE(b, + "caseid: " << caseid + << "; geometry: " << bg::wkt(out) + << "; reason: " << reason); +} #endif diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index 9cf245de72..651ce0a79f 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -169,7 +169,7 @@ void test_areal() test_one("33", case_33[0], case_33[1], 2, 0, 8, 4.5); test_one("36", - case_36[0], case_36[1], 1, 0, 10, 14.375); + case_36[0], case_36[1], 1, 1, 10, 14.375); test_one("40", case_40[0], case_40[1], 2, 0, 18, 11); @@ -181,6 +181,32 @@ void test_areal() test_one("59_iet", case_59[0], case_59[2], 1, 1, 14, 17.20833); + test_one("80", + case_80[0], case_80[1], 1, 2, 18, 221.369); + test_one("81", + case_81[0], case_81[1], 1, 1, 10, 147.5); + test_one("82", + case_82[0], case_82[1], 2, 0, 9, 175); + test_one("83", + case_83[0], case_83[1], 1, 2, 13, 172.917); + test_one("84", + case_84[0], case_84[1], 2, 0, 11, 170); + test_one("85", + case_85[0], case_85[1], 1, 2, 15, 1320); + test_one("86", + case_86[0], case_86[1], 1, 1, 10, 1500); + + test_one("97", + case_97[0], case_97[1], 1, 1, 17, 127.6875598); + test_validity("97", case_97[0], case_97[1]); + + test_one("98", + case_98[0], case_98[1], 1, 2, 23, 155.2452558); + test_validity("98", case_98[0], case_98[1]); + + test_one("103", + case_103[0], case_103[1], 1, 0, 10, 64.072499); + test_validity("103", case_103[0], case_103[1]); /* test_one(102, diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 2af15780ce..dbc435b1cc 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -92,7 +92,7 @@ void test_areal() 1, 0, 13, 6); test_one("case_101_multi", case_101_multi[0], case_101_multi[1], - 1, 0, 32, 22.25); + 1, 3, 35, 22.25); test_one("case_103_multi", case_103_multi[0], case_103_multi[1], 1, 0, 7, 25); @@ -103,6 +103,19 @@ void test_areal() case_105_multi[0], case_105_multi[1], 1, 0, 5, 25); + test_one("case_108_multi", + case_108_multi[0], case_108_multi[1], + 1, 2, 14, 1400); + test_one("case_109_multi", + case_109_multi[0], case_109_multi[1], + 1, 9, 45, 1250); + + test_one("case_110_multi", + case_110_multi[0], case_110_multi[1], + 1, 1, 19, 99.194942); + test_validity("case_110_multi", + case_110_multi[0], case_110_multi[1]); + test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 1, 1, 36, 97.0); @@ -111,14 +124,14 @@ void test_areal() 1, 0, 14, 100.0); // Area from SQL Server test_one("case_recursive_boxes_3", case_recursive_boxes_3[0], case_recursive_boxes_3[1], - 17, 0, 159, 56.5); // Area from SQL Server + 17, 6, 165, 56.5); // Area from SQL Server test_one("case_recursive_boxes_4", case_recursive_boxes_4[0], case_recursive_boxes_4[1], - 1, 1, 42, 96.75); + 1, 2, 43, 96.75); test_one("case_recursive_boxes_5", case_recursive_boxes_5[0], case_recursive_boxes_5[1], - 3, 2, 110, 70.0); + 3, 10, 118, 70.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1],