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 162642150..855b768ea 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -246,6 +247,12 @@ struct buffered_piece_collection buffered_ring_collection traversed_rings; segment_identifier current_segment_id; + // Specificly for offsetted rings around points + // but also for large joins with many points + typedef geometry::sections sections_type; + sections_type monotonic_sections; + + RobustPolicy const& m_robust_policy; struct redundant_turn @@ -642,20 +649,30 @@ struct buffered_piece_collection inline void get_turns() { + for(typename boost::range_iterator::type it + = boost::begin(monotonic_sections); + it != boost::end(monotonic_sections); + ++it) + { + enlarge_box(it->bounding_box, 1); + } + { // Calculate the turns piece_turn_visitor < + piece_vector_type, buffered_ring_collection >, turn_vector_type, RobustPolicy - > visitor(offsetted_rings, m_turns, m_robust_policy); + > visitor(m_pieces, offsetted_rings, m_turns, m_robust_policy); geometry::partition < - model::box, - piece_get_offsetted_box, piece_ovelaps_offsetted_box - >::apply(m_pieces, visitor); + robust_box_type, + detail::section::get_section_box, + detail::section::overlaps_section_box + >::apply(monotonic_sections, visitor); } insert_rescaled_piece_turns(); @@ -882,10 +899,34 @@ struct buffered_piece_collection enlarge_box(pc.robust_offsetted_envelope, 1); } + inline void sectionalize(piece& pc) + { + + buffered_ring const& ring = offsetted_rings.back(); + + typedef geometry::detail::sectionalize::sectionalize_part + < + point_type, + boost::mpl::vector_c // x,y dimension + > sectionalizer; + + // Create a ring-identifier. The source-index is the piece index + // The multi_index is as in this collection (the ring), but not used here + // The ring_index is not used + ring_identifier ring_id(pc.index, pc.first_seg_id.multi_index, -1); + + sectionalizer::apply(monotonic_sections, + boost::begin(ring) + pc.first_seg_id.segment_index, + boost::begin(ring) + pc.last_segment_index, + m_robust_policy, + ring_id, 10); + } + inline void finish_piece(piece& pc) { init_rescale_piece(pc, 0u); calculate_robust_envelope(pc); + sectionalize(pc); } inline void finish_piece(piece& pc, @@ -903,6 +944,7 @@ struct buffered_piece_collection robust_point_type mid_point = add_helper_point(pc, point2); add_helper_point(pc, point3); calculate_robust_envelope(pc); + sectionalize(pc); current_robust_ring.push_back(mid_point); } @@ -918,6 +960,7 @@ struct buffered_piece_collection robust_point_type mid_point2 = add_helper_point(pc, point2); robust_point_type mid_point1 = add_helper_point(pc, point3); add_helper_point(pc, point4); + sectionalize(pc); calculate_robust_envelope(pc); // Add mid-points in other order to current helper_ring diff --git a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 9530d3e25..31ffdec53 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -27,32 +27,16 @@ namespace detail { namespace buffer { -struct piece_get_offsetted_box -{ - template - static inline void apply(Box& total, Piece const& piece) - { - geometry::expand(total, piece.robust_offsetted_envelope); - } -}; - -struct piece_ovelaps_offsetted_box -{ - template - static inline bool apply(Box const& box, Piece const& piece) - { - return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_offsetted_envelope); - } -}; - template < + typename Pieces, typename Rings, typename Turns, typename RobustPolicy > class piece_turn_visitor { + Pieces const& m_pieces; Rings const& m_rings; Turns& m_turns; RobustPolicy const& m_robust_policy; @@ -104,46 +88,57 @@ class piece_turn_visitor return result; } - template - inline void calculate_turns(Piece const& piece1, Piece const& piece2) + template + inline void calculate_turns(Piece const& piece1, Piece const& piece2, + Section const& section1, Section const& section2) { typedef typename boost::range_value::type ring_type; typedef typename boost::range_value::type turn_type; typedef typename boost::range_iterator::type iterator; - segment_identifier seg_id1 = piece1.first_seg_id; - segment_identifier seg_id2 = piece2.first_seg_id; - - if (seg_id1.segment_index < 0 || seg_id2.segment_index < 0) + int const piece1_first_index = piece1.first_seg_id.segment_index; + int const piece2_first_index = piece2.first_seg_id.segment_index; + if (piece1_first_index < 0 || piece2_first_index < 0) { return; } - ring_type const& ring1 = m_rings[seg_id1.multi_index]; - iterator it1_first = boost::begin(ring1) + seg_id1.segment_index; - iterator it1_last = boost::begin(ring1) + piece1.last_segment_index; + // Get indices of part of offsetted_rings for this monotonic section: + int const sec1_first_index = piece1_first_index + section1.begin_index; + int const sec2_first_index = piece2_first_index + section2.begin_index; - ring_type const& ring2 = m_rings[seg_id2.multi_index]; - iterator it2_first = boost::begin(ring2) + seg_id2.segment_index; - iterator it2_last = boost::begin(ring2) + piece2.last_segment_index; + // index of last point in section, beyond-end is one further + int const sec1_last_index = piece1_first_index + section1.end_index; + int const sec2_last_index = piece2_first_index + section2.end_index; + + // get geometry and iterators over these sections + ring_type const& ring1 = m_rings[piece1.first_seg_id.multi_index]; + iterator const it1_first = boost::begin(ring1) + sec1_first_index; + iterator const it1_beyond = boost::begin(ring1) + sec1_last_index + 1; + + ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index]; + iterator const it2_first = boost::begin(ring2) + sec2_first_index; + iterator const it2_beyond = boost::begin(ring2) + sec2_last_index + 1; turn_type the_model; the_model.operations[0].piece_index = piece1.index; the_model.operations[0].seg_id = piece1.first_seg_id; + the_model.operations[0].seg_id.segment_index = sec1_first_index; // override iterator it1 = it1_first; for (iterator prev1 = it1++; - it1 != it1_last; + it1 != it1_beyond; prev1 = it1++, the_model.operations[0].seg_id.segment_index++) { the_model.operations[1].piece_index = piece2.index; the_model.operations[1].seg_id = piece2.first_seg_id; + the_model.operations[1].seg_id.segment_index = sec2_first_index; // override iterator next1 = next_point(ring1, it1); iterator it2 = it2_first; for (iterator prev2 = it2++; - it2 != it2_last; + it2 != it2_beyond; prev2 = it2++, the_model.operations[1].seg_id.segment_index++) { iterator next2 = next_point(ring2, it2); @@ -169,28 +164,36 @@ class piece_turn_visitor public: - piece_turn_visitor(Rings const& ring_collection, + piece_turn_visitor(Pieces const& pieces, + Rings const& ring_collection, Turns& turns, RobustPolicy const& robust_policy) - : m_rings(ring_collection) + : m_pieces(pieces) + , m_rings(ring_collection) , m_turns(turns) , m_robust_policy(robust_policy) {} - template - inline void apply(Piece const& piece1, Piece const& piece2, + template + inline void apply(Section const& section1, Section const& section2, bool first = true) { boost::ignore_unused_variable_warning(first); - if ( is_adjacent(piece1, piece2) + + typedef typename boost::range_value::type piece_type; + piece_type const& piece1 = m_pieces[section1.ring_id.source_index]; + piece_type const& piece2 = m_pieces[section2.ring_id.source_index]; + + if ( piece1.index == piece2.index + || is_adjacent(piece1, piece2) || is_on_same_convex_ring(piece1, piece2) - || detail::disjoint::disjoint_box_box(piece1.robust_offsetted_envelope, - piece2.robust_offsetted_envelope)) + || detail::disjoint::disjoint_box_box(section1.bounding_box, + section2.bounding_box) ) { return; } - calculate_turns(piece1, piece2); + calculate_turns(piece1, piece2, section1, section2); } }; diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index 2c917d834..dafa3c0c3 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -23,6 +23,9 @@ static std::string const multipoint_b = "MULTIPOINT((5 56),(98 67),(20 7),(58 60 // Grid, U-form, generates error for square point at 0.54 (top cells to control rescale) static std::string const grid_a = "MULTIPOINT(5 0,6 0,7 0, 5 1,7 1, 0 13,8 13)"; +static std::string const mysql_report_2015_02_25_1 = "MULTIPOINT(-9 19,9 -6,-4 4,16 -14,-3 16,14 9)"; +static std::string const mysql_report_2015_02_25_2 = "MULTIPOINT(-2 11,-15 3,6 4,-14 0,20 -7,-17 -1)"; + template void test_all() { @@ -31,6 +34,11 @@ void test_all() bg::strategy::buffer::join_miter join_miter; bg::strategy::buffer::end_flat end_flat; + typedef bg::strategy::buffer::distance_symmetric + < + typename bg::coordinate_type

::type + > distance_strategy; + bg::strategy::buffer::side_straight side_strategy; double const pi = boost::geometry::math::pi(); @@ -53,21 +61,55 @@ void test_all() // Grid tests { bg::strategy::buffer::point_square point_strategy; - bg::strategy::buffer::side_straight side_strategy; - typedef bg::strategy::buffer::distance_symmetric - < - typename bg::coordinate_type

::type - > distance_strategy; test_with_custom_strategies("grid_a50", grid_a, join_miter, end_flat, distance_strategy(0.5), side_strategy, point_strategy, 7.0); + #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_with_custom_strategies("grid_a54", grid_a, join_miter, end_flat, distance_strategy(0.54), side_strategy, point_strategy, 99); #endif + + + } + + // Tests for large distances / many points in circles. + // Before Boost 1.58, this would (seem to) hang. It is solved by using monotonic sections in get_turns for buffer + // This is more time consuming, only calculate this for counter clockwise + if (! BOOST_GEOMETRY_CONDITION(Clockwise)) + { + // Reported by MySQL 2015-02-25 + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 6051788, ST_BUFFER_STRATEGY('point_circle', 83585))); + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 5666962, ST_BUFFER_STRATEGY('point_circle', 46641))) ; + + using bg::strategy::buffer::point_circle; + test_with_custom_strategies("mysql_report_2015_02_25_1_800", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(800), 115057490003226.125); + + test_with_custom_strategies("mysql_report_2015_02_25_1_8000", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(8000), 115058661065242.812); + + test_with_custom_strategies("mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219); + +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) + // Try to specify even more points per circle + // Turns are calculated in 23 seconds + // But the follow-up still takes too long (there are 63409 turns), this might be improved too + test_with_custom_strategies("mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(800000), 115058672785611.219); +#endif + + test_with_custom_strategies("mysql_report_2015_02_25_2", + mysql_report_2015_02_25_2, join_miter, end_flat, + distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344); } }