diff options
Diffstat (limited to 'boost/geometry/algorithms/detail')
78 files changed, 8522 insertions, 1527 deletions
diff --git a/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 029053dda3..a149f1dd46 100644 --- a/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -122,7 +122,8 @@ struct buffer_range typename DistanceStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void add_join(Collection& collection, @@ -133,18 +134,19 @@ struct buffer_range Point const& input, output_point_type const& perp1, output_point_type const& perp2, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& strategy) // side strategy { output_point_type intersection_point; geometry::assign_zero(intersection_point); - strategy::buffer::join_selector join - = get_join_type(penultimate_input, previous_input, input); - if (join == strategy::buffer::join_convex) + geometry::strategy::buffer::join_selector join + = get_join_type(penultimate_input, previous_input, input, strategy); + if (join == geometry::strategy::buffer::join_convex) { // Calculate the intersection-point formed by the two sides. // It might be that the two sides are not convex, but continue @@ -157,23 +159,23 @@ struct buffer_range switch(join) { - case strategy::buffer::join_continue : + case geometry::strategy::buffer::join_continue : // No join, we get two consecutive sides break; - case strategy::buffer::join_concave : + case geometry::strategy::buffer::join_concave : { std::vector<output_point_type> range_out; range_out.push_back(prev_perp2); range_out.push_back(previous_input); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); range_out.clear(); range_out.push_back(previous_input); range_out.push_back(perp1); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); } break; - case strategy::buffer::join_spike : + case geometry::strategy::buffer::join_spike : { // For linestrings, only add spike at one side to avoid // duplicates @@ -183,7 +185,7 @@ struct buffer_range collection.set_current_ring_concave(); } break; - case strategy::buffer::join_convex : + case geometry::strategy::buffer::join_convex : { // The corner is convex, we create a join // TODO (future) - avoid a separate vector, add the piece directly @@ -193,7 +195,7 @@ struct buffer_range distance.apply(previous_input, input, side), range_out)) { - collection.add_piece(strategy::buffer::buffered_join, + collection.add_piece(geometry::strategy::buffer::buffered_join, previous_input, range_out); } } @@ -201,27 +203,24 @@ struct buffer_range } } - static inline strategy::buffer::join_selector get_join_type( + template <typename Strategy> + static inline geometry::strategy::buffer::join_selector get_join_type( output_point_type const& p0, output_point_type const& p1, - output_point_type const& p2) + output_point_type const& p2, + Strategy const& strategy) // side strategy { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<output_point_type>::type - >::type side_strategy; - - int const side = side_strategy::apply(p0, p1, p2); - return side == -1 ? strategy::buffer::join_convex - : side == 1 ? strategy::buffer::join_concave + int const side = strategy.apply(p0, p1, p2); + return side == -1 ? geometry::strategy::buffer::join_convex + : side == 1 ? geometry::strategy::buffer::join_concave : parallel_continue ( get<0>(p2) - get<0>(p1), get<1>(p2) - get<1>(p1), get<0>(p1) - get<0>(p0), get<1>(p1) - get<1>(p0) - ) ? strategy::buffer::join_continue - : strategy::buffer::join_spike; + ) ? geometry::strategy::buffer::join_continue + : geometry::strategy::buffer::join_spike; } template @@ -232,16 +231,18 @@ struct buffer_range typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy output_point_type& first_p1, output_point_type& first_p2, output_point_type& last_p1, @@ -273,7 +274,7 @@ struct buffer_range * pup: penultimate_point */ - strategy::buffer::result_code result = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code result = geometry::strategy::buffer::result_no_output; bool first = true; Iterator it = begin; @@ -284,25 +285,25 @@ struct buffer_range for (Iterator prev = it++; it != end; ++it) { generated_side.clear(); - strategy::buffer::result_code error_code + geometry::strategy::buffer::result_code error_code = side_strategy.apply(*prev, *it, side, distance_strategy, generated_side); - if (error_code == strategy::buffer::result_no_output) + if (error_code == geometry::strategy::buffer::result_no_output) { // Because input is simplified, this is improbable, // but it can happen for degenerate geometries // Further handling of this side is skipped continue; } - else if (error_code == strategy::buffer::result_error_numerical) + else if (error_code == geometry::strategy::buffer::result_error_numerical) { return error_code; } BOOST_GEOMETRY_ASSERT(! generated_side.empty()); - result = strategy::buffer::result_normal; + result = geometry::strategy::buffer::result_normal; if (! first) { @@ -312,7 +313,7 @@ struct buffer_range *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } collection.add_side_piece(*prev, *it, generated_side, first); @@ -350,7 +351,8 @@ struct buffer_multi typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Multi const& multi, Collection& collection, @@ -359,7 +361,8 @@ struct buffer_multi JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { for (typename boost::range_iterator<Multi const>::type it = boost::begin(multi); @@ -369,7 +372,7 @@ struct buffer_multi Policy::apply(*it, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } } }; @@ -396,9 +399,9 @@ inline void buffer_point(Point const& point, Collection& collection, collection.start_new_ring(); std::vector<OutputPointType> range_out; point_strategy.apply(point, distance_strategy, range_out); - collection.add_piece(strategy::buffer::buffered_point, range_out, false); + collection.add_piece(geometry::strategy::buffer::buffered_point, range_out, false); collection.set_piece_center(point); - collection.finish_ring(strategy::buffer::result_normal); + collection.finish_ring(geometry::strategy::buffer::result_normal); } @@ -436,7 +439,8 @@ struct buffer_inserter<point_tag, Point, RingOutput> typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Point const& point, Collection& collection, DistanceStrategy const& distance_strategy, @@ -444,7 +448,8 @@ struct buffer_inserter<point_tag, Point, RingOutput> JoinStrategy const& , EndStrategy const& , PointStrategy const& point_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& ) // side strategy { detail::buffer::buffer_point < @@ -472,29 +477,32 @@ struct buffer_inserter_ring typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { output_point_type first_p1, first_p2, last_p1, last_p2; typedef detail::buffer::buffer_range<RingOutput> buffer_range; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = buffer_range::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); // Generate closing join - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { buffer_range::add_join(collection, *(end - 2), @@ -502,7 +510,7 @@ struct buffer_inserter_ring *(begin + 1), first_p1, first_p2, side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } // Buffer is closed automatically by last closing corner @@ -517,21 +525,23 @@ struct buffer_inserter_ring typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code apply(RingInput const& ring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { RingInput simplified; detail::buffer::simplify_input(ring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); std::size_t const min_points = core_detail::closure::minimum_ring_size @@ -546,18 +556,20 @@ struct buffer_inserter_ring { // Walk backwards (rings will be reversed afterwards) code = iterate(collection, boost::rbegin(view), boost::rend(view), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } else { code = iterate(collection, boost::begin(view), boost::end(view), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated ring detail::buffer::buffer_point<output_point_type> @@ -586,23 +598,25 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput> typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code apply(RingInput const& ring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = buffer_inserter_ring<RingInput, RingOutput>::apply(ring, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code); return code; } @@ -627,16 +641,18 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon> typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy output_point_type& first_p1) { input_point_type const& ultimate_point = *(end - 1); @@ -647,18 +663,18 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon> // we have it already from the first phase (left). // But for the first pass, we have to generate it output_point_type reverse_p1; - if (side == strategy::buffer::buffer_side_right) + if (side == geometry::strategy::buffer::buffer_side_right) { reverse_p1 = first_p1; } else { std::vector<output_point_type> generated_side; - strategy::buffer::result_code code + geometry::strategy::buffer::result_code code = side_strategy.apply(ultimate_point, penultimate_point, - strategy::buffer::buffer_side_right, + geometry::strategy::buffer::buffer_side_right, distance_strategy, generated_side); - if (code != strategy::buffer::result_normal) + if (code != geometry::strategy::buffer::result_normal) { // No output or numerical error return code; @@ -668,16 +684,18 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon> output_point_type first_p2, last_p1, last_p2; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = detail::buffer::buffer_range<output_ring_type>::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { std::vector<output_point_type> range_out; - end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, range_out); + end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, + side, distance_strategy, range_out); collection.add_endcap(end_strategy, range_out, ultimate_point); } return result; @@ -691,20 +709,23 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon> typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(Linestring const& linestring, Collection& collection, + static inline geometry::strategy::buffer::result_code apply(Linestring const& linestring, + Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { Linestring simplified; detail::buffer::simplify_input(linestring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); if (n > 1) { @@ -712,21 +733,23 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon> output_point_type first_p1; code = iterate(collection, boost::begin(simplified), boost::end(simplified), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); - if (code == strategy::buffer::result_normal) + if (code == geometry::strategy::buffer::result_normal) { code = iterate(collection, boost::rbegin(simplified), boost::rend(simplified), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); } collection.finish_ring(code); } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated linestring detail::buffer::buffer_point<output_point_type> @@ -763,7 +786,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void iterate(Iterator begin, Iterator end, @@ -774,15 +798,16 @@ private: EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy bool is_interior) { for (Iterator it = begin; it != end; ++it) { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(*it, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, is_interior); } @@ -797,7 +822,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply_interior_rings(InteriorRings const& interior_rings, @@ -807,12 +833,13 @@ private: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { iterate(boost::begin(interior_rings), boost::end(interior_rings), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy, true); + robust_policy, strategy, true); } public: @@ -824,7 +851,8 @@ public: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(PolygonInput const& polygon, Collection& collection, @@ -833,16 +861,17 @@ public: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(exterior_ring(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, false, geometry::num_interior_rings(polygon) > 0u); @@ -851,7 +880,7 @@ public: apply_interior_rings(interior_rings(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } }; @@ -945,7 +974,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator >::apply(geometry_input, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, intersection_strategy.get_side_strategy()); collection.get_turns(); collection.classify_turns(linear); diff --git a/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 7fbbb790bb..c0d906fe62 100644 --- a/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -142,10 +142,20 @@ struct buffered_piece_collection robust_point_type >::type robust_comparable_radius_type; - typedef typename strategy::side::services::default_strategy + typedef typename IntersectionStrategy::side_strategy_type side_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy + < + point_type + >::type area_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy < - typename cs_tag<point_type>::type - >::type side_strategy; + robust_point_type + >::type robust_area_strategy_type; + + typedef typename area_strategy_type::return_type area_result_type; + typedef typename robust_area_strategy_type::return_type robust_area_result_type; typedef typename geometry::rescale_policy_type < @@ -306,7 +316,10 @@ struct buffered_piece_collection cluster_type m_clusters; - IntersectionStrategy const& m_intersection_strategy; + IntersectionStrategy m_intersection_strategy; + side_strategy_type m_side_strategy; + area_strategy_type m_area_strategy; + robust_area_strategy_type m_robust_area_strategy; RobustPolicy const& m_robust_policy; struct redundant_turn @@ -321,6 +334,9 @@ struct buffered_piece_collection RobustPolicy const& robust_policy) : m_first_piece_index(-1) , m_intersection_strategy(intersection_strategy) + , m_side_strategy(intersection_strategy.get_side_strategy()) + , m_area_strategy(intersection_strategy.template get_area_strategy<point_type>()) + , m_robust_area_strategy(intersection_strategy.template get_area_strategy<robust_point_type>()) , m_robust_policy(robust_policy) {} @@ -478,7 +494,7 @@ struct buffered_piece_collection for (typename occupation_map_type::iterator it = occupation_map.begin(); it != occupation_map.end(); ++it) { - it->second.get_left_turns(it->first, m_turns); + it->second.get_left_turns(it->first, m_turns, m_side_strategy); } } @@ -699,7 +715,7 @@ struct buffered_piece_collection ++it) { piece& pc = *it; - if (geometry::area(pc.robust_ring) < 0) + if (geometry::area(pc.robust_ring, m_robust_area_strategy) < 0) { // Rings can be ccw: // - in a concave piece @@ -1220,14 +1236,9 @@ struct buffered_piece_collection inline void enrich() { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Ring>::type - >::type side_strategy_type; - enrich_intersection_points<false, false, overlay_buffer>(m_turns, m_clusters, offsetted_rings, offsetted_rings, - m_robust_policy, side_strategy_type()); + m_robust_policy, m_side_strategy); } // Discards all rings which do have not-OK intersection points only. @@ -1314,7 +1325,7 @@ struct buffered_piece_collection buffered_ring<Ring>& ring = *it; if (! ring.has_intersections() && boost::size(ring) > 0u - && geometry::area(ring) < 0) + && geometry::area(ring, m_area_strategy) < 0) { if (! point_coveredby_original(geometry::range::front(ring))) { @@ -1391,7 +1402,7 @@ struct buffered_piece_collection template <typename GeometryOutput, typename OutputIterator> inline OutputIterator assign(OutputIterator out) const { - typedef detail::overlay::ring_properties<point_type> properties; + typedef detail::overlay::ring_properties<point_type, area_result_type> properties; std::map<ring_identifier, properties> selected; @@ -1407,7 +1418,7 @@ struct buffered_piece_collection if (! it->has_intersections() && ! it->is_untouched_outside_original) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(0, index, -1); @@ -1423,7 +1434,7 @@ struct buffered_piece_collection it != boost::end(traversed_rings); ++it, ++index) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(2, index, -1); @@ -1431,7 +1442,7 @@ struct buffered_piece_collection } } - detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, true); + detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, m_intersection_strategy, true); return detail::overlay::add_rings<GeometryOutput>(selected, offsetted_rings, traversed_rings, out); } diff --git a/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 178c7bcafe..5c012e7151 100644 --- a/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017 Oracle and/or its affiliates. @@ -126,26 +127,31 @@ class piece_turn_visitor template <std::size_t Dimension, typename Iterator, typename Box> inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, - signed_size_type& index, int dir, Box const& other_bounding_box) + signed_size_type& index, int dir, + Box const& this_bounding_box, + Box const& other_bounding_box) { for(; it_begin != it_beyond && it_begin + 1 != it_beyond && detail::section::preceding<Dimension>(dir, *(it_begin + 1), - other_bounding_box, m_robust_policy); + this_bounding_box, + other_bounding_box, + m_robust_policy); ++it_begin, index++) {} } template <std::size_t Dimension, typename Iterator, typename Box> inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, - int dir, Box const& other_bounding_box) + int dir, Box const& this_bounding_box, + Box const& other_bounding_box) { while (it_beyond != it_begin && it_beyond - 1 != it_begin && it_beyond - 2 != it_begin) { if (detail::section::exceeding<Dimension>(dir, *(it_beyond - 2), - other_bounding_box, m_robust_policy)) + this_bounding_box, other_bounding_box, m_robust_policy)) { --it_beyond; } @@ -191,23 +197,23 @@ class piece_turn_visitor // Set begin/end of monotonic ranges, in both x/y directions signed_size_type index1 = sec1_first_index; move_begin_iterator<0>(it1_first, it1_beyond, index1, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_end_iterator<0>(it1_first, it1_beyond, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_begin_iterator<1>(it1_first, it1_beyond, index1, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); move_end_iterator<1>(it1_first, it1_beyond, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); signed_size_type index2 = sec2_first_index; move_begin_iterator<0>(it2_first, it2_beyond, index2, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_end_iterator<0>(it2_first, it2_beyond, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_begin_iterator<1>(it2_first, it2_beyond, index2, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); move_end_iterator<1>(it2_first, it2_beyond, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; @@ -272,7 +278,7 @@ public: {} template <typename Section> - inline void apply(Section const& section1, Section const& section2, + inline bool apply(Section const& section1, Section const& section2, bool first = true) { boost::ignore_unused_variable_warning(first); @@ -285,12 +291,14 @@ public: || is_adjacent(piece1, piece2) || is_on_same_convex_ring(piece1, piece2) || detail::disjoint::disjoint_box_box(section1.bounding_box, - section2.bounding_box) ) + section2.bounding_box) ) { - return; + return true; } calculate_turns(piece1, piece2, section1, section2); + + return true; } }; diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp index da1d084d32..e7cc97539f 100644 --- a/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp +++ b/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp @@ -212,27 +212,27 @@ public: {} template <typename Turn, typename Original> - inline void apply(Turn const& turn, Original const& original, bool first = true) + inline bool apply(Turn const& turn, Original const& original, bool first = true) { boost::ignore_unused_variable_warning(first); if (turn.location != location_ok || turn.within_original) { // Skip all points already processed - return; + return true; } if (geometry::disjoint(turn.robust_point, original.m_box)) { // Skip all disjoint - return; + return true; } int const code = point_in_original(turn.robust_point, original); if (code == -1) { - return; + return true; } Turn& mutable_turn = m_mutable_turns[turn.turn_index]; @@ -259,6 +259,8 @@ public: mutable_turn.within_original = true; mutable_turn.count_in_original = 1; } + + return true; } private : diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp index d7146befb9..d23a3b3fd6 100644 --- a/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp +++ b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2016. // Modifications copyright (c) 2016 Oracle and/or its affiliates. @@ -686,32 +687,32 @@ public: {} template <typename Turn, typename Piece> - inline void apply(Turn const& turn, Piece const& piece, bool first = true) + inline bool apply(Turn const& turn, Piece const& piece, bool first = true) { boost::ignore_unused_variable_warning(first); if (turn.count_within > 0) { // Already inside - no need to check again - return; + return true; } if (piece.type == strategy::buffer::buffered_flat_end || piece.type == strategy::buffer::buffered_concave) { // Turns cannot be located within flat-end or concave pieces - return; + return true; } if (! geometry::covered_by(turn.robust_point, piece.robust_envelope)) { // Easy check: if the turn is not in the envelope, we can safely return - return; + return true; } if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece)) { - return; + return true; } // TODO: mutable_piece to make some on-demand preparations in analyse @@ -733,11 +734,11 @@ public: if (cd < piece.robust_min_comparable_radius) { mutable_turn.count_within++; - return; + return true; } if (cd > piece.robust_max_comparable_radius) { - return; + return true; } } @@ -749,20 +750,20 @@ public: switch(analyse_code) { case analyse_disjoint : - return; + return true; case analyse_on_offsetted : mutable_turn.count_on_offsetted++; // value is not used anymore - return; + return true; case analyse_on_original_boundary : mutable_turn.count_on_original_boundary++; - return; + return true; case analyse_within : mutable_turn.count_within++; - return; + return true; #if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION) case analyse_near_offsetted : mutable_turn.count_within_near_offsetted++; - return; + return true; #endif default : break; @@ -790,6 +791,8 @@ public: { mutable_turn.count_within++; } + + return true; } }; diff --git a/boost/geometry/algorithms/detail/covered_by/implementation.hpp b/boost/geometry/algorithms/detail/covered_by/implementation.hpp new file mode 100644 index 0000000000..3df8b7783d --- /dev/null +++ b/boost/geometry/algorithms/detail/covered_by/implementation.hpp @@ -0,0 +1,280 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013, 2014, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_COVERED_BY_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COVERED_BY_IMPLEMENTATION_HPP + + +#include <cstddef> + +#include <boost/geometry/algorithms/detail/covered_by/interface.hpp> +#include <boost/geometry/algorithms/detail/within/implementation.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace covered_by { + +struct use_point_in_geometry +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + return detail::within::point_in_geometry(geometry1, geometry2, strategy) >= 0; + } +}; + +struct use_relate +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + typedef typename detail::de9im::static_mask_covered_by_type + < + Geometry1, Geometry2 + >::type covered_by_mask; + return geometry::relate(geometry1, geometry2, covered_by_mask(), strategy); + } +}; + +}} // namespace detail::covered_by +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename Point, typename Box> +struct covered_by<Point, Box, point_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Box const& box, Strategy const& strategy) + { + ::boost::ignore_unused_variable_warning(strategy); + return strategy.apply(point, box); + } +}; + +template <typename Box1, typename Box2> +struct covered_by<Box1, Box2, box_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + assert_dimension_equal<Box1, Box2>(); + ::boost::ignore_unused_variable_warning(strategy); + return strategy.apply(box1, box2); + } +}; + + +// P/P + +template <typename Point1, typename Point2> +struct covered_by<Point1, Point2, point_tag, point_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename Point, typename MultiPoint> +struct covered_by<Point, MultiPoint, point_tag, multi_point_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Point> +struct covered_by<MultiPoint, Point, multi_point_tag, point_tag> + : public detail::within::multi_point_point +{}; + +template <typename MultiPoint1, typename MultiPoint2> +struct covered_by<MultiPoint1, MultiPoint2, multi_point_tag, multi_point_tag> + : public detail::within::multi_point_multi_point +{}; + +// P/L + +template <typename Point, typename Segment> +struct covered_by<Point, Segment, point_tag, segment_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename Point, typename Linestring> +struct covered_by<Point, Linestring, point_tag, linestring_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename Point, typename MultiLinestring> +struct covered_by<Point, MultiLinestring, point_tag, multi_linestring_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Segment> +struct covered_by<MultiPoint, Segment, multi_point_tag, segment_tag> + : public detail::within::multi_point_single_geometry<false> +{}; + +template <typename MultiPoint, typename Linestring> +struct covered_by<MultiPoint, Linestring, multi_point_tag, linestring_tag> + : public detail::within::multi_point_single_geometry<false> +{}; + +template <typename MultiPoint, typename MultiLinestring> +struct covered_by<MultiPoint, MultiLinestring, multi_point_tag, multi_linestring_tag> + : public detail::within::multi_point_multi_geometry<false> +{}; + +// P/A + +template <typename Point, typename Ring> +struct covered_by<Point, Ring, point_tag, ring_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename Point, typename Polygon> +struct covered_by<Point, Polygon, point_tag, polygon_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename Point, typename MultiPolygon> +struct covered_by<Point, MultiPolygon, point_tag, multi_polygon_tag> + : public detail::covered_by::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Ring> +struct covered_by<MultiPoint, Ring, multi_point_tag, ring_tag> + : public detail::within::multi_point_single_geometry<false> +{}; + +template <typename MultiPoint, typename Polygon> +struct covered_by<MultiPoint, Polygon, multi_point_tag, polygon_tag> + : public detail::within::multi_point_single_geometry<false> +{}; + +template <typename MultiPoint, typename MultiPolygon> +struct covered_by<MultiPoint, MultiPolygon, multi_point_tag, multi_polygon_tag> + : public detail::within::multi_point_multi_geometry<false> +{}; + +// L/L + +template <typename Linestring1, typename Linestring2> +struct covered_by<Linestring1, Linestring2, linestring_tag, linestring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Linestring, typename MultiLinestring> +struct covered_by<Linestring, MultiLinestring, linestring_tag, multi_linestring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiLinestring, typename Linestring> +struct covered_by<MultiLinestring, Linestring, multi_linestring_tag, linestring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiLinestring1, typename MultiLinestring2> +struct covered_by<MultiLinestring1, MultiLinestring2, multi_linestring_tag, multi_linestring_tag> + : public detail::covered_by::use_relate +{}; + +// L/A + +template <typename Linestring, typename Ring> +struct covered_by<Linestring, Ring, linestring_tag, ring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiLinestring, typename Ring> +struct covered_by<MultiLinestring, Ring, multi_linestring_tag, ring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Linestring, typename Polygon> +struct covered_by<Linestring, Polygon, linestring_tag, polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiLinestring, typename Polygon> +struct covered_by<MultiLinestring, Polygon, multi_linestring_tag, polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Linestring, typename MultiPolygon> +struct covered_by<Linestring, MultiPolygon, linestring_tag, multi_polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiLinestring, typename MultiPolygon> +struct covered_by<MultiLinestring, MultiPolygon, multi_linestring_tag, multi_polygon_tag> + : public detail::covered_by::use_relate +{}; + +// A/A + +template <typename Ring1, typename Ring2> +struct covered_by<Ring1, Ring2, ring_tag, ring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Ring, typename Polygon> +struct covered_by<Ring, Polygon, ring_tag, polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Polygon, typename Ring> +struct covered_by<Polygon, Ring, polygon_tag, ring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Polygon1, typename Polygon2> +struct covered_by<Polygon1, Polygon2, polygon_tag, polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Ring, typename MultiPolygon> +struct covered_by<Ring, MultiPolygon, ring_tag, multi_polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiPolygon, typename Ring> +struct covered_by<MultiPolygon, Ring, multi_polygon_tag, ring_tag> + : public detail::covered_by::use_relate +{}; + +template <typename Polygon, typename MultiPolygon> +struct covered_by<Polygon, MultiPolygon, polygon_tag, multi_polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiPolygon, typename Polygon> +struct covered_by<MultiPolygon, Polygon, multi_polygon_tag, polygon_tag> + : public detail::covered_by::use_relate +{}; + +template <typename MultiPolygon1, typename MultiPolygon2> +struct covered_by<MultiPolygon1, MultiPolygon2, multi_polygon_tag, multi_polygon_tag> + : public detail::covered_by::use_relate +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COVERED_BY_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/covered_by/interface.hpp b/boost/geometry/algorithms/detail/covered_by/interface.hpp new file mode 100644 index 0000000000..6599078210 --- /dev/null +++ b/boost/geometry/algorithms/detail/covered_by/interface.hpp @@ -0,0 +1,261 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013, 2014, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_COVERED_BY_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COVERED_BY_INTERFACE_HPP + + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + +#include <boost/geometry/algorithms/detail/within/interface.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/strategies/cartesian/point_in_box.hpp> +#include <boost/geometry/strategies/cartesian/box_in_box.hpp> +#include <boost/geometry/strategies/default_strategy.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type +> +struct covered_by + : not_implemented<Tag1, Tag2> +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy { + +struct covered_by +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + concepts::within::check + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + Strategy + >(); + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + return dispatch::covered_by<Geometry1, Geometry2>::apply(geometry1, + geometry2, + strategy); + } + + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename strategy::covered_by::services::default_strategy + < + Geometry1, + Geometry2 + >::type strategy_type; + + return covered_by::apply(geometry1, geometry2, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant { + +template <typename Geometry1, typename Geometry2> +struct covered_by +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return resolve_strategy::covered_by + ::apply(geometry1, geometry2, strategy); + } +}; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T), typename Geometry2> +struct covered_by<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Geometry2> +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, Strategy const& strategy) + : m_geometry2(geometry2), m_strategy(strategy) {} + + template <typename Geometry1> + bool operator()(Geometry1 const& geometry1) const + { + return covered_by<Geometry1, Geometry2> + ::apply(geometry1, m_geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry2, strategy), geometry1); + } +}; + +template <typename Geometry1, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct covered_by<Geometry1, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, Strategy const& strategy) + : m_geometry1(geometry1), m_strategy(strategy) {} + + template <typename Geometry2> + bool operator()(Geometry2 const& geometry2) const + { + return covered_by<Geometry1, Geometry2> + ::apply(m_geometry1, geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(Geometry1 const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry1, strategy), geometry2); + } +}; + +template < + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) +> +struct covered_by< + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> +> +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + + template <typename Geometry1, typename Geometry2> + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return covered_by<Geometry1, Geometry2> + ::apply(geometry1, geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(strategy), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_check12{is inside or on border} +\ingroup covered_by +\details \details_check12{covered_by, is inside or on border}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be inside or on the border of the second geometry +\param geometry2 \param_geometry which might cover the first geometry +\return true if geometry1 is inside of or on the border of geometry2, + else false +\note The default strategy is used for covered_by detection + +\qbk{[include reference/algorithms/covered_by.qbk]} + + */ +template<typename Geometry1, typename Geometry2> +inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + return resolve_variant::covered_by<Geometry1, Geometry2> + ::apply(geometry1, geometry2, default_strategy()); +} + +/*! +\brief \brief_check12{is inside or on border} \brief_strategy +\ingroup covered_by +\details \details_check12{covered_by, is inside or on border}, \brief_strategy. \details_strategy_reasons +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be inside or on the border of the second geometry +\param geometry2 \param_geometry which might cover the first geometry +\param strategy strategy to be used +\return true if geometry1 is inside of or on the border of geometry2, + else false + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/covered_by.qbk]} + +*/ +template<typename Geometry1, typename Geometry2, typename Strategy> +inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + return resolve_variant::covered_by<Geometry1, Geometry2> + ::apply(geometry1, geometry2, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COVERED_BY_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/direction_code.hpp b/boost/geometry/algorithms/detail/direction_code.hpp index 26d53ab4e5..c5c5221109 100644 --- a/boost/geometry/algorithms/detail/direction_code.hpp +++ b/boost/geometry/algorithms/detail/direction_code.hpp @@ -2,10 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,14 +18,22 @@ #include <boost/geometry/core/access.hpp> #include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/select_coordinate_type.hpp> +#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp> + +#include <boost/mpl/assert.hpp> + namespace boost { namespace geometry { + #ifndef DOXYGEN_NO_DETAIL namespace detail { + +// TODO: remove template <std::size_t Index, typename Point1, typename Point2> inline int sign_of_difference(Point1 const& point1, Point2 const& point2) { @@ -37,35 +46,204 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) } -// Gives sense of direction for point p, collinear w.r.t. segment (a,b) -// Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a -// Returns 1 if p goes forward, so extends (a,b) -// Returns 0 if p is equal with b, or if (a,b) is degenerate -// Note that it does not do any collinearity test, that should be done before -template <typename Point1, typename Point2> -inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, - const Point2& p) +template <typename Point, typename CSTag = typename cs_tag<Point>::type> +struct direction_code_impl +{ + BOOST_MPL_ASSERT_MSG((false), NOT_IMPLEMENTED_FOR_THIS_CS, (CSTag)); +}; + +template <typename Point> +struct direction_code_impl<Point, cartesian_tag> { - // Suppose segment = (4 3,4 4) and p =(4 2) - // Then sign_a1 = 1 and sign_p1 = 1 -> goes backward -> return -1 + template <typename Point1, typename Point2> + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) + { + typedef typename geometry::select_coordinate_type + < + Point1, Point2 + >::type calc_t; + + if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) + || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(p)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(p))) ) + { + return 0; + } + + calc_t x1 = geometry::get<0>(segment_b) - geometry::get<0>(segment_a); + calc_t y1 = geometry::get<1>(segment_b) - geometry::get<1>(segment_a); + calc_t x2 = geometry::get<0>(segment_b) - geometry::get<0>(p); + calc_t y2 = geometry::get<1>(segment_b) - geometry::get<1>(p); + + calc_t ax = (std::min)(math::abs(x1), math::abs(x2)); + calc_t ay = (std::min)(math::abs(y1), math::abs(y2)); + + int s1 = 0, s2 = 0; + if (ax >= ay) + { + s1 = x1 > 0 ? 1 : -1; + s2 = x2 > 0 ? 1 : -1; + } + else + { + s1 = y1 > 0 ? 1 : -1; + s2 = y2 > 0 ? 1 : -1; + } - int const sign_a0 = sign_of_difference<0>(segment_b, segment_a); - int const sign_a1 = sign_of_difference<1>(segment_b, segment_a); + return s1 == s2 ? -1 : 1; + } +}; - if (sign_a0 == 0 && sign_a1 == 0) +template <typename Point> +struct direction_code_impl<Point, spherical_equatorial_tag> +{ + template <typename Point1, typename Point2> + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) { - return 0; + typedef typename coordinate_type<Point1>::type coord1_t; + typedef typename coordinate_type<Point2>::type coord2_t; + typedef typename coordinate_system<Point1>::type::units units_t; + typedef typename coordinate_system<Point2>::type::units units2_t; + BOOST_MPL_ASSERT_MSG((boost::is_same<units_t, units2_t>::value), + NOT_IMPLEMENTED_FOR_DIFFERENT_UNITS, + (units_t, units2_t)); + + typedef typename geometry::select_coordinate_type <Point1, Point2>::type calc_t; + typedef math::detail::constants_on_spheroid<coord1_t, units_t> constants1; + typedef math::detail::constants_on_spheroid<coord2_t, units_t> constants2; + typedef math::detail::constants_on_spheroid<calc_t, units_t> constants; + + coord1_t const a0 = geometry::get<0>(segment_a); + coord1_t const a1 = geometry::get<1>(segment_a); + coord1_t const b0 = geometry::get<0>(segment_b); + coord1_t const b1 = geometry::get<1>(segment_b); + coord2_t const p0 = geometry::get<0>(p); + coord2_t const p1 = geometry::get<1>(p); + coord1_t const pi_half1 = constants1::max_latitude(); + coord2_t const pi_half2 = constants2::max_latitude(); + calc_t const pi = constants::half_period(); + calc_t const pi_half = constants::max_latitude(); + calc_t const c0 = 0; + + if ( (math::equals(b0, a0) && math::equals(b1, a1)) + || (math::equals(b0, p0) && math::equals(b1, p1)) ) + { + return 0; + } + + bool const is_a_pole = math::equals(pi_half1, math::abs(a1)); + bool const is_b_pole = math::equals(pi_half1, math::abs(b1)); + bool const is_p_pole = math::equals(pi_half2, math::abs(p1)); + + if ( is_b_pole && ((is_a_pole && math::sign(b1) == math::sign(a1)) + || (is_p_pole && math::sign(b1) == math::sign(p1))) ) + { + return 0; + } + + // NOTE: as opposed to the implementation for cartesian CS + // here point b is the origin + + calc_t const dlon1 = math::longitude_distance_signed<units_t>(b0, a0); + calc_t const dlon2 = math::longitude_distance_signed<units_t>(b0, p0); + + bool is_antilon1 = false, is_antilon2 = false; + calc_t const dlat1 = latitude_distance_signed(b1, a1, dlon1, pi, is_antilon1); + calc_t const dlat2 = latitude_distance_signed(b1, p1, dlon2, pi, is_antilon2); + + calc_t mx = is_a_pole || is_b_pole || is_p_pole ? + c0 : + (std::min)(is_antilon1 ? c0 : math::abs(dlon1), + is_antilon2 ? c0 : math::abs(dlon2)); + calc_t my = (std::min)(math::abs(dlat1), + math::abs(dlat2)); + + int s1 = 0, s2 = 0; + if (mx >= my) + { + s1 = dlon1 > 0 ? 1 : -1; + s2 = dlon2 > 0 ? 1 : -1; + } + else + { + s1 = dlat1 > 0 ? 1 : -1; + s2 = dlat2 > 0 ? 1 : -1; + } + + return s1 == s2 ? -1 : 1; } - int const sign_p0 = sign_of_difference<0>(segment_b, p); - int const sign_p1 = sign_of_difference<1>(segment_b, p); + template <typename T> + static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, T const& pi, bool & is_antilon) + { + T const c0 = 0; + + T res = lat2 - lat1; + + is_antilon = math::equals(math::abs(lon_ds), pi); + if (is_antilon) + { + res = lat2 + lat1; + if (res >= c0) + res = pi - res; + else + res = -pi - res; + } - if (sign_p0 == 0 && sign_p1 == 0) + return res; + } +}; + +template <typename Point> +struct direction_code_impl<Point, spherical_polar_tag> +{ + template <typename Point1, typename Point2> + static inline int apply(Point1 segment_a, Point1 segment_b, + Point2 p) { - return 0; + typedef math::detail::constants_on_spheroid + < + typename coordinate_type<Point1>::type, + typename coordinate_system<Point1>::type::units + > constants1; + typedef math::detail::constants_on_spheroid + < + typename coordinate_type<Point2>::type, + typename coordinate_system<Point2>::type::units + > constants2; + + geometry::set<1>(segment_a, + constants1::max_latitude() - geometry::get<1>(segment_a)); + geometry::set<1>(segment_b, + constants1::max_latitude() - geometry::get<1>(segment_b)); + geometry::set<1>(p, + constants2::max_latitude() - geometry::get<1>(p)); + + return direction_code_impl + < + Point, spherical_equatorial_tag + >::apply(segment_a, segment_b, p); } +}; + +template <typename Point> +struct direction_code_impl<Point, geographic_tag> + : direction_code_impl<Point, spherical_equatorial_tag> +{}; - return sign_a0 == sign_p0 && sign_a1 == sign_p1 ? -1 : 1; +// Gives sense of direction for point p, collinear w.r.t. segment (a,b) +// Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a +// Returns 1 if p goes forward, so extends (a,b) +// Returns 0 if p is equal with b, or if (a,b) is degenerate +// Note that it does not do any collinearity test, that should be done before +template <typename Point1, typename Point2> +inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) +{ + return direction_code_impl<Point1>::apply(segment_a, segment_b, p); } @@ -73,7 +251,6 @@ inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, #endif //DOXYGEN_NO_DETAIL - }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/boost/geometry/algorithms/detail/disjoint/box_box.hpp index f830f8161c..87618939b8 100644 --- a/boost/geometry/algorithms/detail/disjoint/box_box.hpp +++ b/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -121,9 +121,18 @@ struct box_box<Box1, Box2, 0, DimensionCount, spherical_tag> // calculate positive longitude translation with b1_min as origin calc_t const diff_min = math::longitude_distance_unsigned<units_t>(b1_min, b2_min); calc_t const b2_min_transl = b1_min + diff_min; // always right of b1_min + calc_t b2_max_transl = b2_min_transl - constants::period() + diff2; - if (b2_min_transl > b1_max // b2_min right of b1_max - && b2_min_transl - constants::period() + diff2 < b1_min) // b2_max left of b1_min + // if the translation is too close then use the original point + // note that math::abs(b2_max_transl - b2_max) takes values very + // close to k*2*constants::period() for k=0,1,2,... + if (math::abs(b2_max_transl - b2_max) < constants::period() / 2) + { + b2_max_transl = b2_max; + } + + if (b2_min_transl > b1_max // b2_min right of b1_max + && b2_max_transl < b1_min) // b2_max left of b1_min { return true; } diff --git a/boost/geometry/algorithms/detail/disjoint/implementation.hpp b/boost/geometry/algorithms/detail/disjoint/implementation.hpp index d482c4a5a9..3a3d9865d1 100644 --- a/boost/geometry/algorithms/detail/disjoint/implementation.hpp +++ b/boost/geometry/algorithms/detail/disjoint/implementation.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2014 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013-2014. -// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2017. +// Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle diff --git a/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp b/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp index 7c1a93cdb7..f8d3e3c593 100644 --- a/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp +++ b/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2014-2017, Oracle and/or its affiliates. +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -30,7 +31,9 @@ #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> #include <boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_geometry.hpp> #include <boost/geometry/algorithms/detail/relate/less.hpp> @@ -117,15 +120,21 @@ private: } }; - // TODO: After adding non-cartesian Segment envelope to the library - // this policy should be modified to take envelope strategy. + template <typename EnvelopeStrategy> struct expand_box_segment { + explicit expand_box_segment(EnvelopeStrategy const& strategy) + : m_strategy(strategy) + {} + template <typename Box, typename Segment> - static inline void apply(Box& total, Segment const& segment) + inline void apply(Box& total, Segment const& segment) const { - geometry::expand(total, geometry::return_envelope<Box>(segment)); + geometry::expand(total, + geometry::return_envelope<Box>(segment, m_strategy)); } + + EnvelopeStrategy const& m_strategy; }; struct overlaps_box_point @@ -134,32 +143,24 @@ private: static inline bool apply(Box const& box, Point const& point) { // The default strategy is enough in this case - typedef typename strategy::disjoint::services::default_strategy - < - Point, Box - >::type strategy_type; - return ! dispatch::disjoint<Point, Box>::apply(point, box, strategy_type()); + return ! detail::disjoint::disjoint_point_box(point, box); } }; - // TODO: After implementing disjoint Segment/Box for non-cartesian geometries - // this strategy should be passed here. - // TODO: This Segment/Box strategy should somehow be derived from Point/Segment strategy - // which by default is winding containing CS-specific side strategy - // TODO: disjoint Segment/Box will be called in this case which may take - // quite long in non-cartesian CS. So we should consider passing range of bounding boxes - // of segments after calculating them once. + template <typename DisjointStrategy> struct overlaps_box_segment { + explicit overlaps_box_segment(DisjointStrategy const& strategy) + : m_strategy(strategy) + {} + template <typename Box, typename Segment> - static inline bool apply(Box const& box, Segment const& segment) + inline bool apply(Box const& box, Segment const& segment) const { - typedef typename strategy::disjoint::services::default_strategy - < - Segment, Box - >::type strategy_type; - return ! dispatch::disjoint<Segment, Box>::apply(segment, box, strategy_type()); + return ! dispatch::disjoint<Segment, Box>::apply(segment, box, m_strategy); } + + DisjointStrategy const& m_strategy; }; template <typename PtSegStrategy> @@ -172,13 +173,15 @@ private: {} template <typename Item1, typename Item2> - inline void apply(Item1 const& item1, Item2 const& item2) + inline bool apply(Item1 const& item1, Item2 const& item2) { if (! m_intersection_found && ! dispatch::disjoint<Item1, Item2>::apply(item1, item2, m_strategy)) { m_intersection_found = true; + return false; } + return true; } inline bool intersection_found() const { return m_intersection_found; } @@ -219,12 +222,22 @@ public: { item_visitor_type<Strategy> visitor(strategy); + typedef typename Strategy::envelope_strategy_type envelope_strategy_type; + typedef typename Strategy::disjoint_strategy_type disjoint_strategy_type; + + // TODO: disjoint Segment/Box may be called in partition multiple times + // possibly for non-cartesian segments which could be slow. We should consider + // passing a range of bounding boxes of segments after calculating them once. + // Alternatively instead of a range of segments a range of Segment/Envelope pairs + // should be passed, where envelope would be lazily calculated when needed the first time geometry::partition < geometry::model::box<typename point_type<MultiPoint>::type> >::apply(multipoint, segment_range(linear), visitor, - expand_box_point(), overlaps_box_point(), - expand_box_segment(), overlaps_box_segment()); + expand_box_point(), + overlaps_box_point(), + expand_box_segment<envelope_strategy_type>(strategy.get_envelope_strategy()), + overlaps_box_segment<disjoint_strategy_type>(strategy.get_disjoint_strategy())); return ! visitor.intersection_found(); } @@ -237,6 +250,176 @@ public: }; +template <typename MultiPoint, typename SingleGeometry> +class multi_point_single_geometry +{ +public: + template <typename Strategy> + static inline bool apply(MultiPoint const& multi_point, SingleGeometry const& single_geometry, Strategy const& strategy) + { + typedef typename point_type<MultiPoint>::type point1_type; + typedef typename point_type<SingleGeometry>::type point2_type; + typedef model::box<point2_type> box2_type; + + box2_type box2; + geometry::envelope(single_geometry, box2, strategy.get_envelope_strategy()); + geometry::detail::expand_by_epsilon(box2); + + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + // The default strategy is enough for Point/Box + if (! detail::disjoint::disjoint_point_box(*it, box2) + && ! dispatch::disjoint<point1_type, SingleGeometry>::apply(*it, single_geometry, strategy)) + { + return false; + } + } + + return true; + } + + template <typename Strategy> + static inline bool apply(SingleGeometry const& single_geometry, MultiPoint const& multi_point, Strategy const& strategy) + { + return apply(multi_point, single_geometry, strategy); + } +}; + + +template <typename MultiPoint, typename MultiGeometry> +class multi_point_multi_geometry +{ +private: + struct expand_box_point + { + template <typename Box, typename Point> + static inline void apply(Box& total, Point const& point) + { + geometry::expand(total, point); + } + }; + + struct expand_box_box_pair + { + template <typename Box, typename BoxPair> + inline void apply(Box& total, BoxPair const& box_pair) const + { + geometry::expand(total, box_pair.first); + } + }; + + struct overlaps_box_point + { + template <typename Box, typename Point> + static inline bool apply(Box const& box, Point const& point) + { + // The default strategy is enough for Point/Box + return ! detail::disjoint::disjoint_point_box(point, box); + } + }; + + struct overlaps_box_box_pair + { + template <typename Box, typename BoxPair> + inline bool apply(Box const& box, BoxPair const& box_pair) const + { + // The default strategy is enough for Box/Box + return ! detail::disjoint::disjoint_box_box(box_pair.first, box); + } + }; + + template <typename PtSegStrategy> + class item_visitor_type + { + public: + item_visitor_type(MultiGeometry const& multi_geometry, + PtSegStrategy const& strategy) + : m_intersection_found(false) + , m_multi_geometry(multi_geometry) + , m_strategy(strategy) + {} + + template <typename Point, typename BoxPair> + inline bool apply(Point const& point, BoxPair const& box_pair) + { + typedef typename boost::range_value<MultiGeometry>::type single_type; + + // The default strategy is enough for Point/Box + if (! m_intersection_found + && ! detail::disjoint::disjoint_point_box(point, box_pair.first) + && ! dispatch::disjoint<Point, single_type>::apply(point, range::at(m_multi_geometry, box_pair.second), m_strategy)) + { + m_intersection_found = true; + return false; + } + return true; + } + + inline bool intersection_found() const { return m_intersection_found; } + + private: + bool m_intersection_found; + MultiGeometry const& m_multi_geometry; + PtSegStrategy const& m_strategy; + }; + // structs for partition -- end + +public: + template <typename Strategy> + static inline bool apply(MultiPoint const& multi_point, MultiGeometry const& multi_geometry, Strategy const& strategy) + { + typedef typename point_type<MultiPoint>::type point1_type; + typedef typename point_type<MultiGeometry>::type point2_type; + typedef model::box<point1_type> box1_type; + typedef model::box<point2_type> box2_type; + typedef std::pair<box2_type, std::size_t> box_pair_type; + + typename Strategy::envelope_strategy_type const + envelope_strategy = strategy.get_envelope_strategy(); + + std::size_t count2 = boost::size(multi_geometry); + std::vector<box_pair_type> boxes(count2); + for (std::size_t i = 0 ; i < count2 ; ++i) + { + geometry::envelope(range::at(multi_geometry, i), boxes[i].first, envelope_strategy); + geometry::detail::expand_by_epsilon(boxes[i].first); + boxes[i].second = i; + } + + item_visitor_type<Strategy> visitor(multi_geometry, strategy); + + geometry::partition + < + box1_type + >::apply(multi_point, boxes, visitor, + expand_box_point(), + overlaps_box_point(), + expand_box_box_pair(), + overlaps_box_box_pair()); + + return ! visitor.intersection_found(); + } + + template <typename Strategy> + static inline bool apply(MultiGeometry const& multi_geometry, MultiPoint const& multi_point, Strategy const& strategy) + { + return apply(multi_point, multi_geometry, strategy); + } +}; + + +template <typename MultiPoint, typename Areal, typename Tag = typename tag<Areal>::type> +struct multipoint_areal + : multi_point_single_geometry<MultiPoint, Areal> +{}; + +template <typename MultiPoint, typename Areal> +struct multipoint_areal<MultiPoint, Areal, multi_polygon_tag> + : multi_point_multi_geometry<MultiPoint, Areal> +{}; + + }} // namespace detail::disjoint #endif // DOXYGEN_NO_DETAIL @@ -321,6 +504,22 @@ struct disjoint {}; +template <typename Areal, typename MultiPoint, std::size_t DimensionCount> +struct disjoint + < + Areal, MultiPoint, DimensionCount, areal_tag, multi_point_tag, false + > : detail::disjoint::multipoint_areal<MultiPoint, Areal> +{}; + + +template <typename MultiPoint, typename Areal, std::size_t DimensionCount> +struct disjoint + < + MultiPoint, Areal, DimensionCount, multi_point_tag, areal_tag, false + > : detail::disjoint::multipoint_areal<MultiPoint, Areal> +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH diff --git a/boost/geometry/algorithms/detail/disjoint/segment_box.hpp b/boost/geometry/algorithms/detail/disjoint/segment_box.hpp index c2741ce72c..fe849e1091 100644 --- a/boost/geometry/algorithms/detail/disjoint/segment_box.hpp +++ b/boost/geometry/algorithms/detail/disjoint/segment_box.hpp @@ -8,8 +8,9 @@ // This file was modified by Oracle on 2013-2017. // Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -24,9 +25,18 @@ #include <cstddef> #include <boost/geometry/core/tags.hpp> +#include <boost/geometry/core/radian_access.hpp> +#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> +#include <boost/geometry/algorithms/detail/envelope/segment.hpp> +#include <boost/geometry/algorithms/detail/normalize.hpp> #include <boost/geometry/algorithms/dispatch/disjoint.hpp> +#include <boost/geometry/formulas/vertex_longitude.hpp> + +#include <boost/geometry/geometries/box.hpp> namespace boost { namespace geometry { @@ -36,10 +46,161 @@ namespace boost { namespace geometry namespace detail { namespace disjoint { +template <typename CS_Tag> +struct disjoint_segment_box_sphere_or_spheroid +{ +private: + + template <typename CT> + static inline void swap(CT& lon1, + CT& lat1, + CT& lon2, + CT& lat2) + { + std::swap(lon1, lon2); + std::swap(lat1, lat2); + } + + +public: + + template <typename Segment, typename Box, typename Strategy> + static inline bool apply(Segment const& segment, + Box const& box, + Strategy const& azimuth_strategy) + { + assert_dimension_equal<Segment, Box>(); + + typedef typename point_type<Segment>::type segment_point_type; + typedef typename cs_tag<Segment>::type segment_cs_type; + + segment_point_type p0, p1; + geometry::detail::assign_point_from_index<0>(segment, p0); + geometry::detail::assign_point_from_index<1>(segment, p1); + + // Simplest cases first + + // Case 1: if box contains one of segment's endpoints then they are not disjoint + if (! disjoint_point_box(p0, box) || ! disjoint_point_box(p1, box)) + { + return false; + } + + // Case 2: disjoint if bounding boxes are disjoint + + typedef typename coordinate_type<segment_point_type>::type CT; + + segment_point_type p0_normalized = + geometry::detail::return_normalized<segment_point_type>(p0); + segment_point_type p1_normalized = + geometry::detail::return_normalized<segment_point_type>(p1); + + CT lon1 = geometry::get_as_radian<0>(p0_normalized); + CT lat1 = geometry::get_as_radian<1>(p0_normalized); + CT lon2 = geometry::get_as_radian<0>(p1_normalized); + CT lat2 = geometry::get_as_radian<1>(p1_normalized); + + if (lon1 > lon2) + { + swap(lon1, lat1, lon2, lat2); + } + + //Compute alp1 outside envelope and pass it to envelope_segment_impl + //in order for it to be used later in the algorithm + CT alp1; + + azimuth_strategy.apply(lon1, lat1, lon2, lat2, alp1); + + geometry::model::box<segment_point_type> box_seg; + + geometry::detail::envelope::envelope_segment_impl<segment_cs_type> + ::template apply<geometry::radian>(lon1, lat1, + lon2, lat2, + box_seg, + azimuth_strategy, + alp1); + if (disjoint_box_box(box, box_seg)) + { + return true; + } + + // Case 3: test intersection by comparing angles + + CT a_b0, a_b1, a_b2, a_b3; + + CT b_lon_min = geometry::get_as_radian<geometry::min_corner, 0>(box); + CT b_lat_min = geometry::get_as_radian<geometry::min_corner, 1>(box); + CT b_lon_max = geometry::get_as_radian<geometry::max_corner, 0>(box); + CT b_lat_max = geometry::get_as_radian<geometry::max_corner, 1>(box); + + azimuth_strategy.apply(lon1, lat1, b_lon_min, b_lat_min, a_b0); + azimuth_strategy.apply(lon1, lat1, b_lon_max, b_lat_min, a_b1); + azimuth_strategy.apply(lon1, lat1, b_lon_min, b_lat_max, a_b2); + azimuth_strategy.apply(lon1, lat1, b_lon_max, b_lat_max, a_b3); + + bool b0 = alp1 > a_b0; + bool b1 = alp1 > a_b1; + bool b2 = alp1 > a_b2; + bool b3 = alp1 > a_b3; + + // if not all box points on the same side of the segment then + // there is an intersection + if (!(b0 && b1 && b2 && b3) && (b0 || b1 || b2 || b3)) + { + return false; + } + + // Case 4: The only intersection case not covered above is when all four + // points of the box are above (below) the segment in northern (southern) + // hemisphere. Then we have to compute the vertex of the segment + + CT vertex_lat; + CT lat_sum = lat1 + lat2; + + if ((b0 && b1 && b2 && b3 && lat_sum > CT(0)) + || (!(b0 && b1 && b2 && b3) && lat_sum < CT(0))) + { + CT b_lat_below; //latitude of box closest to equator + + if (lat_sum > CT(0)) + { + vertex_lat = geometry::get_as_radian<geometry::max_corner, 1>(box_seg); + b_lat_below = b_lat_min; + } else { + vertex_lat = geometry::get_as_radian<geometry::min_corner, 1>(box_seg); + b_lat_below = b_lat_max; + } + + //optimization TODO: computing the spherical longitude should suffice for + // the majority of cases + CT vertex_lon = geometry::formula::vertex_longitude<CT, CS_Tag> + ::apply(lon1, lat1, + lon2, lat2, + vertex_lat, + alp1, + azimuth_strategy); + + // Check if the vertex point is within the band defined by the + // minimum and maximum longitude of the box; if yes, then return + // false if the point is above the min latitude of the box; return + // true in all other cases + if (vertex_lon >= b_lon_min && vertex_lon <= b_lon_max + && std::abs(vertex_lat) > std::abs(b_lat_below)) + { + return false; + } + } + + return true; + } +}; + struct disjoint_segment_box { template <typename Segment, typename Box, typename Strategy> - static inline bool apply(Segment const& segment, Box const& box, Strategy const& strategy) + static inline bool apply(Segment const& segment, + Box const& box, + Strategy const& strategy) { return strategy.apply(segment, box); } @@ -56,7 +217,7 @@ namespace dispatch template <typename Segment, typename Box, std::size_t DimensionCount> struct disjoint<Segment, Box, DimensionCount, segment_tag, box_tag, false> - : detail::disjoint::disjoint_segment_box + : detail::disjoint::disjoint_segment_box {}; diff --git a/boost/geometry/algorithms/detail/envelope/segment.hpp b/boost/geometry/algorithms/detail/envelope/segment.hpp index 7631e84883..7e37194968 100644 --- a/boost/geometry/algorithms/detail/envelope/segment.hpp +++ b/boost/geometry/algorithms/detail/envelope/segment.hpp @@ -130,19 +130,16 @@ private: CalculationType& lat1, CalculationType& lon2, CalculationType& lat2, + CalculationType a1, + CalculationType a2, Strategy const& strategy) { // coordinates are assumed to be in radians BOOST_GEOMETRY_ASSERT(lon1 <= lon2); - CalculationType lon1_rad = math::as_radian<Units>(lon1); CalculationType lat1_rad = math::as_radian<Units>(lat1); - CalculationType lon2_rad = math::as_radian<Units>(lon2); CalculationType lat2_rad = math::as_radian<Units>(lat2); - CalculationType a1, a2; - strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, a1, a2); - if (lat1 > lat2) { std::swap(lat1, lat2); @@ -189,12 +186,11 @@ private: } } - template <typename Units, typename CalculationType, typename Strategy> - static inline void apply(CalculationType& lon1, - CalculationType& lat1, - CalculationType& lon2, - CalculationType& lat2, - Strategy const& strategy) + template <typename Units, typename CalculationType> + static inline void special_cases(CalculationType& lon1, + CalculationType& lat1, + CalculationType& lon2, + CalculationType& lat2) { typedef math::detail::constants_on_spheroid < @@ -249,23 +245,19 @@ private: lon1 += constants::period(); swap(lon1, lat1, lon2, lat2); } - - compute_box_corners<Units>(lon1, lat1, lon2, lat2, strategy); } -public: - template < - typename Units, - typename CalculationType, - typename Box, - typename Strategy - > - static inline void apply(CalculationType lon1, - CalculationType lat1, - CalculationType lon2, - CalculationType lat2, - Box& mbr, - Strategy const& strategy) + template + < + typename Units, + typename CalculationType, + typename Box + > + static inline void create_box(CalculationType lon1, + CalculationType lat1, + CalculationType lon2, + CalculationType lat2, + Box& mbr) { typedef typename coordinate_type<Box>::type box_coordinate_type; @@ -276,8 +268,6 @@ public: helper_box_type radian_mbr; - apply<Units>(lon1, lat1, lon2, lat2, strategy); - geometry::set < min_corner, 0 @@ -300,6 +290,85 @@ public: transform_units(radian_mbr, mbr); } + + + template <typename Units, typename CalculationType, typename Strategy> + static inline void apply(CalculationType& lon1, + CalculationType& lat1, + CalculationType& lon2, + CalculationType& lat2, + Strategy const& strategy) + { + special_cases<Units>(lon1, lat1, lon2, lat2); + + CalculationType lon1_rad = math::as_radian<Units>(lon1); + CalculationType lat1_rad = math::as_radian<Units>(lat1); + CalculationType lon2_rad = math::as_radian<Units>(lon2); + CalculationType lat2_rad = math::as_radian<Units>(lat2); + CalculationType alp1, alp2; + strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2); + + compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy); + } + + template <typename Units, typename CalculationType, typename Strategy> + static inline void apply(CalculationType& lon1, + CalculationType& lat1, + CalculationType& lon2, + CalculationType& lat2, + Strategy const& strategy, + CalculationType alp1) + { + special_cases<Units>(lon1, lat1, lon2, lat2); + + CalculationType lon1_rad = math::as_radian<Units>(lon1); + CalculationType lat1_rad = math::as_radian<Units>(lat1); + CalculationType lon2_rad = math::as_radian<Units>(lon2); + CalculationType lat2_rad = math::as_radian<Units>(lat2); + CalculationType alp2; + strategy.apply(lon2_rad, lat2_rad, lon1_rad, lat1_rad, alp2); + alp2 += math::pi<CalculationType>(); + + compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy); + } + +public: + template + < + typename Units, + typename CalculationType, + typename Box, + typename Strategy + > + static inline void apply(CalculationType lon1, + CalculationType lat1, + CalculationType lon2, + CalculationType lat2, + Box& mbr, + Strategy const& strategy) + { + apply<Units>(lon1, lat1, lon2, lat2, strategy); + create_box<Units>(lon1, lat1, lon2, lat2, mbr); + } + + template + < + typename Units, + typename CalculationType, + typename Box, + typename Strategy + > + static inline void apply(CalculationType lon1, + CalculationType lat1, + CalculationType lon2, + CalculationType lat2, + Box& mbr, + Strategy const& strategy, + CalculationType alp1) + { + apply<Units>(lon1, lat1, lon2, lat2, strategy, alp1); + create_box<Units>(lon1, lat1, lon2, lat2, mbr); + } }; template <std::size_t Dimension, std::size_t DimensionCount> diff --git a/boost/geometry/algorithms/detail/equals/implementation.hpp b/boost/geometry/algorithms/detail/equals/implementation.hpp new file mode 100644 index 0000000000..310059a427 --- /dev/null +++ b/boost/geometry/algorithms/detail/equals/implementation.hpp @@ -0,0 +1,397 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. +// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014, 2015, 2016, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_EQUALS_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_IMPLEMENTATION_HPP + + +#include <cstddef> +#include <vector> + +#include <boost/range.hpp> +#include <boost/type_traits/is_base_of.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/detail/equals/point_point.hpp> + +// For trivial checks +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/length.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/select_coordinate_type.hpp> +#include <boost/geometry/util/select_most_precise.hpp> + +#include <boost/geometry/algorithms/detail/equals/collect_vectors.hpp> +#include <boost/geometry/algorithms/detail/equals/interface.hpp> +#include <boost/geometry/algorithms/detail/relate/relate_impl.hpp> +#include <boost/geometry/algorithms/relate.hpp> + +#include <boost/geometry/views/detail/indexed_point_view.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace equals +{ + + +template +< + std::size_t Dimension, + std::size_t DimensionCount +> +struct point_point +{ + template <typename Point1, typename Point2, typename Strategy> + static inline bool apply(Point1 const& point1, Point2 const& point2, Strategy const& strategy) + { + return ! detail::disjoint::point_point + < + Point1, Point2, + Dimension, DimensionCount + >::apply(point1, point2, strategy); + } +}; + + +template +< + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_box +{ + template <typename Box1, typename Box2, typename Strategy> + static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + if (!geometry::math::equals(get<min_corner, Dimension>(box1), get<min_corner, Dimension>(box2)) + || !geometry::math::equals(get<max_corner, Dimension>(box1), get<max_corner, Dimension>(box2))) + { + return false; + } + return box_box<Dimension + 1, DimensionCount>::apply(box1, box2, strategy); + } +}; + +template <std::size_t DimensionCount> +struct box_box<DimensionCount, DimensionCount> +{ + template <typename Box1, typename Box2, typename Strategy> + static inline bool apply(Box1 const& , Box2 const& , Strategy const& ) + { + return true; + } +}; + + +struct segment_segment +{ + template <typename Segment1, typename Segment2, typename Strategy> + static inline bool apply(Segment1 const& segment1, Segment2 const& segment2, Strategy const& ) + { + return equals::equals_point_point( + indexed_point_view<Segment1 const, 0>(segment1), + indexed_point_view<Segment2 const, 0>(segment2) ) + ? equals::equals_point_point( + indexed_point_view<Segment1 const, 1>(segment1), + indexed_point_view<Segment2 const, 1>(segment2) ) + : ( equals::equals_point_point( + indexed_point_view<Segment1 const, 0>(segment1), + indexed_point_view<Segment2 const, 1>(segment2) ) + && equals::equals_point_point( + indexed_point_view<Segment1 const, 1>(segment1), + indexed_point_view<Segment2 const, 0>(segment2) ) + ); + } +}; + + +struct area_check +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return geometry::math::equals( + geometry::area(geometry1, + strategy.template get_area_strategy<Geometry1>()), + geometry::area(geometry2, + strategy.template get_area_strategy<Geometry2>())); + } +}; + + +struct length_check +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return geometry::math::equals( + geometry::length(geometry1, + strategy.template get_distance_strategy<Geometry1>()), + geometry::length(geometry2, + strategy.template get_distance_strategy<Geometry2>())); + } +}; + + +template <typename Geometry1, typename Geometry2, typename IntersectionStrategy> +struct collected_vector +{ + typedef typename geometry::select_most_precise + < + typename select_coordinate_type + < + Geometry1, Geometry2 + >::type, + double + >::type calculation_type; + + typedef geometry::collected_vector + < + calculation_type, + Geometry1, + typename IntersectionStrategy::side_strategy_type + > type; +}; + +template <typename TrivialCheck> +struct equals_by_collection +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + if (! TrivialCheck::apply(geometry1, geometry2, strategy)) + { + return false; + } + + typedef typename collected_vector + < + Geometry1, Geometry2, Strategy + >::type collected_vector_type; + + std::vector<collected_vector_type> c1, c2; + + geometry::collect_vectors(c1, geometry1); + geometry::collect_vectors(c2, geometry2); + + if (boost::size(c1) != boost::size(c2)) + { + return false; + } + + std::sort(c1.begin(), c1.end()); + std::sort(c2.begin(), c2.end()); + + // Just check if these vectors are equal. + return std::equal(c1.begin(), c1.end(), c2.begin()); + } +}; + +template<typename Geometry1, typename Geometry2> +struct equals_by_relate + : detail::relate::relate_impl + < + detail::de9im::static_mask_equals_type, + Geometry1, + Geometry2 + > +{}; + +// If collect_vectors which is a SideStrategy-dispatched optimization +// is implemented in a way consistent with the Intersection/Side Strategy +// then collect_vectors is used, otherwise relate is used. +// NOTE: the result could be conceptually different for invalid +// geometries in different coordinate systems because collect_vectors +// and relate treat invalid geometries differently. +template<typename TrivialCheck> +struct equals_by_collection_or_relate +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + typedef typename boost::is_base_of + < + nyi::not_implemented_tag, + typename collected_vector + < + Geometry1, Geometry2, Strategy + >::type + >::type enable_relate_type; + + return apply(geometry1, geometry2, strategy, enable_relate_type()); + } + +private: + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy, + boost::false_type /*enable_relate*/) + { + return equals_by_collection<TrivialCheck>::apply(geometry1, geometry2, strategy); + } + + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy, + boost::true_type /*enable_relate*/) + { + return equals_by_relate<Geometry1, Geometry2>::apply(geometry1, geometry2, strategy); + } +}; + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename P1, typename P2, std::size_t DimensionCount, bool Reverse> +struct equals<P1, P2, point_tag, point_tag, DimensionCount, Reverse> + : detail::equals::point_point<0, DimensionCount> +{}; + +template <typename MultiPoint1, typename MultiPoint2, std::size_t DimensionCount, bool Reverse> +struct equals<MultiPoint1, MultiPoint2, multi_point_tag, multi_point_tag, DimensionCount, Reverse> + : detail::equals::equals_by_relate<MultiPoint1, MultiPoint2> +{}; + +template <typename MultiPoint, typename Point, std::size_t DimensionCount, bool Reverse> +struct equals<Point, MultiPoint, point_tag, multi_point_tag, DimensionCount, Reverse> + : detail::equals::equals_by_relate<Point, MultiPoint> +{}; + +template <typename Box1, typename Box2, std::size_t DimensionCount, bool Reverse> +struct equals<Box1, Box2, box_tag, box_tag, DimensionCount, Reverse> + : detail::equals::box_box<0, DimensionCount> +{}; + + +template <typename Ring1, typename Ring2, bool Reverse> +struct equals<Ring1, Ring2, ring_tag, ring_tag, 2, Reverse> + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + + +template <typename Polygon1, typename Polygon2, bool Reverse> +struct equals<Polygon1, Polygon2, polygon_tag, polygon_tag, 2, Reverse> + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + + +template <typename Polygon, typename Ring, bool Reverse> +struct equals<Polygon, Ring, polygon_tag, ring_tag, 2, Reverse> + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + + +template <typename Ring, typename Box, bool Reverse> +struct equals<Ring, Box, ring_tag, box_tag, 2, Reverse> + : detail::equals::equals_by_collection<detail::equals::area_check> +{}; + + +template <typename Polygon, typename Box, bool Reverse> +struct equals<Polygon, Box, polygon_tag, box_tag, 2, Reverse> + : detail::equals::equals_by_collection<detail::equals::area_check> +{}; + +template <typename Segment1, typename Segment2, std::size_t DimensionCount, bool Reverse> +struct equals<Segment1, Segment2, segment_tag, segment_tag, DimensionCount, Reverse> + : detail::equals::segment_segment +{}; + +template <typename LineString1, typename LineString2, bool Reverse> +struct equals<LineString1, LineString2, linestring_tag, linestring_tag, 2, Reverse> + : detail::equals::equals_by_relate<LineString1, LineString2> +{}; + +template <typename LineString, typename MultiLineString, bool Reverse> +struct equals<LineString, MultiLineString, linestring_tag, multi_linestring_tag, 2, Reverse> + : detail::equals::equals_by_relate<LineString, MultiLineString> +{}; + +template <typename MultiLineString1, typename MultiLineString2, bool Reverse> +struct equals<MultiLineString1, MultiLineString2, multi_linestring_tag, multi_linestring_tag, 2, Reverse> + : detail::equals::equals_by_relate<MultiLineString1, MultiLineString2> +{}; + + +template <typename MultiPolygon1, typename MultiPolygon2, bool Reverse> +struct equals + < + MultiPolygon1, MultiPolygon2, + multi_polygon_tag, multi_polygon_tag, + 2, + Reverse + > + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + + +template <typename Polygon, typename MultiPolygon, bool Reverse> +struct equals + < + Polygon, MultiPolygon, + polygon_tag, multi_polygon_tag, + 2, + Reverse + > + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + +template <typename MultiPolygon, typename Ring, bool Reverse> +struct equals + < + MultiPolygon, Ring, + multi_polygon_tag, ring_tag, + 2, + Reverse + > + : detail::equals::equals_by_collection_or_relate<detail::equals::area_check> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_IMPLEMENTATION_HPP + diff --git a/boost/geometry/algorithms/detail/equals/interface.hpp b/boost/geometry/algorithms/detail/equals/interface.hpp new file mode 100644 index 0000000000..eacf95e9fe --- /dev/null +++ b/boost/geometry/algorithms/detail/equals/interface.hpp @@ -0,0 +1,317 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. +// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014, 2015, 2016, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_EQUALS_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_INTERFACE_HPP + + +#include <cstddef> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/strategies/default_strategy.hpp> +#include <boost/geometry/strategies/relate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type, + std::size_t DimensionCount = dimension<Geometry1>::type::value, + bool Reverse = reverse_dispatch<Geometry1, Geometry2>::type::value +> +struct equals: not_implemented<Tag1, Tag2> +{}; + + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, + typename Tag1, typename Tag2, + std::size_t DimensionCount +> +struct equals<Geometry1, Geometry2, Tag1, Tag2, DimensionCount, true> + : equals<Geometry2, Geometry1, Tag2, Tag1, DimensionCount, false> +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2, Strategy const& strategy) + { + return equals + < + Geometry2, Geometry1, + Tag2, Tag1, + DimensionCount, + false + >::apply(g2, g1, strategy); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy +{ + +struct equals +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return dispatch::equals + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy); + } + + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename strategy::relate::services::default_strategy + < + Geometry1, + Geometry2 + >::type strategy_type; + + return dispatch::equals + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant { + +template <typename Geometry1, typename Geometry2> +struct equals +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + concepts::check_concepts_and_equal_dimensions + < + Geometry1 const, + Geometry2 const + >(); + + return resolve_strategy::equals + ::apply(geometry1, geometry2, strategy); + } +}; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T), typename Geometry2> +struct equals<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Geometry2> +{ + template <typename Strategy> + struct visitor: static_visitor<bool> + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, Strategy const& strategy) + : m_geometry2(geometry2) + , m_strategy(strategy) + {} + + template <typename Geometry1> + inline bool operator()(Geometry1 const& geometry1) const + { + return equals<Geometry1, Geometry2> + ::apply(geometry1, m_geometry2, m_strategy); + } + + }; + + template <typename Strategy> + static inline bool apply( + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy + ) + { + return boost::apply_visitor(visitor<Strategy>(geometry2, strategy), geometry1); + } +}; + +template <typename Geometry1, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct equals<Geometry1, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > +{ + template <typename Strategy> + struct visitor: static_visitor<bool> + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, Strategy const& strategy) + : m_geometry1(geometry1) + , m_strategy(strategy) + {} + + template <typename Geometry2> + inline bool operator()(Geometry2 const& geometry2) const + { + return equals<Geometry1, Geometry2> + ::apply(m_geometry1, geometry2, m_strategy); + } + + }; + + template <typename Strategy> + static inline bool apply( + Geometry1 const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry2, + Strategy const& strategy + ) + { + return boost::apply_visitor(visitor<Strategy>(geometry1, strategy), geometry2); + } +}; + +template < + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) +> +struct equals< + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> +> +{ + template <typename Strategy> + struct visitor: static_visitor<bool> + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) + : m_strategy(strategy) + {} + + template <typename Geometry1, typename Geometry2> + inline bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return equals<Geometry1, Geometry2> + ::apply(geometry1, geometry2, m_strategy); + } + + }; + + template <typename Strategy> + static inline bool apply( + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, + Strategy const& strategy + ) + { + return boost::apply_visitor(visitor<Strategy>(strategy), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_check{are spatially equal} +\details \details_check12{equals, is spatially equal}. Spatially equal means + that the same point set is included. A box can therefore be spatially equal + to a ring or a polygon, or a linestring can be spatially equal to a + multi-linestring or a segment. This only works theoretically, not all + combinations are implemented yet. +\ingroup equals +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Equals} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{equals} +\return \return_check2{are spatially equal} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/equals.qbk]} + */ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline bool equals(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + return resolve_variant::equals + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy); +} + + +/*! +\brief \brief_check{are spatially equal} +\details \details_check12{equals, is spatially equal}. Spatially equal means + that the same point set is included. A box can therefore be spatially equal + to a ring or a polygon, or a linestring can be spatially equal to a + multi-linestring or a segment. This only works theoretically, not all + combinations are implemented yet. +\ingroup equals +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{are spatially equal} + +\qbk{[include reference/algorithms/equals.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline bool equals(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + return resolve_variant::equals<Geometry1, Geometry2> + ::apply(geometry1, geometry2, default_strategy()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_INTERFACE_HPP + diff --git a/boost/geometry/algorithms/detail/extreme_points.hpp b/boost/geometry/algorithms/detail/extreme_points.hpp index 65795cd05b..61e984ee3c 100644 --- a/boost/geometry/algorithms/detail/extreme_points.hpp +++ b/boost/geometry/algorithms/detail/extreme_points.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -117,12 +122,6 @@ struct extreme_points_on_ring typedef typename boost::range_iterator<Ring const>::type range_iterator; typedef typename geometry::point_type<Ring>::type point_type; - typedef typename geometry::strategy::side::services::default_strategy - < - typename geometry::cs_tag<point_type>::type - >::type side_strategy; - - template <typename CirclingIterator, typename Points> static inline bool extend(CirclingIterator& it, std::size_t n, @@ -214,10 +213,11 @@ struct extreme_points_on_ring return true; } - template <typename Extremes, typename Intruders, typename CirclingIterator> + template <typename Extremes, typename Intruders, typename CirclingIterator, typename SideStrategy> static inline void get_intruders(Ring const& ring, CirclingIterator left, CirclingIterator right, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { if (boost::size(extremes) < 3) { @@ -238,8 +238,8 @@ struct extreme_points_on_ring if (coordinate > min_value && other_coordinate > other_min && other_coordinate < other_max) { int const factor = geometry::point_order<Ring>::value == geometry::clockwise ? 1 : -1; - int const first_side = side_strategy::apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; - int const last_side = side_strategy::apply(*right, *(extremes.rbegin() + 1), extremes.back()) * factor; + int const first_side = strategy.apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; + int const last_side = strategy.apply(*right, *(extremes.rbegin() + 1), extremes.back()) * factor; // If not lying left from any of the extemes side if (first_side != 1 && last_side != 1) @@ -263,10 +263,11 @@ struct extreme_points_on_ring } } - template <typename Extremes, typename Intruders> + template <typename Extremes, typename Intruders, typename SideStrategy> static inline void get_intruders(Ring const& ring, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n >= 3) @@ -275,12 +276,12 @@ struct extreme_points_on_ring geometry::ever_circling_range_iterator<Ring const> right(ring); ++right; - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); } } - template <typename Iterator> - static inline bool right_turn(Ring const& ring, Iterator it) + template <typename Iterator, typename SideStrategy> + static inline bool right_turn(Ring const& ring, Iterator it, SideStrategy const& strategy) { typename std::iterator_traits<Iterator>::difference_type const index = std::distance(boost::begin(ring), it); @@ -295,8 +296,8 @@ struct extreme_points_on_ring } int const factor = geometry::point_order<Ring>::value == geometry::clockwise ? 1 : -1; - int const first_side = side_strategy::apply(*(right - 1), *right, *left) * factor; - int const last_side = side_strategy::apply(*left, *(left + 1), *right) * factor; + int const first_side = strategy.apply(*(right - 1), *right, *left) * factor; + int const last_side = strategy.apply(*left, *(left + 1), *right) * factor; //std::cout << "Candidate at " << geometry::wkt(*it) << " first=" << first_side << " last=" << last_side << std::endl; @@ -306,8 +307,11 @@ struct extreme_points_on_ring // Gets the extreme segments (top point plus neighbouring points), plus intruders, if any, on the same ring - template <typename Extremes, typename Intruders> - static inline bool apply(Ring const& ring, Extremes& extremes, Intruders& intruders) + template <typename Extremes, typename Intruders, typename SideStrategy> + static inline bool apply(Ring const& ring, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n < 3) @@ -321,7 +325,7 @@ struct extreme_points_on_ring compare<Dimension> smaller; for (range_iterator it = max_it + 1; it != boost::end(ring); ++it) { - if (smaller(*max_it, *it) && right_turn(ring, it)) + if (smaller(*max_it, *it) && right_turn(ring, it, strategy)) { max_it = it; } @@ -365,7 +369,7 @@ struct extreme_points_on_ring std::copy(points.begin(), points.end(), std::back_inserter(extremes)); - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); return true; } @@ -403,8 +407,9 @@ struct extreme_points<Ring, Dimension, ring_tag> template<typename Polygon, std::size_t Dimension> struct extreme_points<Polygon, Dimension, polygon_tag> { - template <typename Extremes, typename Intruders> - static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders) + template <typename Extremes, typename Intruders, typename SideStrategy> + static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders, + SideStrategy const& strategy) { typedef typename geometry::ring_type<Polygon>::type ring_type; typedef detail::extreme_points::extreme_points_on_ring @@ -412,7 +417,8 @@ struct extreme_points<Polygon, Dimension, polygon_tag> ring_type, Dimension > ring_implementation; - if (! ring_implementation::apply(geometry::exterior_ring(polygon), extremes, intruders)) + if (! ring_implementation::apply(geometry::exterior_ring(polygon), + extremes, intruders, strategy)) { return false; } @@ -423,7 +429,7 @@ struct extreme_points<Polygon, Dimension, polygon_tag> for (typename detail::interior_iterator<Polygon const>::type it = boost::begin(rings); it != boost::end(rings); ++it) { - ring_implementation::get_intruders(*it, extremes, intruders); + ring_implementation::get_intruders(*it, extremes, intruders, strategy); } return true; @@ -433,8 +439,9 @@ struct extreme_points<Polygon, Dimension, polygon_tag> template<typename Box> struct extreme_points<Box, 1, box_tag> { - template <typename Extremes, typename Intruders> - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template <typename Extremes, typename Intruders, typename SideStrategy> + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented<false>(box, extremes); @@ -446,8 +453,9 @@ struct extreme_points<Box, 1, box_tag> template<typename Box> struct extreme_points<Box, 0, box_tag> { - template <typename Extremes, typename Intruders> - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template <typename Extremes, typename Intruders, typename SideStrategy> + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented<false>(box, extremes); @@ -460,8 +468,9 @@ struct extreme_points<Box, 0, box_tag> template<typename MultiPolygon, std::size_t Dimension> struct extreme_points<MultiPolygon, Dimension, multi_polygon_tag> { - template <typename Extremes, typename Intruders> - static inline bool apply(MultiPolygon const& multi, Extremes& extremes, Intruders& intruders) + template <typename Extremes, typename Intruders, typename SideStrategy> + static inline bool apply(MultiPolygon const& multi, Extremes& extremes, + Intruders& intruders, SideStrategy const& strategy) { // Get one for the very first polygon, that is (for the moment) enough. // It is not guaranteed the "extreme" then, but for the current purpose @@ -473,7 +482,7 @@ struct extreme_points<MultiPolygon, Dimension, multi_polygon_tag> typename boost::range_value<MultiPolygon const>::type, Dimension, polygon_tag - >::apply(*boost::begin(multi), extremes, intruders); + >::apply(*boost::begin(multi), extremes, intruders, strategy); } return false; @@ -489,8 +498,18 @@ struct extreme_points<MultiPolygon, Dimension, multi_polygon_tag> for Edge=0 in dimension 0, the right side) \note We could specify a strategy (less/greater) to get bottom/left side too. However, until now we don't need that. */ -template <std::size_t Edge, typename Geometry, typename Extremes, typename Intruders> -inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intruders& intruders) +template +< + std::size_t Edge, + typename Geometry, + typename Extremes, + typename Intruders, + typename SideStrategy +> +inline bool extreme_points(Geometry const& geometry, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { concepts::check<Geometry const>(); @@ -509,7 +528,11 @@ inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intrude const >(); - return dispatch::extreme_points<Geometry, Edge>::apply(geometry, extremes, intruders); + return dispatch::extreme_points + < + Geometry, + Edge + >::apply(geometry, extremes, intruders, strategy); } diff --git a/boost/geometry/algorithms/detail/get_left_turns.hpp b/boost/geometry/algorithms/detail/get_left_turns.hpp index 95ab98c236..e9f6a50859 100644 --- a/boost/geometry/algorithms/detail/get_left_turns.hpp +++ b/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -60,17 +65,14 @@ inline int squared_length(Vector const& vector) } -template <typename Point> +template <typename Point, typename SideStrategy> struct angle_less { typedef Point vector_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Point>::type - >::type side_strategy_type; - angle_less(Point const& origin) + angle_less(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template <typename Angle> @@ -89,8 +91,7 @@ struct angle_less return quadrant_p < quadrant_q; } // Same quadrant, check if p is located left of q - int const side = side_strategy_type::apply(m_origin, q.point, - p.point); + int const side = m_strategy.apply(m_origin, q.point, p.point); if (side != 0) { return side == 1; @@ -114,19 +115,17 @@ struct angle_less private: Point m_origin; + SideStrategy m_strategy; }; -template <typename Point> +template <typename Point, typename SideStrategy> struct angle_equal_to { typedef Point vector_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Point>::type - >::type side_strategy_type; - - inline angle_equal_to(Point const& origin) + + inline angle_equal_to(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template <typename Angle> @@ -143,13 +142,13 @@ struct angle_equal_to return false; } // Same quadrant, check if p/q are collinear - int const side = side_strategy_type::apply(m_origin, q.point, - p.point); + int const side = m_strategy.apply(m_origin, q.point, p.point); return side == 0; } private: Point m_origin; + SideStrategy m_strategy; }; template <typename AngleCollection, typename Turns> @@ -193,13 +192,14 @@ inline void get_left_turns(AngleCollection const& sorted_angles, //! Returns the number of clusters -template <typename Point, typename AngleCollection> -inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin) +template <typename Point, typename AngleCollection, typename SideStrategy> +inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin, + SideStrategy const& strategy) { // Assign same cluster_index for all turns in same direction BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u); - angle_equal_to<Point> comparator(origin); + angle_equal_to<Point, SideStrategy> comparator(origin, strategy); typename boost::range_iterator<AngleCollection>::type it = sorted.begin(); std::size_t cluster_index = 0; diff --git a/boost/geometry/algorithms/detail/has_self_intersections.hpp b/boost/geometry/algorithms/detail/has_self_intersections.hpp index 9a388a4d80..c34bb217a6 100644 --- a/boost/geometry/algorithms/detail/has_self_intersections.hpp +++ b/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -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; diff --git a/boost/geometry/algorithms/detail/intersects/implementation.hpp b/boost/geometry/algorithms/detail/intersects/implementation.hpp new file mode 100644 index 0000000000..2379168e83 --- /dev/null +++ b/boost/geometry/algorithms/detail/intersects/implementation.hpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013-2017. +// Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_INTERSECTS_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTS_IMPLEMENTATION_HPP + + +#include <deque> + +#include <boost/geometry/algorithms/detail/intersects/interface.hpp> +#include <boost/geometry/algorithms/detail/disjoint/implementation.hpp> + +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> +#include <boost/geometry/policies/disjoint_interrupt_policy.hpp> +#include <boost/geometry/policies/robustness/no_rescale_policy.hpp> +#include <boost/geometry/policies/robustness/segment_ratio_type.hpp> + +#include <boost/geometry/strategies/relate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersects +{ + +template <typename Geometry> +struct self_intersects +{ + static bool apply(Geometry const& geometry) + { + concepts::check<Geometry const>(); + + typedef typename geometry::point_type<Geometry>::type point_type; + typedef typename strategy::relate::services::default_strategy + < + Geometry, Geometry + >::type strategy_type; + typedef detail::no_rescale_policy rescale_policy_type; + + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type<point_type, rescale_policy_type>::type + > turn_info; + + std::deque<turn_info> turns; + + typedef detail::overlay::get_turn_info + < + detail::overlay::assign_null_policy + > turn_policy; + + strategy_type strategy; + rescale_policy_type robust_policy; + + detail::disjoint::disjoint_interrupt_policy policy; + detail::self_get_turn_points::get_turns + < + false, turn_policy + >::apply(geometry, strategy, robust_policy, turns, policy, 0); + return policy.has_intersections; + } +}; + +}} // namespace detail::intersects +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTS_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/intersects/interface.hpp b/boost/geometry/algorithms/detail/intersects/interface.hpp new file mode 100644 index 0000000000..8940a426f3 --- /dev/null +++ b/boost/geometry/algorithms/detail/intersects/interface.hpp @@ -0,0 +1,115 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013-2017. +// Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_INTERSECTS_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTS_INTERFACE_HPP + + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/disjoint/interface.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersects +{ + +// Forward declaration +template <typename Geometry> +struct self_intersects; + +}} // namespace detail::intersects +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief \brief_check{has at least one intersection (crossing or self-tangency)} +\note This function can be called for one geometry (self-intersection) and + also for two geometries (intersection) +\ingroup intersects +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is self-intersecting} + +\qbk{distinguish,one geometry} +\qbk{[def __one_parameter__]} +\qbk{[include reference/algorithms/intersects.qbk]} +*/ +template <typename Geometry> +inline bool intersects(Geometry const& geometry) +{ + return detail::intersects::self_intersects<Geometry>::apply(geometry); +} + + +/*! +\brief \brief_check2{have at least one intersection} +\ingroup intersects +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Intersects} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{intersects} +\return \return_check2{intersect each other} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/intersects.qbk]} + */ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline bool intersects(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + + return ! geometry::disjoint(geometry1, geometry2, strategy); +} + + +/*! +\brief \brief_check2{have at least one intersection} +\ingroup intersects +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{intersect each other} + +\qbk{distinguish,two geometries} +\qbk{[include reference/algorithms/intersects.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline bool intersects(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + + return ! geometry::disjoint(geometry1, geometry2); +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTS_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/linear.hpp b/boost/geometry/algorithms/detail/is_simple/linear.hpp index 52b9d9d1c8..5acf56c5b1 100644 --- a/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -219,12 +219,12 @@ 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(), turns, - interrupt_policy); + interrupt_policy, 0); detail::is_valid::debug_print_turns(turns.begin(), turns.end()); debug_print_boundary_points(linear); @@ -236,7 +236,9 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg template <typename Linestring, bool CheckSelfIntersections = true> struct is_simple_linestring { - static inline bool apply(Linestring const& linestring) + template <typename Strategy> + static inline bool apply(Linestring const& linestring, + Strategy const& strategy) { simplicity_failure_policy policy; return ! boost::empty(linestring) @@ -247,7 +249,7 @@ struct is_simple_linestring && ! detail::is_valid::has_spikes < Linestring, closed - >::apply(linestring, policy); + >::apply(linestring, policy, strategy.get_side_strategy()); } }; @@ -258,7 +260,10 @@ struct is_simple_linestring<Linestring, true> static inline bool apply(Linestring const& linestring, Strategy const& strategy) { - return is_simple_linestring<Linestring, false>::apply(linestring) + return is_simple_linestring + < + Linestring, false + >::apply(linestring, strategy) && ! has_self_intersections(linestring, strategy); } }; @@ -267,23 +272,44 @@ struct is_simple_linestring<Linestring, true> template <typename MultiLinestring> struct is_simple_multilinestring { +private: + template <typename Strategy> + struct per_linestring + { + per_linestring(Strategy const& strategy) + : m_strategy(strategy) + {} + + template <typename Linestring> + inline bool apply(Linestring const& linestring) const + { + return detail::is_simple::is_simple_linestring + < + Linestring, + false // do not compute self-intersections + >::apply(linestring, m_strategy); + } + + Strategy const& m_strategy; + }; + +public: template <typename Strategy> static inline bool apply(MultiLinestring const& multilinestring, Strategy const& strategy) { + typedef per_linestring<Strategy> per_ls; + // check each of the linestrings for simplicity // but do not compute self-intersections yet; these will be // computed for the entire multilinestring if ( ! detail::check_iterator_range < - is_simple_linestring - < - typename boost::range_value<MultiLinestring>::type, - false // do not compute self-intersections - >, + per_ls, // do not compute self-intersections true // allow empty multilinestring >::apply(boost::begin(multilinestring), - boost::end(multilinestring)) + boost::end(multilinestring), + per_ls(strategy)) ) { return false; diff --git a/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp index aa90e52db6..96efec79cd 100644 --- a/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp +++ b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -91,8 +92,9 @@ struct has_spikes return std::find_if(second, last, not_equal(*first)); } - template <typename VisitPolicy> - static inline bool apply(Range const& range, VisitPolicy& visitor) + template <typename VisitPolicy, typename SideStrategy> + static inline bool apply(Range const& range, VisitPolicy& visitor, + SideStrategy const& strategy) { boost::ignore_unused(visitor); @@ -124,9 +126,8 @@ struct has_spikes while (next != boost::end(view)) { - if ( geometry::detail::point_is_spike_or_equal(*prev, - *next, - *cur) ) + if ( geometry::detail::point_is_spike_or_equal(*prev, *next, *cur, + strategy) ) { return ! visitor.template apply<failure_spikes>(is_linear, *cur); @@ -146,7 +147,7 @@ struct has_spikes boost::rend(view)); iterator next = find_different_from_first(cur, boost::end(view)); - if (detail::point_is_spike_or_equal(*prev, *next, *cur)) + if (detail::point_is_spike_or_equal(*prev, *next, *cur, strategy)) { return ! visitor.template apply<failure_spikes>(is_linear, *cur); diff --git a/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index b91dc6a697..b36e9f38b7 100644 --- a/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -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, diff --git a/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp index 0d80d6f6c0..fccc0ffdb7 100644 --- a/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp +++ b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp @@ -138,10 +138,17 @@ 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) - ; + // Turn is acceptable only in case of a touch(interior) and both lines + // (polygons) do not cross + return (turn.method == method_touch + || turn.method == method_touch_interior) + && turn.touch_only; } }; diff --git a/boost/geometry/algorithms/detail/is_valid/linear.hpp b/boost/geometry/algorithms/detail/is_valid/linear.hpp index 6bc6b86cf8..39cb36ef5b 100644 --- a/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -43,9 +43,10 @@ namespace detail { namespace is_valid template <typename Linestring> struct is_valid_linestring { - template <typename VisitPolicy> + template <typename VisitPolicy, typename Strategy> static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { if (has_invalid_coordinate<Linestring>::apply(linestring, visitor)) { @@ -75,15 +76,12 @@ struct is_valid_linestring { return visitor.template apply<no_failure>(); } - return ! has_spikes<Linestring, closed>::apply(linestring, visitor); - } - template <typename VisitPolicy, typename Strategy> - static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor, - Strategy const&) - { - return apply(linestring, visitor); + return ! has_spikes + < + Linestring, closed + >::apply(linestring, visitor, + strategy.get_side_strategy()); } }; @@ -132,10 +130,13 @@ class is_valid > { private: - template <typename VisitPolicy> + template <typename VisitPolicy, typename Strategy> struct per_linestring { - per_linestring(VisitPolicy& policy) : m_policy(policy) {} + per_linestring(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template <typename Linestring> inline bool apply(Linestring const& linestring) const @@ -143,17 +144,18 @@ private: return detail::is_valid::is_valid_linestring < Linestring - >::apply(linestring, m_policy); + >::apply(linestring, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; public: template <typename VisitPolicy, typename Strategy> static inline bool apply(MultiLinestring const& multilinestring, VisitPolicy& visitor, - Strategy const&) + Strategy const& strategy) { if (BOOST_GEOMETRY_CONDITION( AllowEmptyMultiGeometries && boost::empty(multilinestring))) @@ -161,13 +163,15 @@ public: return visitor.template apply<no_failure>(); } + typedef per_linestring<VisitPolicy, Strategy> per_ls; + return detail::check_iterator_range < - per_linestring<VisitPolicy>, + per_ls, false // do not check for empty multilinestring (done above) >::apply(boost::begin(multilinestring), boost::end(multilinestring), - per_linestring<VisitPolicy>(visitor)); + per_ls(visitor, strategy)); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index 84dacc57f1..ed24b13810 100644 --- a/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -76,45 +76,66 @@ private: < typename PolygonIterator, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool are_polygon_interiors_disjoint(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, TurnIterator turns_beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); - // collect all polygons that have turns + // collect all polygons that have crossing turns std::set<signed_size_type> multi_indices; for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) { - multi_indices.insert(tit->operations[0].seg_id.multi_index); - multi_indices.insert(tit->operations[1].seg_id.multi_index); + if (! tit->touch_only) + { + multi_indices.insert(tit->operations[0].seg_id.multi_index); + multi_indices.insert(tit->operations[1].seg_id.multi_index); + } } + typedef geometry::model::box<typename point_type<MultiPolygon>::type> box_type; + typedef typename base::template partition_item<PolygonIterator, box_type> item_type; + // put polygon iterators without turns in a vector - std::vector<PolygonIterator> polygon_iterators; + std::vector<item_type> polygon_iterators; signed_size_type multi_index = 0; for (PolygonIterator it = polygons_first; it != polygons_beyond; ++it, ++multi_index) { if (multi_indices.find(multi_index) == multi_indices.end()) { - polygon_iterators.push_back(it); + polygon_iterators.push_back(item_type(it)); } } - typename base::item_visitor_type item_visitor; + // prepare strategies + typedef typename std::iterator_traits<PolygonIterator>::value_type polygon_type; + typedef typename Strategy::template point_in_geometry_strategy + < + polygon_type, polygon_type + >::type within_strategy_type; + within_strategy_type const within_strategy + = strategy.template get_point_in_geometry_strategy<polygon_type, polygon_type>(); + typedef typename Strategy::envelope_strategy_type envelope_strategy_type; + envelope_strategy_type const envelope_strategy + = strategy.get_envelope_strategy(); + + // call partition to check if polygons are disjoint from each other + typename base::template item_visitor_type<within_strategy_type> item_visitor(within_strategy); geometry::partition < geometry::model::box<typename point_type<MultiPolygon>::type> >::apply(polygon_iterators, item_visitor, - typename base::expand_box(), - typename base::overlaps_box()); + typename base::template expand_box<envelope_strategy_type>(envelope_strategy), + typename base::template overlaps_box<envelope_strategy_type>(envelope_strategy)); if (item_visitor.items_overlap) { @@ -155,13 +176,15 @@ private: < typename PolygonIterator, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool apply(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, TurnIterator turns_beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { signed_size_type multi_index = 0; for (PolygonIterator it = polygons_first; it != polygons_beyond; @@ -185,7 +208,8 @@ private: if (! Predicate::apply(*it, filtered_turns_first, filtered_turns_beyond, - visitor)) + visitor, + strategy)) { return false; } @@ -200,19 +224,21 @@ private: < typename PolygonIterator, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool have_holes_inside(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, TurnIterator turns_beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return has_property_per_polygon < typename base::has_holes_inside >::apply(polygons_first, polygons_beyond, - turns_first, turns_beyond, visitor); + turns_first, turns_beyond, visitor, strategy); } @@ -221,19 +247,21 @@ private: < typename PolygonIterator, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool have_connected_interior(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, TurnIterator turns_beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return has_property_per_polygon < typename base::has_connected_interior >::apply(polygons_first, polygons_beyond, - turns_first, turns_beyond, visitor); + turns_first, turns_beyond, visitor, strategy); } @@ -307,7 +335,8 @@ public: boost::end(multipolygon), turns.begin(), turns.end(), - visitor)) + visitor, + strategy)) { return false; } @@ -320,7 +349,8 @@ public: boost::end(multipolygon), turns.begin(), turns.end(), - visitor)) + visitor, + strategy)) { return false; } @@ -332,7 +362,8 @@ public: boost::end(multipolygon), turns.begin(), turns.end(), - visitor); + visitor, + strategy); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/boost/geometry/algorithms/detail/is_valid/polygon.hpp index f7e22fb8d2..5c6229b793 100644 --- a/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -1,5 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. + // Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -43,6 +45,7 @@ #include <boost/geometry/algorithms/expand.hpp> #include <boost/geometry/algorithms/num_interior_rings.hpp> #include <boost/geometry/algorithms/validity_failure_type.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/within.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> @@ -142,43 +145,103 @@ protected: }; + // Iterator value_type is a Ring or Polygon + template <typename Iterator, typename Box> + struct partition_item + { + explicit partition_item(Iterator it) + : m_it(it) + , m_is_initialized(false) + {} + + Iterator get() const + { + return m_it; + } + + template <typename EnvelopeStrategy> + Box const& get_envelope(EnvelopeStrategy const& strategy) const + { + if (! m_is_initialized) + { + m_box = geometry::return_envelope<Box>(*m_it, strategy); + m_is_initialized = true; + } + return m_box; + } + + private: + Iterator m_it; + mutable Box m_box; + mutable bool m_is_initialized; + }; + // structs for partition -- start + template <typename EnvelopeStrategy> struct expand_box { + explicit expand_box(EnvelopeStrategy const& strategy) : m_strategy(strategy) {} + template <typename Box, typename Iterator> - static inline void apply(Box& total, Iterator const& it) + inline void apply(Box& total, partition_item<Iterator, Box> const& item) const { - geometry::expand(total, geometry::return_envelope<Box>(*it)); + geometry::expand(total, item.get_envelope(m_strategy)); } + EnvelopeStrategy const& m_strategy; }; + template <typename EnvelopeStrategy> struct overlaps_box { + explicit overlaps_box(EnvelopeStrategy const& strategy) : m_strategy(strategy) {} + template <typename Box, typename Iterator> - static inline bool apply(Box const& box, Iterator const& it) + inline bool apply(Box const& box, partition_item<Iterator, Box> const& item) const { - return ! geometry::disjoint(*it, box); + return ! geometry::disjoint(item.get_envelope(m_strategy), box); } + + EnvelopeStrategy const& m_strategy; }; + template <typename WithinStrategy> struct item_visitor_type { bool items_overlap; + WithinStrategy const& m_strategy; + + explicit item_visitor_type(WithinStrategy const& strategy) + : items_overlap(false) + , m_strategy(strategy) + {} - item_visitor_type() : items_overlap(false) {} + template <typename Item> + inline bool is_within(Item const& first, Item const& second) + { + typename point_type<Polygon>::type point; + typedef detail::point_on_border::point_on_range<true> pob; + + // TODO: this should check for a point on the interior, instead + // of on border. Or it should check using the overlap function. + + return pob::apply(point, points_begin(first), points_end(first)) + && geometry::within(point, second, m_strategy); + } - template <typename Item1, typename Item2> - inline void apply(Item1 const& item1, Item2 const& item2) + template <typename Iterator, typename Box> + inline bool apply(partition_item<Iterator, Box> const& item1, + partition_item<Iterator, Box> const& item2) { if (! items_overlap - && (geometry::within(*points_begin(*item1), *item2) - || geometry::within(*points_begin(*item2), *item1)) - ) + && (is_within(*item1.get(), *item2.get()) + || is_within(*item2.get(), *item1.get()))) { items_overlap = true; + return false; // interrupt } + return true; } }; // structs for partition -- end @@ -189,14 +252,16 @@ protected: typename RingIterator, typename ExteriorRing, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool are_holes_inside(RingIterator rings_first, RingIterator rings_beyond, ExteriorRing const& exterior_ring, TurnIterator turns_first, TurnIterator turns_beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); @@ -217,6 +282,14 @@ protected: } } + // prepare strategy + typedef typename std::iterator_traits<RingIterator>::value_type inter_ring_type; + typename Strategy::template point_in_geometry_strategy + < + inter_ring_type, ExteriorRing + >::type const in_exterior_strategy + = strategy.template get_point_in_geometry_strategy<inter_ring_type, ExteriorRing>(); + signed_size_type ring_index = 0; for (RingIterator it = rings_first; it != rings_beyond; ++it, ++ring_index) @@ -224,7 +297,7 @@ protected: // do not examine interior rings that have turns with the // exterior ring if (ring_indices.find(ring_index) == ring_indices.end() - && ! geometry::covered_by(range::front(*it), exterior_ring)) + && ! geometry::covered_by(range::front(*it), exterior_ring, in_exterior_strategy)) { return visitor.template apply<failure_interior_rings_outside>(); } @@ -237,26 +310,42 @@ protected: ring_indices.insert(tit->operations[1].seg_id.ring_index); } + typedef geometry::model::box<typename point_type<Polygon>::type> box_type; + typedef partition_item<RingIterator, box_type> item_type; + // put iterators for interior rings without turns in a vector - std::vector<RingIterator> ring_iterators; + std::vector<item_type> ring_iterators; ring_index = 0; for (RingIterator it = rings_first; it != rings_beyond; ++it, ++ring_index) { if (ring_indices.find(ring_index) == ring_indices.end()) { - ring_iterators.push_back(it); + ring_iterators.push_back(item_type(it)); } } - // call partition to check is interior rings are disjoint from + // prepare strategies + typedef typename Strategy::template point_in_geometry_strategy + < + inter_ring_type, inter_ring_type + >::type in_interior_strategy_type; + in_interior_strategy_type const in_interior_strategy + = strategy.template get_point_in_geometry_strategy<inter_ring_type, inter_ring_type>(); + typedef typename Strategy::envelope_strategy_type envelope_strategy_type; + envelope_strategy_type const envelope_strategy + = strategy.get_envelope_strategy(); + + // call partition to check if interior rings are disjoint from // each other - item_visitor_type item_visitor; + item_visitor_type<in_interior_strategy_type> item_visitor(in_interior_strategy); geometry::partition < - geometry::model::box<typename point_type<Polygon>::type> - >::apply(ring_iterators, item_visitor, expand_box(), overlaps_box()); + box_type + >::apply(ring_iterators, item_visitor, + expand_box<envelope_strategy_type>(envelope_strategy), + overlaps_box<envelope_strategy_type>(envelope_strategy)); if (item_visitor.items_overlap) { @@ -273,35 +362,40 @@ protected: typename InteriorRings, typename ExteriorRing, typename TurnIterator, - typename VisitPolicy + typename VisitPolicy, + typename Strategy > static inline bool are_holes_inside(InteriorRings const& interior_rings, ExteriorRing const& exterior_ring, TurnIterator first, TurnIterator beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return are_holes_inside(boost::begin(interior_rings), boost::end(interior_rings), exterior_ring, first, beyond, - visitor); + visitor, + strategy); } struct has_holes_inside { - template <typename TurnIterator, typename VisitPolicy> + template <typename TurnIterator, typename VisitPolicy, typename Strategy> static inline bool apply(Polygon const& polygon, TurnIterator first, TurnIterator beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return are_holes_inside(geometry::interior_rings(polygon), geometry::exterior_ring(polygon), first, beyond, - visitor); + visitor, + strategy); } }; @@ -310,11 +404,12 @@ protected: struct has_connected_interior { - template <typename TurnIterator, typename VisitPolicy> + template <typename TurnIterator, typename VisitPolicy, typename Strategy> static inline bool apply(Polygon const& polygon, TurnIterator first, TurnIterator beyond, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& ) { boost::ignore_unused(visitor); @@ -388,7 +483,8 @@ public: if (! has_holes_inside::apply(polygon, turns.begin(), turns.end(), - visitor)) + visitor, + strategy)) { return false; } @@ -399,7 +495,8 @@ public: return has_connected_interior::apply(polygon, turns.begin(), turns.end(), - visitor); + visitor, + strategy); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/ring.hpp b/boost/geometry/algorithms/detail/is_valid/ring.hpp index 9ab68fdc48..0b95950430 100644 --- a/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -115,7 +115,10 @@ struct is_properly_oriented geometry::closure<Ring>::value > ring_area_type; - typedef typename default_area_result<Ring>::type area_result_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typename ring_area_predicate < @@ -195,7 +198,7 @@ struct is_valid_ring return is_topologically_closed<Ring, closure>::apply(ring, visitor) && ! has_duplicates<Ring, closure>::apply(ring, visitor) - && ! has_spikes<Ring, closure>::apply(ring, visitor) + && ! has_spikes<Ring, closure>::apply(ring, visitor, strategy.get_side_strategy()) && (! CheckSelfIntersections || has_valid_self_turns<Ring>::apply(ring, visitor, strategy)) && is_properly_oriented<Ring, IsInteriorRing>::apply(ring, visitor, strategy); diff --git a/boost/geometry/algorithms/detail/multi_modify.hpp b/boost/geometry/algorithms/detail/multi_modify.hpp index f0b9ddd3e6..23187f9323 100644 --- a/boost/geometry/algorithms/detail/multi_modify.hpp +++ b/boost/geometry/algorithms/detail/multi_modify.hpp @@ -4,6 +4,10 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -40,6 +44,18 @@ struct multi_modify Policy::apply(*it); } } + + template <typename Strategy> + static inline void apply(MultiGeometry& multi, Strategy const& strategy) + { + typedef typename boost::range_iterator<MultiGeometry>::type iterator_type; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, strategy); + } + } }; diff --git a/boost/geometry/algorithms/detail/occupation_info.hpp b/boost/geometry/algorithms/detail/occupation_info.hpp index 4048d59d75..fc74f0cc7f 100644 --- a/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/boost/geometry/algorithms/detail/occupation_info.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -99,12 +104,18 @@ public : } } - template <typename RobustPoint, typename Turns> - inline void get_left_turns(RobustPoint const& origin, Turns& turns) + template <typename RobustPoint, typename Turns, typename SideStrategy> + inline void get_left_turns(RobustPoint const& origin, Turns& turns, + SideStrategy const& strategy) { + typedef detail::left_turns::angle_less + < + typename AngleInfo::point_type, + SideStrategy + > angle_less; + // Sort on angle - std::sort(m_angles.begin(), m_angles.end(), - detail::left_turns::angle_less<typename AngleInfo::point_type>(origin)); + std::sort(m_angles.begin(), m_angles.end(), angle_less(origin, strategy)); // Group same-angled elements std::size_t cluster_size = detail::left_turns::assign_cluster_indices(m_angles, origin); diff --git a/boost/geometry/algorithms/detail/overlaps/implementation.hpp b/boost/geometry/algorithms/detail/overlaps/implementation.hpp new file mode 100644 index 0000000000..49f44bef81 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlaps/implementation.hpp @@ -0,0 +1,156 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014, 2015, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_OVERLAPS_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAPS_IMPLEMENTATION_HPP + + +#include <cstddef> + +#include <boost/geometry/core/access.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/relate.hpp> +#include <boost/geometry/algorithms/detail/overlaps/interface.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlaps +{ + +template +< + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_box_loop +{ + template <typename Box1, typename Box2> + static inline void apply(Box1 const& b1, Box2 const& b2, + bool& overlaps, bool& one_in_two, bool& two_in_one) + { + assert_dimension_equal<Box1, Box2>(); + + typedef typename coordinate_type<Box1>::type coordinate_type1; + typedef typename coordinate_type<Box2>::type coordinate_type2; + + coordinate_type1 const& min1 = get<min_corner, Dimension>(b1); + coordinate_type1 const& max1 = get<max_corner, Dimension>(b1); + coordinate_type2 const& min2 = get<min_corner, Dimension>(b2); + coordinate_type2 const& max2 = get<max_corner, Dimension>(b2); + + // We might use the (not yet accepted) Boost.Interval + // submission in the future + + // If: + // B1: |-------| + // B2: |------| + // in any dimension -> no overlap + if (max1 <= min2 || min1 >= max2) + { + overlaps = false; + return; + } + + // If: + // B1: |--------------------| + // B2: |-------------| + // in all dimensions -> within, then no overlap + // B1: |--------------------| + // B2: |-------------| + // this is "within-touch" -> then no overlap. So use < and > + if (min1 < min2 || max1 > max2) + { + one_in_two = false; + } + + // Same other way round + if (min2 < min1 || max2 > max1) + { + two_in_one = false; + } + + box_box_loop + < + Dimension + 1, + DimensionCount + >::apply(b1, b2, overlaps, one_in_two, two_in_one); + } +}; + +template +< + std::size_t DimensionCount +> +struct box_box_loop<DimensionCount, DimensionCount> +{ + template <typename Box1, typename Box2> + static inline void apply(Box1 const& , Box2 const&, bool&, bool&, bool&) + { + } +}; + +struct box_box +{ + template <typename Box1, typename Box2, typename Strategy> + static inline bool apply(Box1 const& b1, Box2 const& b2, Strategy const& /*strategy*/) + { + bool overlaps = true; + bool within1 = true; + bool within2 = true; + box_box_loop + < + 0, + dimension<Box1>::type::value + >::apply(b1, b2, overlaps, within1, within2); + + /* + \see http://docs.codehaus.org/display/GEOTDOC/02+Geometry+Relationships#02GeometryRelationships-Overlaps + where is stated that "inside" is not an "overlap", + this is true and is implemented as such. + */ + return overlaps && ! within1 && ! within2; + } +}; + +}} // namespace detail::overlaps +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename Box1, typename Box2> +struct overlaps<Box1, Box2, box_tag, box_tag> + : detail::overlaps::box_box +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAPS_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/overlaps/interface.hpp b/boost/geometry/algorithms/detail/overlaps/interface.hpp new file mode 100644 index 0000000000..f9f6a853fd --- /dev/null +++ b/boost/geometry/algorithms/detail/overlaps/interface.hpp @@ -0,0 +1,124 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014, 2015, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_OVERLAPS_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAPS_INTERFACE_HPP + + +#include <cstddef> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/relate/relate_impl.hpp> + +#include <boost/geometry/strategies/relate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type +> +struct overlaps + : detail::relate::relate_impl + < + detail::de9im::static_mask_overlaps_type, + Geometry1, + Geometry2 + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check2{overlap} +\ingroup overlaps +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Overlaps} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{overlaps} +\return \return_check2{overlap} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/overlaps.qbk]} +*/ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline bool overlaps(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + + return dispatch::overlaps + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + +/*! +\brief \brief_check2{overlap} +\ingroup overlaps +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{overlap} + +\qbk{[include reference/algorithms/overlaps.qbk]} +*/ +template <typename Geometry1, typename Geometry2> +inline bool overlaps(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + + typedef typename strategy::relate::services::default_strategy + < + Geometry1, + Geometry2 + >::type strategy_type; + + return dispatch::overlaps + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy_type()); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAPS_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index df62a1f2f6..106ecaad07 100644 --- a/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -24,8 +24,12 @@ struct ring_with_direction { ring_identifier ring_id; direction_type direction; - bool only_turn_on_ring; + 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 { @@ -36,7 +40,11 @@ struct ring_with_direction ring_with_direction() : direction(dir_unknown) - , only_turn_on_ring(false) + , turn_index(-1) + , operation_index(0) + , operation(operation_none) + , region_id(-1) + , isolated(false) {} }; @@ -50,28 +58,168 @@ struct rank_with_rings { } + inline bool all_equal(direction_type dir_type) const + { + for (std::set<ring_with_direction>::const_iterator it = rings.begin(); + it != rings.end(); ++it) + { + if (it->direction != dir_type) + { + return false; + } + } + return true; + } + inline bool all_to() const { + return all_equal(sort_by_side::dir_to); + } + + inline bool all_from() const + { + 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) { - if (it->direction == sort_by_side::dir_from) + 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 + { + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename turn_type::turn_operation_type turn_operation_type; + + for (std::set<ring_with_direction>::const_iterator it = rings.begin(); + it != rings.end(); ++it) + { + const ring_with_direction& rwd = *it; + turn_type const& turn = turns[rwd.turn_index]; + turn_operation_type const& op = turn.operations[rwd.operation_index]; + + // 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; + } + } + return true; + } + }; -template <typename Sbs> -inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& aggregation) +template <typename Sbs, typename Turns> +inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& aggregation, + 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; + aggregation.clear(); for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) { typename Sbs::rp const& ranked_point = sbs.m_ranked_points[i]; + turn_type const& turn = turns[ranked_point.turn_index]; + + turn_operation_type const& op = turn.operations[ranked_point.operation_index]; + + 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; + } + if (aggregation.empty() || aggregation.back().rank != ranked_point.rank) { rank_with_rings current; @@ -81,10 +229,14 @@ inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& a ring_with_direction rwd; segment_identifier const& sid = ranked_point.seg_id; + rwd.ring_id = ring_identifier(sid.source_index, sid.multi_index, sid.ring_index); rwd.direction = ranked_point.direction; - rwd.only_turn_on_ring = ranked_point.only_turn_on_ring; - + 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); } diff --git a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp index 03c06c28d1..fb73840798 100644 --- a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp +++ b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -63,8 +63,9 @@ inline bool points_equal_or_close(Point1 const& point1, } -template <typename Range, typename Point, typename RobustPolicy> +template <typename Range, typename Point, typename SideStrategy, typename RobustPolicy> inline void append_no_dups_or_spikes(Range& range, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION @@ -92,6 +93,7 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, && point_is_spike_or_equal(point, *(boost::end(range) - 3), *(boost::end(range) - 2), + strategy, robust_policy)) { // Use the Concept/traits, so resize and append again @@ -100,8 +102,9 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, } } -template <typename Range, typename RobustPolicy> +template <typename Range, typename SideStrategy, typename RobustPolicy> inline void clean_closing_dups_and_spikes(Range& range, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { std::size_t const minsize @@ -135,7 +138,7 @@ inline void clean_closing_dups_and_spikes(Range& range, // Check if closing point is a spike (this is so if the second point is // considered as a spike w.r.t. the last segment) - if (point_is_spike_or_equal(*second, *ultimate, *first, robust_policy)) + if (point_is_spike_or_equal(*second, *ultimate, *first, strategy, robust_policy)) { range::erase(range, first); if (BOOST_GEOMETRY_CONDITION(closed)) diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 2408b4b68e..78160f5204 100644 --- a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -20,7 +20,8 @@ #include <boost/geometry/algorithms/expand.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> -#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp> +#include <boost/geometry/algorithms/covered_by.hpp> #include <boost/geometry/geometries/box.hpp> @@ -37,50 +38,88 @@ namespace detail { namespace overlay template < typename Item, + typename InnerGeometry, typename Geometry1, typename Geometry2, - typename RingCollection + typename RingCollection, + typename Strategy +> +static inline bool within_selected_input(Item const& item2, + InnerGeometry const& inner_geometry, + ring_identifier const& outer_id, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection, + Strategy const& strategy) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + // NOTE: range_in_geometry first checks the item2.point and then + // if this point is on boundary it checks points of inner_geometry + // ring until a point inside/outside other geometry ring is found + switch (outer_id.source_index) + { + // covered_by + case 0 : + return range_in_geometry(item2.point, inner_geometry, + get_ring<tag1>::apply(outer_id, geometry1), strategy) >= 0; + case 1 : + return range_in_geometry(item2.point, inner_geometry, + get_ring<tag2>::apply(outer_id, geometry2), strategy) >= 0; + case 2 : + return range_in_geometry(item2.point, inner_geometry, + get_ring<void>::apply(outer_id, collection), strategy) >= 0; + } + return false; +} + +template +< + typename Item, + typename Geometry1, typename Geometry2, + typename RingCollection, + typename Strategy > -static inline bool within_selected_input(Item const& item2, ring_identifier const& ring_id, +static inline bool within_selected_input(Item const& item2, + ring_identifier const& inner_id, ring_identifier const& outer_id, Geometry1 const& geometry1, Geometry2 const& geometry2, - RingCollection const& collection) + RingCollection const& collection, + Strategy const& strategy) { typedef typename geometry::tag<Geometry1>::type tag1; typedef typename geometry::tag<Geometry2>::type tag2; - switch (ring_id.source_index) + switch (inner_id.source_index) { case 0 : - return geometry::within(item2.point, - get_ring<tag1>::apply(ring_id, geometry1)); - break; + return within_selected_input(item2, + get_ring<tag1>::apply(inner_id, geometry1), + outer_id, geometry1, geometry2, collection, strategy); case 1 : - return geometry::within(item2.point, - get_ring<tag2>::apply(ring_id, geometry2)); - break; + return within_selected_input(item2, + get_ring<tag2>::apply(inner_id, geometry2), + outer_id, geometry1, geometry2, collection, strategy); case 2 : - return geometry::within(item2.point, - get_ring<void>::apply(ring_id, collection)); - break; + return within_selected_input(item2, + get_ring<void>::apply(inner_id, collection), + outer_id, geometry1, geometry2, collection, strategy); } return false; } -template <typename Point> +template <typename Point, typename AreaType> struct ring_info_helper { - typedef typename geometry::default_area_result<Point>::type area_type; - ring_identifier id; - area_type real_area; - area_type abs_area; + AreaType real_area; + AreaType abs_area; model::box<Point> envelope; inline ring_info_helper() : real_area(0), abs_area(0) {} - inline ring_info_helper(ring_identifier i, area_type a) + inline ring_info_helper(ring_identifier i, AreaType const& a) : id(i), real_area(a), abs_area(geometry::math::abs(a)) {} }; @@ -104,7 +143,14 @@ struct ring_info_helper_ovelaps_box } }; -template <typename Geometry1, typename Geometry2, typename Collection, typename RingMap> +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename RingMap, + typename Strategy +> struct assign_visitor { typedef typename RingMap::mapped_type ring_info_type; @@ -113,26 +159,27 @@ struct assign_visitor Geometry2 const& m_geometry2; Collection const& m_collection; RingMap& m_ring_map; + Strategy const& m_strategy; bool m_check_for_orientation; - inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c, - RingMap& map, bool check) + RingMap& map, Strategy const& strategy, bool check) : m_geometry1(g1) , m_geometry2(g2) , m_collection(c) , m_ring_map(map) + , m_strategy(strategy) , m_check_for_orientation(check) {} template <typename Item> - inline void apply(Item const& outer, Item const& inner, bool first = true) + inline bool apply(Item const& outer, Item const& inner, bool first = true) { if (first && outer.abs_area < inner.abs_area) { // Apply with reversed arguments apply(inner, outer, false); - return; + return true; } if (m_check_for_orientation @@ -141,8 +188,10 @@ struct assign_visitor { ring_info_type& inner_in_map = m_ring_map[inner.id]; - if (geometry::within(inner_in_map.point, outer.envelope) - && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) + if (geometry::covered_by(inner_in_map.point, outer.envelope) + && within_selected_input(inner_in_map, inner.id, outer.id, + m_geometry1, m_geometry2, m_collection, + m_strategy) ) { // Assign a parent if there was no earlier parent, or the newly @@ -155,6 +204,8 @@ struct assign_visitor } } } + + return true; } }; @@ -165,12 +216,14 @@ template < typename Geometry1, typename Geometry2, typename RingCollection, - typename RingMap + typename RingMap, + typename Strategy > inline void assign_parents(Geometry1 const& geometry1, Geometry2 const& geometry2, RingCollection const& collection, RingMap& ring_map, + Strategy const& strategy, bool check_for_orientation = false) { typedef typename geometry::tag<Geometry1>::type tag1; @@ -179,11 +232,15 @@ inline void assign_parents(Geometry1 const& geometry1, typedef typename RingMap::mapped_type ring_info_type; typedef typename ring_info_type::point_type point_type; typedef model::box<point_type> box_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typedef typename RingMap::iterator map_iterator_type; { - typedef ring_info_helper<point_type> helper; + typedef ring_info_helper<point_type, area_result_type> helper; typedef std::vector<helper> vector_type; typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type; @@ -204,17 +261,21 @@ inline void assign_parents(Geometry1 const& geometry1, { case 0 : geometry::envelope(get_ring<tag1>::apply(it->first, geometry1), - item.envelope); + item.envelope, strategy.get_envelope_strategy()); break; case 1 : geometry::envelope(get_ring<tag2>::apply(it->first, geometry2), - item.envelope); + item.envelope, strategy.get_envelope_strategy()); break; case 2 : geometry::envelope(get_ring<void>::apply(it->first, collection), - item.envelope); + item.envelope, strategy.get_envelope_strategy()); break; } + + // Expand envelope slightly + expand_by_epsilon(item.envelope); + if (item.real_area > 0) { count_positive++; @@ -257,8 +318,9 @@ inline void assign_parents(Geometry1 const& geometry1, assign_visitor < Geometry1, Geometry2, - RingCollection, RingMap - > visitor(geometry1, geometry2, collection, ring_map, check_for_orientation); + RingCollection, RingMap, + Strategy + > visitor(geometry1, geometry2, collection, ring_map, strategy, check_for_orientation); geometry::partition < @@ -315,18 +377,20 @@ template < typename Geometry, typename RingCollection, - typename RingMap + typename RingMap, + typename Strategy > inline void assign_parents(Geometry const& geometry, RingCollection const& collection, RingMap& ring_map, + Strategy const& strategy, bool check_for_orientation) { // Call it with an empty geometry as second geometry (source_id == 1) // (ring_map should be empty for source_id==1) Geometry empty; - assign_parents(geometry, empty, collection, ring_map, check_for_orientation); + assign_parents(geometry, empty, collection, ring_map, strategy, check_for_orientation); } diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp index 795523d7a0..0e9bfe2ea0 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -20,6 +20,7 @@ #include <boost/geometry/core/interior_rings.hpp> #include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/signed_size_type.hpp> #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/iterators/ever_circling_iterator.hpp> diff --git a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index fe1a034f8b..c6f540a978 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -36,6 +36,7 @@ #include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> #include <boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp> +#include <boost/geometry/algorithms/detail/signed_size_type.hpp> #include <boost/geometry/util/range.hpp> @@ -56,12 +57,14 @@ struct copy_segments_ring < typename Ring, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Ring const& ring, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -108,7 +111,7 @@ struct copy_segments_ring for (signed_size_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_dups_or_spikes(current_output, *it, robust_policy); + detail::overlay::append_no_dups_or_spikes(current_output, *it, strategy, robust_policy); } } }; @@ -118,20 +121,23 @@ class copy_segments_linestring { private: // remove spikes - template <typename RangeOut, typename Point, typename RobustPolicy> + template <typename RangeOut, typename Point, typename SideStrategy, typename RobustPolicy> static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy, boost::true_type const&) { detail::overlay::append_no_dups_or_spikes(current_output, point, + strategy, robust_policy); } // keep spikes - template <typename RangeOut, typename Point, typename RobustPolicy> + template <typename RangeOut, typename Point, typename SideStrategy, typename RobustPolicy> static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const&, RobustPolicy const&, boost::false_type const&) { @@ -143,12 +149,14 @@ public: < typename LineString, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(LineString const& ls, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -169,7 +177,7 @@ public: for (signed_size_type i = 0; i < count; ++i, ++it) { - append_to_output(current_output, *it, robust_policy, + append_to_output(current_output, *it, strategy, robust_policy, boost::integral_constant<bool, RemoveSpikes>()); } } @@ -182,12 +190,14 @@ struct copy_segments_polygon < typename Polygon, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Polygon const& polygon, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -198,6 +208,7 @@ struct copy_segments_polygon ? geometry::exterior_ring(polygon) : range::at(geometry::interior_rings(polygon), seg_id.ring_index), seg_id, to_index, + strategy, robust_policy, current_output ); @@ -212,12 +223,14 @@ struct copy_segments_box < typename Box, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Box const& box, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -238,7 +251,7 @@ struct copy_segments_box for (signed_size_type i = 0; i < count; i++, index++) { detail::overlay::append_no_dups_or_spikes(current_output, - bp[index % 5], robust_policy); + bp[index % 5], strategy, robust_policy); } } @@ -252,12 +265,14 @@ struct copy_segments_multi < typename MultiGeometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(MultiGeometry const& multi_geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -271,6 +286,7 @@ struct copy_segments_multi // Call the single-version Policy::apply(range::at(multi_geometry, seg_id.multi_index), seg_id, to_index, + strategy, robust_policy, current_output); } @@ -340,12 +356,14 @@ template bool Reverse, typename Geometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > inline void copy_segments(Geometry const& geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& range_out) { @@ -355,7 +373,7 @@ inline void copy_segments(Geometry const& geometry, < typename tag<Geometry>::type, Reverse - >::apply(geometry, seg_id, to_index, robust_policy, range_out); + >::apply(geometry, seg_id, to_index, strategy, robust_policy, range_out); } diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index 5cab2b4cb8..47225328df 100644 --- a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -19,20 +24,21 @@ # include <iostream> # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> # include <boost/geometry/io/wkt/wkt.hpp> -# define BOOST_GEOMETRY_DEBUG_IDENTIFIER +# if ! defined(BOOST_GEOMETRY_DEBUG_IDENTIFIER) +# define BOOST_GEOMETRY_DEBUG_IDENTIFIER + #endif #endif #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 @@ -58,7 +64,7 @@ template typename Turns, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_sort(Operations& operations, Turns const& turns, @@ -66,7 +72,7 @@ inline void enrich_sort(Operations& operations, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& /*strategy*/) + SideStrategy const& strategy) { std::sort(boost::begin(operations), boost::end(operations), @@ -76,8 +82,9 @@ inline void enrich_sort(Operations& operations, typename boost::range_value<Operations>::type, Geometry1, Geometry2, RobustPolicy, + SideStrategy, Reverse1, Reverse2 - >(turns, for_operation, geometry1, geometry2, robust_policy)); + >(turns, for_operation, geometry1, geometry2, robust_policy, strategy)); } @@ -145,7 +152,7 @@ inline void enrich_assign(Operations& operations, Turns& turns) it != boost::end(operations); ++it) { - op_type& op = turns[it->turn_index] + op_type const& op = turns[it->turn_index] .operations[it->operation_index]; std::cout << it->turn_index @@ -171,9 +178,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; @@ -195,15 +200,6 @@ inline void create_map(Turns const& turns, continue; } - if (for_operation == operation_intersection - && turn.cluster_id == -1 - && turn.both(operation_union)) - { - // Only include uu turns if part of cluster (to block potential paths), - // otherwise they can block possibly viable paths - continue; - } - std::size_t op_index = 0; for (typename boost::range_iterator<container_type const>::type op_it = boost::begin(turn.operations); @@ -225,6 +221,56 @@ inline void create_map(Turns const& turns, } } +template <typename Point1, typename Point2> +inline typename geometry::coordinate_type<Point1>::type + distance_measure(Point1 const& a, Point2 const& b) +{ + // TODO: use comparable distance for point-point instead - but that + // causes currently cycling include problems + typedef typename geometry::coordinate_type<Point1>::type ctype; + ctype const dx = get<0>(a) - get<0>(b); + ctype const dy = get<1>(a) - get<1>(b); + return dx * dx + dy * dy; +} + +template <typename Turns> +inline void calculate_remaining_distance(Turns& turns) +{ + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename turn_type::turn_operation_type op_type; + + for (typename boost::range_iterator<Turns>::type + it = boost::begin(turns); + it != boost::end(turns); + ++it) + { + turn_type& turn = *it; + if (! turn.both(detail::overlay::operation_continue)) + { + continue; + } + + op_type& op0 = turn.operations[0]; + op_type& op1 = turn.operations[1]; + + if (op0.remaining_distance != 0 + || op1.remaining_distance != 0) + { + continue; + } + + int const to_index0 = op0.enriched.get_next_turn_index(); + int const to_index1 = op1.enriched.get_next_turn_index(); + if (to_index1 >= 0 + && to_index1 >= 0 + && to_index0 != to_index1) + { + op0.remaining_distance = distance_measure(turn.point, turns[to_index0].point); + op1.remaining_distance = distance_measure(turn.point, turns[to_index1].point); + } + } +} + }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL @@ -239,7 +285,7 @@ inline void create_map(Turns const& turns, \tparam Clusters type of cluster container \tparam Geometry1 \tparam_geometry \tparam Geometry2 \tparam_geometry -\tparam Strategy side strategy type +\tparam SideStrategy side strategy type \param turns container containing intersection points \param clusters container containing clusters \param geometry1 \param_geometry @@ -255,16 +301,21 @@ template typename Clusters, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_intersection_points(Turns& turns, Clusters& clusters, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& strategy) + SideStrategy 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 @@ -278,27 +329,68 @@ inline void enrich_intersection_points(Turns& turns, std::vector<indexed_turn_operation> > mapped_vector_type; + bool has_cc = false; bool const has_colocations - = detail::overlay::handle_colocations<Reverse1, Reverse2>(turns, + = detail::overlay::handle_colocations<Reverse1, Reverse2, OverlayType>(turns, clusters, geometry1, geometry2); - // Discard none turns, if any + // Discard turns not part of target overlay for (typename boost::range_iterator<Turns>::type it = boost::begin(turns); it != boost::end(turns); ++it) { - if (it->both(detail::overlay::operation_none)) + turn_type& turn = *it; + + if (turn.both(detail::overlay::operation_none)) + { + turn.discarded = true; + continue; + } + + if (turn.both(opposite_operation)) { - it->discarded = true; + // For intersections, remove uu to avoid the need to travel + // a union (during intersection) in uu/cc clusters (e.g. #31,#32,#33) + // Also, for union, discard ii + turn.discarded = true; + turn.cluster_id = -1; + continue; + } + + if (detail::overlay::is_self_turn<OverlayType>(turn) + && turn.cluster_id < 0 + && ! turn.both(target_operation)) + { + // Only keep self-uu-turns or self-ii-turns + turn.discarded = true; + turn.cluster_id = -1; + continue; + } + + if (! turn.discarded + && turn.both(detail::overlay::operation_continue)) + { + has_cc = true; } } + 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 @@ -312,7 +404,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); } @@ -331,8 +423,22 @@ inline void enrich_intersection_points(Turns& turns, if (has_colocations) { - detail::overlay::gather_cluster_properties<Reverse1, Reverse2>( - clusters, turns, for_operation, geometry1, geometry2); + // 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, target_operation, + geometry1, geometry2, strategy); + + detail::overlay::cleanup_clusters(turns, clusters); + } + + if (has_cc) + { + detail::overlay::calculate_remaining_distance(turns); } #ifdef BOOST_GEOMETRY_DEBUG_ENRICH diff --git a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp index 2643415343..fdffd665e4 100644 --- a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -37,10 +37,17 @@ struct enrichment_info , startable(true) , count_left(0) , count_right(0) + , rank(-1) , zone(-1) - , only_turn_on_ring(false) + , region_id(-1) + , isolated(false) {} + inline signed_size_type get_next_turn_index() const + { + return next_ip_index == -1 ? travels_to_ip_index : next_ip_index; + } + // vertex to which is free travel after this IP, // so from "segment_index+1" to "travels_to_vertex_index", without IP-s, // can be -1 @@ -57,8 +64,10 @@ struct enrichment_info // Counts if polygons left/right of this operation std::size_t count_left; std::size_t count_right; + signed_size_type rank; // in cluster signed_size_type zone; // open zone, in cluster - bool only_turn_on_ring; // True if it is the only turn on a ring (for clusters) + signed_size_type region_id; + bool isolated; }; diff --git a/boost/geometry/algorithms/detail/overlay/follow.hpp b/boost/geometry/algorithms/detail/overlay/follow.hpp index 22807b5140..589e12cc2b 100644 --- a/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -2,10 +2,10 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. - +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -55,21 +55,14 @@ template typename Turn, typename Operation, typename LineString, - typename Polygon + typename Polygon, + typename PtInPolyStrategy > static inline bool last_covered_by(Turn const& turn, Operation const& op, - LineString const& linestring, Polygon const& polygon) + LineString const& linestring, Polygon const& polygon, + PtInPolyStrategy const& strategy) { - // Check any point between the this one and the first IP - typedef typename geometry::point_type<LineString>::type point_type; - point_type point_in_between; - detail::point_on_border::midpoint_helper - < - point_type, - 0, dimension<point_type>::value - >::apply(point_in_between, *(::boost::begin(linestring) + op.seg_id.segment_index), turn.point); - - return geometry::covered_by(point_in_between, polygon); + return geometry::covered_by(range::at(linestring, op.seg_id.segment_index), polygon, strategy); } @@ -78,17 +71,19 @@ template typename Turn, typename Operation, typename LineString, - typename Polygon + typename Polygon, + typename PtInPolyStrategy > static inline bool is_leaving(Turn const& turn, Operation const& op, bool entered, bool first, - LineString const& linestring, Polygon const& polygon) + LineString const& linestring, Polygon const& polygon, + PtInPolyStrategy const& strategy) { if (op.operation == operation_union) { return entered || turn.method == method_crosses - || (first && last_covered_by(turn, op, linestring, polygon)) + || (first && last_covered_by(turn, op, linestring, polygon, strategy)) ; } return false; @@ -100,11 +95,13 @@ template typename Turn, typename Operation, typename LineString, - typename Polygon + typename Polygon, + typename PtInPolyStrategy > static inline bool is_staying_inside(Turn const& turn, Operation const& op, bool entered, bool first, - LineString const& linestring, Polygon const& polygon) + LineString const& linestring, Polygon const& polygon, + PtInPolyStrategy const& strategy) { if (turn.method == method_crosses) { @@ -115,7 +112,7 @@ static inline bool is_staying_inside(Turn const& turn, Operation const& op, if (is_entering(turn, op)) { - return entered || (first && last_covered_by(turn, op, linestring, polygon)); + return entered || (first && last_covered_by(turn, op, linestring, polygon, strategy)); } return false; @@ -126,14 +123,16 @@ template typename Turn, typename Operation, typename Linestring, - typename Polygon + typename Polygon, + typename PtInPolyStrategy > static inline bool was_entered(Turn const& turn, Operation const& op, bool first, - Linestring const& linestring, Polygon const& polygon) + Linestring const& linestring, Polygon const& polygon, + PtInPolyStrategy const& strategy) { if (first && (turn.method == method_collinear || turn.method == method_equal)) { - return last_covered_by(turn, op, linestring, polygon); + return last_covered_by(turn, op, linestring, polygon, strategy); } return false; } @@ -158,6 +157,7 @@ struct action_selector<overlay_intersection, RemoveSpikes> typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -165,6 +165,7 @@ struct action_selector<overlay_intersection, RemoveSpikes> segment_identifier& segment_id, signed_size_type , Point const& point, Operation const& operation, + SideStrategy const& , RobustPolicy const& , OutputIterator& ) { @@ -181,6 +182,7 @@ struct action_selector<overlay_intersection, RemoveSpikes> typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -188,6 +190,7 @@ struct action_selector<overlay_intersection, RemoveSpikes> segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& , + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { @@ -196,7 +199,7 @@ struct action_selector<overlay_intersection, RemoveSpikes> detail::copy_segments::copy_segments_linestring < false, RemoveSpikes - >::apply(linestring, segment_id, index, robust_policy, current_piece); + >::apply(linestring, segment_id, index, strategy, robust_policy, current_piece); detail::overlay::append_no_duplicates(current_piece, point); if (::boost::size(current_piece) > 1) { @@ -235,17 +238,9 @@ struct action_selector<overlay_intersection, RemoveSpikes> return entered; } - template - < - typename Point, - typename Geometry, - typename RobustPolicy - > - static inline bool included(Point const& point, - Geometry const& geometry, - RobustPolicy const& ) + static inline bool included(int inside_value) { - return geometry::covered_by(point, geometry); + return inside_value >= 0; // covered_by } }; @@ -263,6 +258,7 @@ struct action_selector<overlay_difference, RemoveSpikes> typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -270,11 +266,12 @@ struct action_selector<overlay_difference, RemoveSpikes> segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::leave(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -284,6 +281,7 @@ struct action_selector<overlay_difference, RemoveSpikes> typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -291,11 +289,12 @@ struct action_selector<overlay_difference, RemoveSpikes> segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::enter(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -319,17 +318,9 @@ struct action_selector<overlay_difference, RemoveSpikes> return ! normal_action::is_entered(entered); } - template - < - typename Point, - typename Geometry, - typename RobustPolicy - > - static inline bool included(Point const& point, - Geometry const& geometry, - RobustPolicy const& robust_policy) + static inline bool included(int inside_value) { - return ! normal_action::included(point, geometry, robust_policy); + return ! normal_action::included(inside_value); } }; @@ -403,33 +394,27 @@ class follow public : - template - < - typename Point, - typename Geometry, - typename RobustPolicy - > - static inline bool included(Point const& point, - Geometry const& geometry, - RobustPolicy const& robust_policy) + static inline bool included(int inside_value) { return following::action_selector < OverlayType, RemoveSpikes - >::included(point, geometry, robust_policy); + >::included(inside_value); } template < typename Turns, typename OutputIterator, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon, detail::overlay::operation_type , // TODO: this parameter might be redundant Turns& turns, RobustPolicy const& robust_policy, - OutputIterator out) + OutputIterator out, + Strategy const& strategy) { typedef typename boost::range_iterator<Turns>::type turn_iterator; typedef typename boost::range_value<Turns>::type turn_type; @@ -440,6 +425,12 @@ public : typedef following::action_selector<OverlayType, RemoveSpikes> action; + typename Strategy::template point_in_geometry_strategy + < + LineString, Polygon + >::type const pt_in_poly_strategy + = strategy.template get_point_in_geometry_strategy<LineString, Polygon>(); + // Sort intersection points on segments-along-linestring, and distance // (like in enrich is done for poly/poly) std::sort(boost::begin(turns), boost::end(turns), sort_on_segment<turn_type>()); @@ -454,13 +445,13 @@ public : { turn_operation_iterator_type iit = boost::begin(it->operations); - if (following::was_entered(*it, *iit, first, linestring, polygon)) + if (following::was_entered(*it, *iit, first, linestring, polygon, pt_in_poly_strategy)) { debug_traverse(*it, *iit, "-> Was entered"); entered = true; } - if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon)) + if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy)) { debug_traverse(*it, *iit, "-> Staying inside"); @@ -473,17 +464,17 @@ public : entered = true; action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } - else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon)) + else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy)) { debug_traverse(*it, *iit, "-> Leaving"); entered = false; action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } first = false; @@ -497,7 +488,7 @@ public : >::apply(linestring, current_segment_id, static_cast<signed_size_type>(boost::size(linestring) - 1), - robust_policy, + strategy, robust_policy, current_piece); } diff --git a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp index c249ff57ff..2a374bf0b0 100644 --- a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp +++ b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -2,12 +2,14 @@ // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // 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_FOLLOW_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP @@ -183,7 +185,8 @@ protected: typename TurnIterator, typename TurnOperationIterator, typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_turn(TurnIterator it, @@ -193,7 +196,8 @@ protected: Linestring const& linestring, LinestringOut& current_piece, SegmentIdentifier& current_segment_id, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // We don't rescale linear/linear detail::no_rescale_policy robust_policy; @@ -208,7 +212,7 @@ protected: action::enter(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } ++enter_count; } @@ -223,7 +227,7 @@ protected: action::leave(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } } else if ( FollowIsolatedPoints @@ -249,14 +253,16 @@ protected: template < typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_end(bool entered, Linestring const& linestring, SegmentIdentifier const& current_segment_id, LinestringOut& current_piece, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { if ( action::is_entered(entered) ) { @@ -269,6 +275,7 @@ protected: >::apply(linestring, current_segment_id, static_cast<signed_size_type>(boost::size(linestring) - 1), + strategy, robust_policy, current_piece); } @@ -283,11 +290,12 @@ protected: } public: - template <typename TurnIterator, typename OutputIterator> + template <typename TurnIterator, typename OutputIterator, typename SideStrategy> static inline OutputIterator apply(Linestring const& linestring, Linear const&, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // Iterate through all intersection points (they are // ordered along the each line) @@ -304,7 +312,8 @@ public: entered, enter_count, linestring, current_piece, current_segment_id, - oit); + oit, + strategy); } #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) @@ -318,7 +327,8 @@ public: return process_end(entered, linestring, current_segment_id, current_piece, - oit); + oit, + strategy); } }; @@ -413,11 +423,12 @@ protected: }; public: - template <typename TurnIterator, typename OutputIterator> + template <typename TurnIterator, typename OutputIterator, typename SideStrategy> static inline OutputIterator apply(MultiLinestring const& multilinestring, Linear const& linear, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { BOOST_GEOMETRY_ASSERT( first != beyond ); @@ -447,7 +458,7 @@ public: has_other_multi_id(current_multi_id)); oit = Base::apply(*(ls_first + current_multi_id), - linear, per_ls_current, per_ls_next, oit); + linear, per_ls_current, per_ls_next, oit, strategy); signed_size_type next_multi_id = -1; linestring_iterator ls_next = ls_beyond; diff --git a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp index ea9aa29f19..2eec6af665 100644 --- a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -31,20 +36,15 @@ namespace detail { namespace overlay but we still need to know which comes first. Therefore, it is useful that using sides we are able to discover this. */ -template <typename Point1> struct get_relative_order { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Point1>::type - >::type strategy; - - template <typename Point> + template <typename Point, typename SideStrategy> static inline int value_via_product(Point const& ti, Point const& tj, - Point const& ui, Point const& uj, int factor) + Point const& ui, Point const& uj, int factor, + SideStrategy const& strategy) { - int const side_ti_u = strategy::apply(ti, tj, ui); - int const side_tj_u = strategy::apply(ti, tj, uj); + int const side_ti_u = strategy.apply(ti, tj, ui); + int const side_tj_u = strategy.apply(ti, tj, uj); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER std::cout << (factor == 1 ? " r//s " : " s//r ") @@ -57,13 +57,15 @@ struct get_relative_order } + template <typename Point1, typename SideStrategy> static inline int apply( Point1 const& pi, Point1 const& pj, Point1 const& ri, Point1 const& rj, - Point1 const& si, Point1 const& sj) + Point1 const& si, Point1 const& sj, + SideStrategy const& strategy) { - int const side_ri_p = strategy::apply(pi, pj, ri); - int const side_si_p = strategy::apply(pi, pj, si); + int const side_ri_p = strategy.apply(pi, pj, ri); + int const side_si_p = strategy.apply(pi, pj, si); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER int const side_rj_p = strategy::apply(pi, pj, rj); @@ -72,10 +74,10 @@ struct get_relative_order std::cout << " s//p: " << side_si_p << " / " << side_sj_p; #endif - int value = value_via_product(si, sj, ri, rj, 1); + int value = value_via_product(si, sj, ri, rj, 1, strategy); if (value == 0) { - value = value_via_product(ri, rj, si, sj, -1); + value = value_via_product(ri, rj, si, sj, -1, strategy); } int const order = side_ri_p * side_ri_p * side_si_p * value; diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 08bc342186..895952c8fc 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -190,6 +190,7 @@ struct touch_interior : public base_turn_handler // Q turns left on the right side of P (test "MR3") // Both directions for "intersection" both(ti, operation_intersection); + ti.touch_only = true; } else if (side_qi_p == 1 && side_qk_p == 1 && side_qk_q == -1) { @@ -197,6 +198,7 @@ struct touch_interior : public base_turn_handler // Union: take both operation // Intersection: skip both(ti, operation_union); + ti.touch_only = true; } else if (side_qi_p == side_qk_p && side_qi_p == side_qk_q) { @@ -207,6 +209,7 @@ struct touch_interior : public base_turn_handler unsigned int index = side_qk_q == 1 ? index_q : index_p; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; + ti.touch_only = true; } else if (side_qk_p == 0) { @@ -346,6 +349,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == -side_qk_q) { ui_else_iu(! q_turns_left, ti); + ti.touch_only = true; return; } @@ -358,6 +362,10 @@ struct touch : public base_turn_handler { ti.operations[1].operation = operation_blocked; } + else + { + ti.touch_only = true; + } //block_second(block_q, ti); return; } @@ -373,6 +381,10 @@ struct touch : public base_turn_handler : side_qi_p1 == 1 || side_qk_p1 == 1 ? operation_union : operation_intersection; + if (! block_q) + { + ti.touch_only = true; + } return; } @@ -400,6 +412,7 @@ struct touch : public base_turn_handler if (side_pk_q1 == side_qk_p1) { uu_else_ii(right_to_left, ti); + ti.touch_only = true; return; } } @@ -418,6 +431,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == side_qk_p1) { ui_else_iu(right_to_left, ti); + ti.touch_only = true; return; } } diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index 5f2cb07faf..f8247cd240 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -66,7 +66,7 @@ struct side_calculator Qj const& m_qj; Qk const& m_qk; - SideStrategy const& m_side_strategy; + SideStrategy m_side_strategy; }; template <typename Point1, typename Point2, typename RobustPolicy> diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 4e97a84a37..f88dfe8422 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2014, 2016, 2017. // Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. @@ -90,6 +90,9 @@ struct no_interrupt_policy { static bool const enabled = false; + // variable required by self_get_turn_points::get_turns + static bool const has_intersections = false; + template <typename Range> static inline bool apply(Range const&) { @@ -230,7 +233,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec1.bounding_box, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator<range1_iterator> nd_next1( @@ -248,7 +251,7 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec2.bounding_box, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -356,7 +359,7 @@ private : // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here template <typename Range, typename Section, typename Box, typename RobustPolicy> - static inline void get_start_point_iterator(Section & section, + static inline void get_start_point_iterator(Section const& section, Range const& range, typename boost::range_iterator<Range const>::type& it, typename boost::range_iterator<Range const>::type& prev, @@ -370,7 +373,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, section.bounding_box, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -418,6 +421,7 @@ struct section_visitor { if (! detail::disjoint::disjoint_box_box(sec1.bounding_box, sec2.bounding_box)) { + // false if interrupted return get_turns_in_sections < Geometry1, @@ -425,13 +429,12 @@ struct section_visitor Reverse1, Reverse2, Section, Section, TurnPolicy - >::apply( - m_source_id1, m_geometry1, sec1, - m_source_id2, m_geometry2, sec2, - false, - m_intersection_strategy, - m_rescale_policy, - m_turns, m_interrupt_policy); + >::apply(m_source_id1, m_geometry1, sec1, + m_source_id2, m_geometry2, sec2, + false, + m_intersection_strategy, + m_rescale_policy, + m_turns, m_interrupt_policy); } return true; } @@ -473,10 +476,13 @@ public: sections_type sec1, sec2; typedef boost::mpl::vector_c<std::size_t, 0, 1> dimensions; + typename IntersectionStrategy::envelope_strategy_type const + envelope_strategy = intersection_strategy.get_envelope_strategy(); + geometry::sectionalize<Reverse1, dimensions>(geometry1, robust_policy, - sec1, 0); + sec1, envelope_strategy, 0); geometry::sectionalize<Reverse2, dimensions>(geometry2, robust_policy, - sec2, 1); + sec2, envelope_strategy, 1); // ... and then partition them, intersecting overlapping sections in visitor method section_visitor diff --git a/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 400ed3b881..f3311b34e9 100644 --- a/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -262,15 +267,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 +327,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,17 +342,44 @@ 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) { turn.discarded = true; + // Set cluster id to -1, but don't clear colocated flags turn.cluster_id = -1; // To remove it later from clusters ids.insert(id); @@ -378,6 +397,13 @@ inline bool is_ie_turn(segment_identifier const& ext_seg_0, segment_identifier const& int_seg_0, segment_identifier const& other_seg_1) { + if (ext_seg_0.source_index == ext_seg_1.source_index) + { + // External turn is a self-turn, dont discard internal turn for this + return false; + } + + // Compares two segment identifiers from two turns (external / one internal) // From first turn [0], both are from same polygon (multi_index), @@ -411,6 +437,7 @@ inline bool is_ie_turn(segment_identifier const& ext_seg_0, template < bool Reverse0, bool Reverse1, // Reverse interpretation interior/exterior + overlay_type OverlayType, typename Turns, typename Clusters > @@ -435,19 +462,6 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters) segment_identifier const& seg_0 = turn.operations[0].seg_id; segment_identifier const& seg_1 = turn.operations[1].seg_id; - if (turn.both(operation_intersection) - && Reverse0 == Reverse1) - { - if ( is_interior<Reverse0>(seg_0) - && is_interior<Reverse1>(seg_1)) - { - // ii touch with, two interior rings - discard_ie_turn(turn, ids_to_remove, *it); - } - - continue; - } - if (! (turn.both(operation_union) || turn.combination(operation_union, operation_blocked))) { @@ -487,6 +501,53 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters) } } +template +< + typename Turns, + typename Clusters +> +inline void set_colocation(Turns& turns, Clusters const& clusters) +{ + typedef std::set<signed_size_type>::const_iterator set_iterator; + typedef typename boost::range_value<Turns>::type turn_type; + + for (typename Clusters::const_iterator cit = clusters.begin(); + cit != clusters.end(); ++cit) + { + cluster_info const& cinfo = cit->second; + std::set<signed_size_type> const& ids = cinfo.turn_indices; + + bool has_ii = false; + bool has_uu = false; + for (set_iterator it = ids.begin(); it != ids.end(); ++it) + { + turn_type const& turn = turns[*it]; + if (turn.both(operation_intersection)) + { + has_ii = true; + } + if (turn.both(operation_union) || turn.combination(operation_union, operation_blocked)) + { + has_uu = true; + } + } + if (has_ii || has_uu) + { + for (set_iterator it = ids.begin(); it != ids.end(); ++it) + { + turn_type& turn = turns[*it]; + if (has_ii) + { + turn.colocated_ii = true; + } + if (has_uu) + { + turn.colocated_uu = true; + } + } + } + } +} // Checks colocated turns and flags combinations of uu/other, possibly a // combination of a ring touching another geometry's interior ring which is @@ -498,6 +559,7 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters) template < bool Reverse1, bool Reverse2, + overlay_type OverlayType, typename Turns, typename Clusters, typename Geometry1, @@ -578,12 +640,13 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, } assign_cluster_to_turns(turns, clusters, cluster_per_segment); + set_colocation(turns, clusters); discard_interior_exterior_turns < do_reverse<geometry::point_order<Geometry1>::value>::value != Reverse1, - do_reverse<geometry::point_order<Geometry2>::value>::value != Reverse2 + 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; @@ -598,7 +661,8 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, std::cout << geometry::wkt(turns[toi.turn_index].point) << std::boolalpha << " discarded=" << turns[toi.turn_index].discarded - << " colocated=" << turns[toi.turn_index].colocated + << " colocated(uu)=" << turns[toi.turn_index].colocated_uu + << " colocated(ii)=" << turns[toi.turn_index].colocated_ii << " " << operation_char(turns[toi.turn_index].operations[0].operation) << " " << turns[toi.turn_index].operations[0].seg_id << " " << turns[toi.turn_index].operations[0].fraction @@ -634,14 +698,17 @@ struct is_turn_index template < bool Reverse1, bool Reverse2, + overlay_type OverlayType, typename Turns, typename Clusters, typename Geometry1, - typename Geometry2 + typename Geometry2, + typename SideStrategy > inline void gather_cluster_properties(Clusters& clusters, Turns& turns, operation_type for_operation, - Geometry1 const& geometry1, Geometry2 const& geometry2) + Geometry1 const& geometry1, Geometry2 const& geometry2, + SideStrategy const& strategy) { typedef typename boost::range_value<Turns>::type turn_type; typedef typename turn_type::point_type point_type; @@ -651,7 +718,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, // right side typedef sort_by_side::side_sorter < - Reverse1, Reverse2, point_type, std::less<int> + Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less<int> > sbs_type; for (typename Clusters::iterator mit = clusters.begin(); @@ -664,11 +731,11 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, continue; } - sbs_type sbs; + sbs_type sbs(strategy); 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; @@ -689,6 +756,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++) { @@ -696,6 +765,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; @@ -703,6 +777,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, op.enriched.count_left = ranked.count_left; op.enriched.count_right = ranked.count_right; + op.enriched.rank = ranked.rank; op.enriched.zone = ranked.zone; if ((for_operation == operation_union @@ -714,7 +789,6 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, } } - cinfo.open_count = sbs.open_count(for_operation); } } diff --git a/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp new file mode 100644 index 0000000000..39c55db759 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -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 diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 3244480f48..7106e7b480 100644 --- a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -30,10 +30,11 @@ #include <boost/geometry/algorithms/convert.hpp> #include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/detail/overlay/clip_linestring.hpp> +#include <boost/geometry/algorithms/detail/overlay/follow.hpp> #include <boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> -#include <boost/geometry/algorithms/detail/overlay/follow.hpp> +#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp> #include <boost/geometry/policies/robustness/robust_point_type.hpp> #include <boost/geometry/policies/robustness/segment_ratio_type.hpp> @@ -288,6 +289,27 @@ struct intersection_of_linestring_with_areal >::apply(boost::begin(turns), boost::end(turns)); } + template <typename Turns> + static inline int inside_or_outside_turn(Turns const& turns) + { + using namespace overlay; + for (typename Turns::const_iterator it = turns.begin(); + it != turns.end(); ++it) + { + operation_type op0 = it->operations[0].operation; + operation_type op1 = it->operations[1].operation; + if (op0 == operation_intersection && op1 == operation_intersection) + { + return 1; // inside + } + else if (op0 == operation_union && op1 == operation_union) + { + return -1; // outside + } + } + return 0; + } + template < typename LineString, typename Areal, @@ -331,19 +353,21 @@ struct intersection_of_linestring_with_areal if (no_crossing_turns_or_empty(turns)) { - // No intersection points, it is either completely + // No intersection points, it is either // inside (interior + borders) - // or completely outside + // or outside (exterior + borders) - // Use border point (on a segment) to check this - // (because turn points might skip some cases) - point_type border_point; - if (! geometry::point_on_border(border_point, linestring, true)) + // analyse the turns + int inside_value = inside_or_outside_turn(turns); + if (inside_value == 0) { - return out; + // if needed analyse points of a linestring + // NOTE: range_in_geometry checks points of a linestring + // until a point inside/outside areal is found + inside_value = overlay::range_in_geometry(linestring, areal, strategy); } - - if (follower::included(border_point, areal, robust_policy)) + // add point to the output if conditions are met + if (inside_value != 0 && follower::included(inside_value)) { LineStringOut copy; geometry::convert(linestring, copy); @@ -365,7 +389,7 @@ struct intersection_of_linestring_with_areal ( linestring, areal, geometry::detail::overlay::operation_intersection, - turns, robust_policy, out + turns, robust_policy, out, strategy ); } }; diff --git a/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp b/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp new file mode 100644 index 0000000000..9cb7a0fca9 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp @@ -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 diff --git a/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp b/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp index 21868a2939..dd30635ee2 100644 --- a/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp +++ b/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -60,6 +65,7 @@ template typename Indexed, typename Geometry1, typename Geometry2, typename RobustPolicy, + typename SideStrategy, bool Reverse1, bool Reverse2 > struct less_by_segment_ratio @@ -68,12 +74,14 @@ struct less_by_segment_ratio , operation_type for_operation , Geometry1 const& geometry1 , Geometry2 const& geometry2 - , RobustPolicy const& robust_policy) + , RobustPolicy const& robust_policy + , SideStrategy const& strategy) : m_turns(turns) , m_for_operation(for_operation) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_robust_policy(robust_policy) + , m_strategy(strategy) { } @@ -84,6 +92,7 @@ private : Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; RobustPolicy const& m_robust_policy; + SideStrategy const& m_strategy; typedef typename geometry::point_type<Geometry1>::type point_type; @@ -108,13 +117,8 @@ private : *right.other_seg_id, si, sj); - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<point_type>::type - >::type strategy; - - int const side_rj_p = strategy::apply(pi, pj, rj); - int const side_sj_p = strategy::apply(pi, pj, sj); + int const side_rj_p = m_strategy.apply(pi, pj, rj); + int const side_sj_p = m_strategy.apply(pi, pj, sj); // Put the one turning left (1; right == -1) as last if (side_rj_p != side_sj_p) @@ -122,8 +126,8 @@ private : return side_rj_p < side_sj_p; } - int const side_sj_r = strategy::apply(ri, rj, sj); - int const side_rj_s = strategy::apply(si, sj, rj); + int const side_sj_r = m_strategy.apply(ri, rj, sj); + int const side_rj_s = m_strategy.apply(si, sj, rj); // If they both turn left: the most left as last // If they both turn right: this is not relevant, but take also here most left diff --git a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp index a74bb33ba1..21d079d95c 100644 --- a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp +++ b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -194,13 +194,15 @@ protected: typename Turns, typename LinearGeometry1, typename LinearGeometry2, - typename OutputIterator + typename OutputIterator, + typename IntersectionStrategy > static inline OutputIterator sort_and_follow_turns(Turns& turns, LinearGeometry1 const& linear1, LinearGeometry2 const& linear2, - OutputIterator oit) + OutputIterator oit, + IntersectionStrategy const& strategy) { // remove turns that have no added value turns::filter_continue_turns @@ -228,7 +230,7 @@ protected: FollowIsolatedPoints, !EnableFilterContinueTurns || OverlayType == overlay_intersection >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), - oit); + oit, strategy.get_side_strategy()); } public: @@ -277,7 +279,7 @@ public: OverlayType, EnableFollowIsolatedPoints && OverlayType == overlay_intersection - >(turns, linear1, linear2, oit); + >(turns, linear1, linear2, oit, strategy); } }; diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp index f1af2f1949..10829abd4f 100644 --- a/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -28,9 +28,11 @@ #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> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> #include <boost/geometry/algorithms/detail/recalculate.hpp> @@ -87,29 +89,58 @@ struct overlay_null_visitor {} }; -template <typename Turns, typename TurnInfoMap> -inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns) +template +< + overlay_type OverlayType, + typename TurnInfoMap, + typename Turns, + typename Clusters +> +inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, Clusters const& clusters) { typedef typename boost::range_value<Turns>::type turn_type; typedef typename turn_type::container_type container_type; + static const operation_type target_operation + = operation_from_overlay<OverlayType>::value; + static const operation_type opposite_operation + = target_operation == operation_union ? operation_intersection : operation_union; + + signed_size_type turn_index = 0; for (typename boost::range_iterator<Turns const>::type it = boost::begin(turns); it != boost::end(turns); - ++it) + ++it, turn_index++) { - typename boost::range_value<Turns>::type const& turn_info = *it; - - if (turn_info.discarded - && ! turn_info.any_blocked() - && ! turn_info.colocated) + typename boost::range_value<Turns>::type const& turn = *it; + + bool const colocated_target = target_operation == operation_union + ? 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() + || turn.operations[0].visited.rejected() + || turn.operations[1].visited.finalized() + || turn.operations[1].visited.rejected() + || turn.both(operation_blocked) + || turn.combination(opposite_operation, operation_blocked); + + bool is_closed = false; + if (turn.cluster_id >= 0 && target_operation == operation_union) { - continue; + 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_info.operations); - op_it != boost::end(turn_info.operations); + op_it = boost::begin(turn.operations); + op_it != boost::end(turn.operations); ++op_it) { ring_identifier const ring_id @@ -118,7 +149,34 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns) op_it->seg_id.multi_index, op_it->seg_id.ring_index ); - turn_info_map[ring_id].has_normal_turn = true; + + if (traversed || is_closed || ! op_it->enriched.startable) + { + turn_info_map[ring_id].has_traversed_turn = true; + } + 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 + // unless it is two interior inner rings colocated with a uu + + // 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 - but not if it is a self-turn + turn_info_map[ring_id].has_traversed_turn = true; + } + else if (colocated_opp && ! colocated_target) + { + // 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; + } } } } @@ -128,18 +186,27 @@ template < typename GeometryOut, overlay_type OverlayType, bool ReverseOut, typename Geometry1, typename Geometry2, - typename OutputIterator + typename OutputIterator, typename Strategy > inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, Geometry2 const& geometry2, - OutputIterator out) + OutputIterator out, Strategy const& strategy) { typedef std::deque < typename geometry::ring_type<GeometryOut>::type > ring_container_type; - typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; + typedef typename geometry::point_type<Geometry1>::type point_type1; + + typedef ring_properties + < + point_type1, + typename Strategy::template area_strategy + < + point_type1 + >::type::return_type + > properties; // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) @@ -164,9 +231,9 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, std::map<ring_identifier, ring_turn_info> empty; std::map<ring_identifier, properties> all_of_one_of_them; - select_rings<OverlayType>(geometry1, geometry2, empty, all_of_one_of_them); + select_rings<OverlayType>(geometry1, geometry2, empty, all_of_one_of_them, strategy); ring_container_type rings; - assign_parents(geometry1, geometry2, rings, all_of_one_of_them); + assign_parents(geometry1, geometry2, rings, all_of_one_of_them, strategy); return add_rings<GeometryOut>(all_of_one_of_them, geometry1, geometry2, rings, out); } @@ -201,7 +268,7 @@ struct overlay return return_if_one_input_is_empty < GeometryOut, OverlayType, ReverseOut - >(geometry1, geometry2, out); + >(geometry1, geometry2, out, strategy); } typedef typename geometry::point_type<GeometryOut>::type point_type; @@ -238,10 +305,20 @@ std::cout << "get turns" << std::endl; visitor.visit_turns(1, turns); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + { + 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 + + #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE std::cout << "enrich" << std::endl; #endif - typename Strategy::side_strategy_type side_strategy; + typename Strategy::side_strategy_type side_strategy = strategy.get_side_strategy(); cluster_type clusters; geometry::enrich_intersection_points<Reverse1, Reverse2, OverlayType>(turns, @@ -271,33 +348,38 @@ std::cout << "traverse" << std::endl; ); std::map<ring_identifier, ring_turn_info> turn_info_per_ring; - get_ring_turn_info(turn_info_per_ring, turns); + get_ring_turn_info<OverlayType>(turn_info_per_ring, turns, clusters); + + typedef typename Strategy::template area_strategy<point_type>::type area_strategy_type; typedef ring_properties - < - typename geometry::point_type<GeometryOut>::type - > properties; + < + point_type, + typename area_strategy_type::return_type + > properties; // Select all rings which are NOT touched by any intersection point std::map<ring_identifier, properties> selected_ring_properties; select_rings<OverlayType>(geometry1, geometry2, turn_info_per_ring, - selected_ring_properties); + selected_ring_properties, strategy); // Add rings created during traversal { + area_strategy_type const area_strategy = strategy.template get_area_strategy<point_type>(); + ring_identifier id(2, 0, -1); for (typename boost::range_iterator<ring_container_type>::type it = boost::begin(rings); it != boost::end(rings); ++it) { - selected_ring_properties[id] = properties(*it); + selected_ring_properties[id] = properties(*it, area_strategy); selected_ring_properties[id].reversed = ReverseOut; id.multi_index++; } } - assign_parents(geometry1, geometry2, rings, selected_ring_properties); + assign_parents(geometry1, geometry2, rings, selected_ring_properties, strategy); return add_rings<GeometryOut>(selected_ring_properties, geometry1, geometry2, rings, out); } diff --git a/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp b/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp index a26f54e008..0a7c3bc469 100644 --- a/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp +++ b/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp @@ -1,5 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. + // Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -127,25 +129,57 @@ class multipoint_linear_point { private: // structs for partition -- start - struct expand_box + struct expand_box_point { - template <typename Box, typename Geometry> - static inline void apply(Box& total, Geometry const& geometry) + template <typename Box, typename Point> + static inline void apply(Box& total, Point const& point) { - geometry::expand(total, geometry::return_envelope<Box>(geometry)); + geometry::expand(total, point); } + }; + + template <typename EnvelopeStrategy> + struct expand_box_segment + { + explicit expand_box_segment(EnvelopeStrategy const& strategy) + : m_strategy(strategy) + {} + template <typename Box, typename Segment> + inline void apply(Box& total, Segment const& segment) const + { + geometry::expand(total, + geometry::return_envelope<Box>(segment, m_strategy)); + } + + EnvelopeStrategy const& m_strategy; }; - struct overlaps_box + struct overlaps_box_point { - template <typename Box, typename Geometry> - static inline bool apply(Box const& box, Geometry const& geometry) + template <typename Box, typename Point> + static inline bool apply(Box const& box, Point const& point) { - return ! geometry::disjoint(geometry, box); + return ! geometry::disjoint(point, box); } }; + template <typename DisjointStrategy> + struct overlaps_box_segment + { + explicit overlaps_box_segment(DisjointStrategy const& strategy) + : m_strategy(strategy) + {} + + template <typename Box, typename Segment> + inline bool apply(Box const& box, Segment const& segment) const + { + return ! geometry::disjoint(segment, box, m_strategy); + } + + DisjointStrategy const& m_strategy; + }; + template <typename OutputIterator, typename Strategy> class item_visitor_type { @@ -156,12 +190,14 @@ private: {} template <typename Item1, typename Item2> - inline void apply(Item1 const& item1, Item2 const& item2) + inline bool apply(Item1 const& item1, Item2 const& item2) { action_selector_pl_l < PointOut, overlay_intersection >::apply(item1, Policy::apply(item1, item2, m_strategy), m_oit); + + return true; } private: @@ -202,16 +238,25 @@ private: { item_visitor_type<OutputIterator, Strategy> item_visitor(oit, strategy); - segment_range rng(linear); + typedef typename Strategy::envelope_strategy_type envelope_strategy_type; + typedef typename Strategy::disjoint_strategy_type disjoint_strategy_type; + // TODO: disjoint Segment/Box may be called in partition multiple times + // possibly for non-cartesian segments which could be slow. We should consider + // passing a range of bounding boxes of segments after calculating them once. + // Alternatively instead of a range of segments a range of Segment/Envelope pairs + // should be passed, where envelope would be lazily calculated when needed the first time geometry::partition < geometry::model::box < typename boost::range_value<MultiPoint>::type > - >::apply(multipoint, rng, item_visitor, - expand_box(), overlaps_box()); + >::apply(multipoint, segment_range(linear), item_visitor, + expand_box_point(), + overlaps_box_point(), + expand_box_segment<envelope_strategy_type>(strategy.get_envelope_strategy()), + overlaps_box_segment<disjoint_strategy_type>(strategy.get_disjoint_strategy())); return oit; } diff --git a/boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp b/boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp new file mode 100644 index 0000000000..d4a47abecf --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp @@ -0,0 +1,178 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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_RANGE_IN_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RANGE_IN_GEOMETRY_HPP + + +#include <boost/geometry/algorithms/covered_by.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/iterators/point_iterator.hpp> + +#include <boost/range.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename Geometry, + typename Tag = typename geometry::tag<Geometry>::type +> +struct points_range +{ + typedef geometry::point_iterator<Geometry const> iterator_type; + + explicit points_range(Geometry const& geometry) + : m_geometry(geometry) + {} + + iterator_type begin() const + { + return geometry::points_begin(m_geometry); + } + + iterator_type end() const + { + return geometry::points_end(m_geometry); + } + + Geometry const& m_geometry; +}; +// Specialized because point_iterator doesn't support boxes +template <typename Box> +struct points_range<Box, box_tag> +{ + typedef typename geometry::point_type<Box>::type point_type; + typedef const point_type * iterator_type; + + explicit points_range(Box const& box) + { + detail::assign_box_corners(box, + m_corners[0], m_corners[1], m_corners[2], m_corners[3]); + } + + iterator_type begin() const + { + return m_corners; + } + + iterator_type end() const + { + return m_corners + 4; + } + + point_type m_corners[4]; +}; + +template +< + typename Geometry, + typename Tag = typename geometry::tag<Geometry>::type +> +struct point_in_geometry_helper +{ + template <typename Point, typename Strategy> + static inline int apply(Point const& point, Geometry const& geometry, + Strategy const& strategy) + { + return detail::within::point_in_geometry(point, geometry, strategy); + } +}; +// Specialized because point_in_geometry doesn't support Boxes +template <typename Box> +struct point_in_geometry_helper<Box, box_tag> +{ + template <typename Point, typename Strategy> + static inline int apply(Point const& point, Box const& box, + Strategy const&) + { + return geometry::covered_by(point, box) ? 1 : -1; + } +}; + +// This function returns +// when it finds a point of geometry1 inside or outside geometry2 +template <typename Geometry1, typename Geometry2, typename Strategy> +static inline int range_in_geometry(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy, + bool skip_first = false) +{ + int result = 0; + points_range<Geometry1> points(geometry1); + typedef typename points_range<Geometry1>::iterator_type iterator_type; + iterator_type const end = points.end(); + iterator_type it = points.begin(); + if (it == end) + { + return result; + } + else if (skip_first) + { + ++it; + } + + typename Strategy::template point_in_geometry_strategy + < + Geometry1, Geometry2 + >::type const in_strategy + = strategy.template get_point_in_geometry_strategy<Geometry1, Geometry2>(); + + for ( ; it != end; ++it) + { + result = point_in_geometry_helper<Geometry2>::apply(*it, geometry2, in_strategy); + if (result != 0) + { + return result; + } + } + // all points contained entirely by the boundary + return result; +} + +// This function returns if first_point1 is inside or outside geometry2 or +// when it finds a point of geometry1 inside or outside geometry2 +template <typename Point1, typename Geometry1, typename Geometry2, typename Strategy> +inline int range_in_geometry(Point1 const& first_point1, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + // check a point on border of geometry1 first + int result = point_in_geometry_helper<Geometry2>::apply(first_point1, geometry2, + strategy.template get_point_in_geometry_strategy<Point1, Geometry2>()); + if (result == 0) + { + // if a point is on boundary of geometry2 + // check points of geometry1 until point inside/outside is found + // NOTE: skip first point because it should be already tested above + result = range_in_geometry(geometry1, geometry2, strategy, true); + } + return result; +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RANGE_IN_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp index 0f2da67b62..7dbc5d5fab 100644 --- a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp +++ b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -2,6 +2,10 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -23,11 +27,11 @@ namespace boost { namespace geometry namespace detail { namespace overlay { -template <typename Point> +template <typename Point, typename AreaType> struct ring_properties { typedef Point point_type; - typedef typename default_area_result<Point>::type area_type; + typedef AreaType area_type; bool valid; @@ -52,17 +56,14 @@ struct ring_properties , parent_area(-1) {} - template <typename RingOrBox> - inline ring_properties(RingOrBox const& ring_or_box) + template <typename RingOrBox, typename AreaStrategy> + inline ring_properties(RingOrBox const& ring_or_box, AreaStrategy const& strategy) : reversed(false) , discarded(false) , parent_area(-1) { - this->area = geometry::area(ring_or_box); - // We should take a point somewhere in the middle of the ring, - // to avoid taking a point on a (self)tangency, - // in cases where multiple points come together - valid = geometry::point_on_border(this->point, ring_or_box, true); + this->area = geometry::area(ring_or_box, strategy); + valid = geometry::point_on_border(this->point, ring_or_box); } inline area_type get_area() const diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp index de5eac8acb..67a4f4bb75 100644 --- a/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -3,6 +3,10 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -18,9 +22,10 @@ #include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/area.hpp> -#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/covered_by.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/detail/ring_identifier.hpp> +#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp> #include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> @@ -35,11 +40,11 @@ namespace detail { namespace overlay struct ring_turn_info { - bool has_normal_turn; + bool has_traversed_turn; bool within_other; ring_turn_info() - : has_normal_turn(false) + : has_traversed_turn(false) , within_other(false) {} }; @@ -54,41 +59,45 @@ namespace dispatch template <typename Box> struct select_rings<box_tag, Box> { - template <typename Geometry, typename RingPropertyMap> + template <typename Geometry, typename RingPropertyMap, typename AreaStrategy> static inline void apply(Box const& box, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } - template <typename RingPropertyMap> + template <typename RingPropertyMap, typename AreaStrategy> static inline void apply(Box const& box, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } }; template <typename Ring> struct select_rings<ring_tag, Ring> { - template <typename Geometry, typename RingPropertyMap> + template <typename Geometry, typename RingPropertyMap, typename AreaStrategy> static inline void apply(Ring const& ring, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } - template <typename RingPropertyMap> + template <typename RingPropertyMap, typename AreaStrategy> static inline void apply(Ring const& ring, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } }; @@ -97,14 +106,15 @@ namespace dispatch template <typename Polygon> struct select_rings<polygon_tag, Polygon> { - template <typename Geometry, typename RingPropertyMap> + template <typename Geometry, typename RingPropertyMap, typename AreaStrategy> static inline void apply(Polygon const& polygon, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type<Polygon>::type ring_type; typedef select_rings<ring_tag, ring_type> per_ring; - per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties); + per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties, strategy); typename interior_return_type<Polygon const>::type rings = interior_rings(polygon); @@ -112,18 +122,19 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, geometry, id, ring_properties); + per_ring::apply(*it, geometry, id, ring_properties, strategy); } } - template <typename RingPropertyMap> + template <typename RingPropertyMap, typename AreaStrategy> static inline void apply(Polygon const& polygon, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type<Polygon>::type ring_type; typedef select_rings<ring_tag, ring_type> per_ring; - per_ring::apply(exterior_ring(polygon), id, ring_properties); + per_ring::apply(exterior_ring(polygon), id, ring_properties, strategy); typename interior_return_type<Polygon const>::type rings = interior_rings(polygon); @@ -131,7 +142,7 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, id, ring_properties); + per_ring::apply(*it, id, ring_properties, strategy); } } }; @@ -139,9 +150,10 @@ namespace dispatch template <typename Multi> struct select_rings<multi_polygon_tag, Multi> { - template <typename Geometry, typename RingPropertyMap> + template <typename Geometry, typename RingPropertyMap, typename AreaStrategy> static inline void apply(Multi const& multi, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename boost::range_iterator < @@ -154,7 +166,7 @@ namespace dispatch for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) { id.ring_index = -1; - per_polygon::apply(*it, geometry, id, ring_properties); + per_polygon::apply(*it, geometry, id, ring_properties, strategy); id.multi_index++; } } @@ -221,20 +233,21 @@ struct decide<overlay_intersection> } }; - template < overlay_type OverlayType, typename Geometry1, typename Geometry2, typename TurnInfoMap, - typename RingPropertyMap + typename RingPropertyMap, + typename Strategy > inline void update_ring_selection(Geometry1 const& geometry1, Geometry2 const& geometry2, TurnInfoMap const& turn_info_map, RingPropertyMap const& all_ring_properties, - RingPropertyMap& selected_ring_properties) + RingPropertyMap& selected_ring_properties, + Strategy const& strategy) { selected_ring_properties.clear(); @@ -252,9 +265,9 @@ inline void update_ring_selection(Geometry1 const& geometry1, info = tcit->second; // Copy by value } - if (info.has_normal_turn) + if (info.has_traversed_turn) { - // There are normal turns on this ring. It should be traversed, we + // This turn is traversed (or blocked), // don't include the original ring continue; } @@ -263,11 +276,16 @@ inline void update_ring_selection(Geometry1 const& geometry1, // a point lying on the ring switch(id.source_index) { + // within case 0 : - info.within_other = geometry::within(it->second.point, geometry2); + info.within_other = range_in_geometry(it->second.point, + geometry1, geometry2, + strategy) > 0; break; case 1 : - info.within_other = geometry::within(it->second.point, geometry1); + info.within_other = range_in_geometry(it->second.point, + geometry2, geometry1, + strategy) > 0; break; } @@ -290,23 +308,30 @@ template typename Geometry1, typename Geometry2, typename RingTurnInfoMap, - typename RingPropertyMap + typename RingPropertyMap, + typename Strategy > inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, RingTurnInfoMap const& turn_info_per_ring, - RingPropertyMap& selected_ring_properties) + RingPropertyMap& selected_ring_properties, + Strategy const& strategy) { typedef typename geometry::tag<Geometry1>::type tag1; typedef typename geometry::tag<Geometry2>::type tag2; + typedef typename geometry::point_type<Geometry1>::type point1_type; + typedef typename geometry::point_type<Geometry2>::type point2_type; RingPropertyMap all_ring_properties; dispatch::select_rings<tag1, Geometry1>::apply(geometry1, geometry2, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy<point1_type>()); dispatch::select_rings<tag2, Geometry2>::apply(geometry2, geometry1, - ring_identifier(1, -1, -1), all_ring_properties); + ring_identifier(1, -1, -1), all_ring_properties, + strategy.template get_area_strategy<point2_type>()); update_ring_selection<OverlayType>(geometry1, geometry2, turn_info_per_ring, - all_ring_properties, selected_ring_properties); + all_ring_properties, selected_ring_properties, + strategy); } template @@ -314,20 +339,25 @@ template overlay_type OverlayType, typename Geometry, typename RingTurnInfoMap, - typename RingPropertyMap + typename RingPropertyMap, + typename Strategy > inline void select_rings(Geometry const& geometry, RingTurnInfoMap const& turn_info_per_ring, - RingPropertyMap& selected_ring_properties) + RingPropertyMap& selected_ring_properties, + Strategy const& strategy) { typedef typename geometry::tag<Geometry>::type tag; + typedef typename geometry::point_type<Geometry>::type point_type; RingPropertyMap all_ring_properties; dispatch::select_rings<tag, Geometry>::apply(geometry, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy<point_type>()); update_ring_selection<OverlayType>(geometry, geometry, turn_info_per_ring, - all_ring_properties, selected_ring_properties); + all_ring_properties, selected_ring_properties, + strategy); } diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 8540ef98a0..5e9d8efa8e 100644 --- a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017 Oracle and/or its affiliates. @@ -22,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> @@ -57,12 +60,9 @@ struct no_interrupt_policy }; - - -class self_ip_exception : public geometry::exception {}; - template < + bool Reverse, typename Geometry, typename Turns, typename TurnPolicy, @@ -77,17 +77,20 @@ struct self_section_visitor RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; + std::size_t m_source_index; inline self_section_visitor(Geometry const& g, IntersectionStrategy const& is, RobustPolicy const& rp, Turns& turns, - InterruptPolicy& ip) + InterruptPolicy& ip, + std::size_t source_index) : m_geometry(g) , m_intersection_strategy(is) , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) + , m_source_index(source_index) {} template <typename Section> @@ -97,26 +100,21 @@ struct self_section_visitor && ! sec1.duplicate && ! sec2.duplicate) { - detail::get_turns::get_turns_in_sections + // false if interrupted + return detail::get_turns::get_turns_in_sections < Geometry, Geometry, - false, false, + Reverse, Reverse, Section, Section, TurnPolicy - >::apply( - 0, m_geometry, sec1, - 0, m_geometry, sec2, - false, - m_intersection_strategy, - m_rescale_policy, - m_turns, m_interrupt_policy); - } - if (BOOST_GEOMETRY_CONDITION(m_interrupt_policy.has_intersections)) - { - // TODO: we should give partition an interrupt policy. - // Now we throw, and catch below, to stop the partition loop. - throw self_ip_exception(); + >::apply(m_source_index, m_geometry, sec1, + m_source_index, m_geometry, sec2, + false, + m_intersection_strategy, + m_rescale_policy, + m_turns, m_interrupt_policy); } + return true; } @@ -124,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> @@ -133,7 +131,8 @@ struct get_turns IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Turns& turns, - InterruptPolicy& interrupt_policy) + InterruptPolicy& interrupt_policy, + std::size_t source_index) { typedef model::box < @@ -149,29 +148,24 @@ 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); + > visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index); - try - { - geometry::partition - < - box_type - >::apply(sec, visitor, - detail::section::get_section_box(), - detail::section::overlaps_section_box()); - } - catch(self_ip_exception const& ) - { - return false; - } + // false if interrupted + geometry::partition + < + box_type + >::apply(sec, visitor, + detail::section::get_section_box(), + detail::section::overlaps_section_box()); - return true; + return ! interrupt_policy.has_intersections; } }; @@ -186,6 +180,7 @@ namespace dispatch template < + bool Reverse, typename GeometryTag, typename Geometry, typename TurnPolicy @@ -197,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 > { @@ -226,7 +223,8 @@ struct self_get_turn_points Strategy const& , RobustPolicy const& , Turns& , - InterruptPolicy& ) + InterruptPolicy& , + std::size_t) { return true; } @@ -235,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> {}; @@ -265,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 @@ -290,18 +329,22 @@ template inline void self_turns(Geometry const& geometry, IntersectionStrategy const& strategy, RobustPolicy const& robust_policy, - Turns& turns, InterruptPolicy& interrupt_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; + 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); + reverse, + AssignPolicy + >(geometry, strategy, robust_policy, turns, interrupt_policy, source_index); } diff --git a/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index bbba623eee..5ad2e41b12 100644 --- a/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -10,11 +15,14 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP #include <algorithm> +#include <map> #include <vector> +#include <boost/geometry/algorithms/num_points.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> #include <boost/geometry/algorithms/detail/direction_code.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> -#include <boost/geometry/strategies/side.hpp> namespace boost { namespace geometry { @@ -37,7 +45,6 @@ struct ranked_point , count_left(0) , count_right(0) , operation(operation_none) - , only_turn_on_ring(false) {} template <typename Op> @@ -53,7 +60,6 @@ struct ranked_point , count_right(0) , operation(op.operation) , seg_id(op.seg_id) - , only_turn_on_ring(op.enriched.only_turn_on_ring) {} Point point; @@ -66,7 +72,6 @@ struct ranked_point std::size_t count_right; operation_type operation; segment_identifier seg_id; - bool only_turn_on_ring; }; struct less_by_turn_index @@ -105,17 +110,13 @@ struct less_false } }; -template <typename Point, typename LessOnSame, typename Compare> +template <typename Point, typename SideStrategy, typename LessOnSame, typename Compare> struct less_by_side { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Point>::type - >::type side; - - less_by_side(const Point& p1, const Point& p2) + less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy) : m_p1(p1) , m_p2(p2) + , m_strategy(strategy) {} template <typename T> @@ -124,8 +125,8 @@ struct less_by_side LessOnSame on_same; Compare compare; - int const side_first = side::apply(m_p1, m_p2, first.point); - int const side_second = side::apply(m_p1, m_p2, second.point); + int const side_first = m_strategy.apply(m_p1, m_p2, first.point); + int const side_second = m_strategy.apply(m_p1, m_p2, second.point); if (side_first == 0 && side_second == 0) { @@ -165,7 +166,7 @@ struct less_by_side // They are both left, both right, and/or both collinear (with each other and/or with p1,p2) // Check mutual side - int const side_second_wrt_first = side::apply(m_p2, first.point, second.point); + int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point); if (side_second_wrt_first == 0) { @@ -183,10 +184,19 @@ struct less_by_side private : Point m_p1, m_p2; + SideStrategy const& m_strategy; }; // Sorts vectors in counter clockwise order (by default) -template <bool Reverse1, bool Reverse2, typename Point, typename Compare> +template +< + bool Reverse1, + bool Reverse2, + overlay_type OverlayType, + typename Point, + typename SideStrategy, + typename Compare +> struct side_sorter { typedef ranked_point<Point> rp; @@ -215,13 +225,14 @@ private : }; public : - inline void set_origin(Point const& origin) - { - m_origin = origin; - } + side_sorter(SideStrategy const& strategy) + : m_origin_count(0) + , m_origin_segment_distance(0) + , m_strategy(strategy) + {} template <typename Operation, typename Geometry1, typename Geometry2> - void add(Operation const& op, signed_size_type turn_index, signed_size_type op_index, + Point add(Operation const& op, signed_size_type turn_index, signed_size_type op_index, Geometry1 const& geometry1, Geometry2 const& geometry2, bool is_origin) @@ -233,11 +244,63 @@ public : m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op)); m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op)); - if (is_origin) { m_origin = point1; + m_origin_count++; } + return point1; + } + + template <typename Operation, typename Geometry1, typename Geometry2> + void add(Operation const& op, signed_size_type turn_index, signed_size_type op_index, + segment_identifier const& departure_seg_id, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + bool check_origin) + { + Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false); + + if (check_origin) + { + bool const is_origin + = op.seg_id.source_index == departure_seg_id.source_index + && op.seg_id.ring_index == departure_seg_id.ring_index + && op.seg_id.multi_index == departure_seg_id.multi_index; + + if (is_origin) + { + int const segment_distance = calculate_segment_distance(op, departure_seg_id, geometry1, geometry2); + if (m_origin_count == 0 || + segment_distance < m_origin_segment_distance) + { + m_origin = point1; + m_origin_segment_distance = segment_distance; + } + m_origin_count++; + } + } + } + + template <typename Operation, typename Geometry1, typename Geometry2> + static int calculate_segment_distance(Operation const& op, + segment_identifier const& departure_seg_id, + Geometry1 const& geometry1, + Geometry2 const& geometry2) + { + if (op.seg_id.segment_index >= departure_seg_id.segment_index) + { + return op.seg_id.segment_index - departure_seg_id.segment_index; + } + // Take wrap into account + // Suppose ring_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2, then distance=10-9+2 + // Generic function (is this used somewhere else too?) + ring_identifier const rid(op.seg_id.source_index, op.seg_id.multi_index, op.seg_id.ring_index); + int const segment_count + (op.seg_id.source_index == 0 + ? geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry1>::type>::apply(rid, geometry1)) + : geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry2>::type>::apply(rid, geometry2))); + return ((segment_count - 1) - departure_seg_id.segment_index) + op.seg_id.segment_index; } void apply(Point const& turn_point) @@ -249,8 +312,8 @@ public : // to give colinear points // Sort by side and assign rank - less_by_side<Point, less_by_index, Compare> less_unique(m_origin, turn_point); - less_by_side<Point, less_false, Compare> less_non_unique(m_origin, turn_point); + less_by_side<Point, SideStrategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy); + less_by_side<Point, SideStrategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy); std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique); @@ -269,7 +332,7 @@ public : } template <signed_size_type segment_identifier::*Member, typename Map> - void find_open_generic(Map& handled) + void find_open_generic(Map& handled, bool check) { for (std::size_t i = 0; i < m_ranked_points.size(); i++) { @@ -280,6 +343,11 @@ public : } signed_size_type const& index = ranked.seg_id.*Member; + if (check && (index < 0 || index > 1)) + { + // Should not occur + continue; + } if (! handled[index]) { find_polygons_for_source<Member>(index, i); @@ -290,36 +358,23 @@ public : void find_open() { - // TODO: we might pass Buffer as overlay_type, instead on the fly below - bool one_source = true; - for (std::size_t i = 0; i < m_ranked_points.size(); i++) - { - const rp& ranked = m_ranked_points[i]; - signed_size_type const& src = ranked.seg_id.source_index; - if (src != 0) - { - one_source = false; - break; - } - } - - if (one_source) + if (OverlayType == overlay_buffer) { - // by multi index + // For buffers, use piece index std::map<signed_size_type, bool> handled; find_open_generic < &segment_identifier::piece_index - >(handled); + >(handled, false); } else { - // by source (there should only source 0,1) TODO assert this + // For other operations, by source (there should only source 0,1) bool handled[2] = {false, false}; find_open_generic < &segment_identifier::source_index - >(handled); + >(handled, true); } } @@ -361,11 +416,19 @@ public : } } + bool has_origin() const + { + return m_origin_count > 0; + } + //private : typedef std::vector<rp> container_type; container_type m_ranked_points; Point m_origin; + std::size_t m_origin_count; + int m_origin_segment_distance; + SideStrategy m_strategy; private : @@ -439,9 +502,10 @@ private : void find_polygons_for_source(signed_size_type the_index, std::size_t start_index) { - int state = 1; // 'closed', because start_index is "from", arrives at the turn - std::size_t last_from_rank = m_ranked_points[start_index].rank; - std::size_t previous_rank = m_ranked_points[start_index].rank; + bool in_polygon = true; // Because start_index is "from", arrives at the turn + rp const& start_rp = m_ranked_points[start_index]; + std::size_t last_from_rank = start_rp.rank; + std::size_t previous_rank = start_rp.rank; for (std::size_t index = move<Member>(the_index, start_index); ; @@ -449,7 +513,7 @@ private : { rp& ranked = m_ranked_points[index]; - if (ranked.rank != previous_rank && state == 0) + if (ranked.rank != previous_rank && ! in_polygon) { assign_ranks(last_from_rank, previous_rank - 1, 1); assign_ranks(last_from_rank + 1, previous_rank, 2); @@ -463,11 +527,11 @@ private : if (ranked.direction == dir_from) { last_from_rank = ranked.rank; - state++; + in_polygon = true; } else if (ranked.direction == dir_to) { - state--; + in_polygon = false; } previous_rank = ranked.rank; diff --git a/boost/geometry/algorithms/detail/overlay/traversal.hpp b/boost/geometry/algorithms/detail/overlay/traversal.hpp index bc828920e9..69d62b788b 100644 --- a/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -14,7 +19,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> @@ -37,9 +44,13 @@ namespace detail { namespace overlay template <typename Turn, typename Operation> #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE inline void debug_traverse(Turn const& turn, Operation op, - std::string const& header) + std::string const& header, bool condition = true) { - std::cout << header + if (! condition) + { + return; + } + std::cout << " " << header << " at " << op.seg_id << " meth: " << method_char(turn.method) << " op: " << operation_char(op.operation) @@ -55,7 +66,7 @@ inline void debug_traverse(Turn const& turn, Operation op, } } #else -inline void debug_traverse(Turn const& , Operation, const char*) +inline void debug_traverse(Turn const& , Operation, const char*, bool = true) { } #endif @@ -88,6 +99,7 @@ template typename Turns, typename Clusters, typename RobustPolicy, + typename SideStrategy, typename Visitor > struct traversal @@ -101,18 +113,20 @@ struct traversal typedef typename geometry::point_type<Geometry1>::type point_type; typedef sort_by_side::side_sorter < - Reverse1, Reverse2, - point_type, side_compare_type + Reverse1, Reverse2, OverlayType, + point_type, SideStrategy, side_compare_type > sbs_type; inline traversal(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, Clusters const& clusters, - RobustPolicy const& robust_policy, Visitor& visitor) + RobustPolicy const& robust_policy, SideStrategy const& strategy, + Visitor& visitor) : m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) , m_clusters(clusters) , m_robust_policy(robust_policy) + , m_strategy(strategy) , m_visitor(visitor) { } @@ -133,11 +147,38 @@ struct traversal } } + //! Sets visited for ALL turns traveling to the same turn + inline void set_visited_in_cluster(signed_size_type cluster_id, + signed_size_type rank) + { + typename Clusters::const_iterator mit = m_clusters.find(cluster_id); + BOOST_ASSERT(mit != m_clusters.end()); + + cluster_info const& cinfo = mit->second; + std::set<signed_size_type> const& ids = cinfo.turn_indices; + + for (typename std::set<signed_size_type>::const_iterator it = ids.begin(); + it != ids.end(); ++it) + { + signed_size_type const turn_index = *it; + turn_type& turn = m_turns[turn_index]; + + for (int i = 0; i < 2; i++) + { + turn_operation_type& op = turn.operations[i]; + if (op.visited.none() + && op.enriched.rank == rank) + { + op.visited.set_visited(); + } + } + } + } inline void set_visited(turn_type& turn, turn_operation_type& op) { - // On "continue", set "visited" for ALL directions in this turn if (op.operation == detail::overlay::operation_continue) { + // On "continue", all go in same direction so set "visited" for ALL for (int i = 0; i < 2; i++) { turn_operation_type& turn_op = turn.operations[i]; @@ -151,6 +192,10 @@ struct traversal { op.visited.set_visited(); } + if (turn.cluster_id >= 0) + { + set_visited_in_cluster(turn.cluster_id, op.enriched.rank); + } } inline bool is_visited(turn_type const& , turn_operation_type const& op, @@ -160,8 +205,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]; @@ -170,8 +215,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 (is_self_turn<OverlayType>(turn)) + { + // 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) @@ -185,16 +238,8 @@ struct traversal } #endif return turn.switch_source - ? seg_id1.source_index != seg_id2.source_index - : seg_id1.source_index == seg_id2.source_index; - } - - inline - signed_size_type get_next_turn_index(turn_operation_type const& op) const - { - return op.enriched.next_ip_index == -1 - ? op.enriched.travels_to_ip_index - : op.enriched.next_ip_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 @@ -220,39 +265,39 @@ struct traversal { // For "cc", take either one, but if there is a starting one, // take that one. If next is dead end, skip that one. + // If both are valid candidates, take the one with minimal remaining + // distance (important for #mysql_23023665 in buffer). - bool result = false; - + // Initialize with 0, automatically assigned on first result typename turn_operation_type::comparable_distance_type - max_remaining_distance = 0; + min_remaining_distance = 0; + + bool result = false; for (int i = 0; i < 2; i++) { turn_operation_type const& op = turn.operations[i]; - signed_size_type const next_turn_index = get_next_turn_index(op); + signed_size_type const next_turn_index = op.enriched.get_next_turn_index(); - if (! result && traverse_possible(next_turn_index)) + if (! traverse_possible(next_turn_index)) { - max_remaining_distance = op.remaining_distance; - selected_op_index = i; - debug_traverse(turn, op, " Candidate"); - result = true; + continue; } - if (result) + if (! result + || next_turn_index == start_turn_index + || op.remaining_distance < min_remaining_distance) { - if (next_turn_index == start_turn_index) - { - selected_op_index = i; - debug_traverse(turn, op, " Candidate cc override (start)"); - } - else if (op.remaining_distance > max_remaining_distance) - { - max_remaining_distance = op.remaining_distance; - selected_op_index = i; - debug_traverse(turn, op, " Candidate cc override (remaining)"); - } + debug_traverse(turn, op, "First candidate cc", ! result); + debug_traverse(turn, op, "Candidate cc override (start)", + result && next_turn_index == start_turn_index); + debug_traverse(turn, op, "Candidate cc override (remaining)", + result && op.remaining_distance < min_remaining_distance); + + selected_op_index = i; + min_remaining_distance = op.remaining_distance; + result = true; } } @@ -262,7 +307,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; @@ -273,10 +318,10 @@ 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"); + debug_traverse(turn, op, "Candidate"); result = true; } } @@ -305,7 +350,7 @@ struct traversal } if (result) { - debug_traverse(turn, turn.operations[selected_op_index], " Accepted"); + debug_traverse(turn, turn.operations[selected_op_index], "Accepted"); } return result; @@ -336,108 +381,164 @@ 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& turn = m_turns[turn_index]; + turn_operation_type const& op = turn.operations[op_index]; + + if (op.enriched.count_left == 0 + && op.enriched.count_right > 0 + && ! op.visited.finalized()) + { + // In some cases interior rings might be generated with polygons + // on both sides + + // TODO: this should be finetuned such that checking + // finalized is not necessary + return true; + } } + } + return false; + } - turn_type const& ranked_turn = m_turns[ranked_point.turn_index]; - turn_operation_type const& ranked_op = ranked_turn.operations[ranked_point.operation_index]; - if (result && ranked_op.visited.finalized()) + 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 + { + typedef std::set<sort_by_side::ring_with_direction>::const_iterator sit_type; + for (sit_type it = rwr.rings.begin(); it != rwr.rings.end(); ++it) + { + sort_by_side::ring_with_direction const& rwd = *it; + if (rwd.direction != dir) { - // One of the arcs in the same direction as the selected result - // is already traversed. return false; } - - if (! is_touching && ranked_op.visited.finalized()) + turn_type const& turn = m_turns[rwd.turn_index]; + if (! turn.both(op_type)) { - // Skip this one, go to next - min_rank = ranked_point.rank; - continue; + return false; } - if (ranked_point.direction == sort_by_side::dir_to - && (ranked_point.rank > min_rank - || ranked_turn.both(operation_continue))) + // Check if this is not yet taken + turn_operation_type const& op = turn.operations[rwd.operation_index]; + if (op.visited.finalized()) { - if (ranked_op.enriched.count_left == 0 - && ranked_op.enriched.count_right > 0) - { - if (result && ranked_point.turn_index != start_turn_index) - { - // Don't override - only override if arrive at start - continue; - } - - 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; - } + return false; } + } - return result; + return true; } inline bool analyze_cluster_intersection(signed_size_type& turn_index, - int& op_index, - sbs_type const& sbs) const + int& op_index, sbs_type const& sbs) const { std::vector<sort_by_side::rank_with_rings> aggregation; - sort_by_side::aggregate_operations(sbs, aggregation); + sort_by_side::aggregate_operations(sbs, aggregation, m_turns, operation_intersection); std::size_t selected_rank = 0; - 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 (i > 1 - && i - 1 == selected_rank - && rwr.rings.size() == 1) + for (std::size_t i = 0; i < aggregation.size(); i++) { - sort_by_side::ring_with_direction const& rwd = *rwr.rings.begin(); + sort_by_side::rank_with_rings const& rwr = aggregation[i]; - if (rwd.only_turn_on_ring) + if (rwr.all_to() + && rwr.traversable(m_turns) + && selected_rank == 0) { - // Find if this arriving ring was leaving previously - sort_by_side::ring_with_direction leaving = rwd; - leaving.direction = sort_by_side::dir_to; - - sort_by_side::rank_with_rings const& previous = aggregation[i - 1]; + // Take the first (= right) where segments leave, + // having the polygon on the right side + selected_rank = rwr.rank; + } - if (previous.rings.size() == 1 - && previous.rings.count(leaving) == 1) - { - // It arrives back - if this is one of the selected, unselect it - selected_rank = 0; - } + if (rwr.all_from() + && selected_rank > 0 + && outgoing_region_ids.empty()) + { + // Incoming + break; } - } - if (rwr.all_to()) - { - if (selected_rank == 0) + if (incoming_region_id == 0) { - // Take the first (= right) where segments leave, - // having the polygon on the right side - selected_rank = rwr.rank; + 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) + { + 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) + { + selected_rank = 0; + outgoing_region_ids.erase(region_id); + } + } + } + } } } } @@ -458,7 +559,7 @@ struct traversal { // This direction is already traveled before, the same // cannot be traveled again - return false; + continue; } // Take the last turn from this rank @@ -479,10 +580,9 @@ 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, - bool is_start) const + segment_identifier const& previous_seg_id) const { bool const is_union = target_operation == operation_union; @@ -495,15 +595,14 @@ struct traversal cluster_info const& cinfo = mit->second; std::set<signed_size_type> const& ids = cinfo.turn_indices; - sbs_type sbs; - - bool has_origin = false; + sbs_type sbs(m_strategy); for (typename std::set<signed_size_type>::const_iterator sit = ids.begin(); sit != ids.end(); ++sit) { signed_size_type cluster_turn_index = *sit; turn_type const& cluster_turn = m_turns[cluster_turn_index]; + bool const departure_turn = cluster_turn_index == turn_index; if (cluster_turn.discarded) { // Defensive check, discarded turns should not be in cluster @@ -512,79 +611,27 @@ struct traversal for (int i = 0; i < 2; i++) { - turn_operation_type const& op = cluster_turn.operations[i]; - bool is_origin = false; - if (cluster_turn_index == turn_index) - { - // Check if this is the origin - if (OverlayType == overlay_buffer) - { - is_origin = op.seg_id.multi_index == previous_seg_id.multi_index; - } - else - { - is_origin = op.seg_id.source_index - == previous_seg_id.source_index; - } - if (is_origin) - { - has_origin = true; - } - } - - sbs.add(op, cluster_turn_index, i, m_geometry1, m_geometry2, - is_origin); + sbs.add(cluster_turn.operations[i], + cluster_turn_index, i, previous_seg_id, + m_geometry1, m_geometry2, + departure_turn); } } - if (! has_origin) + if (! sbs.has_origin()) { return false; } - sbs.apply(turn.point); bool result = false; 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 { - if (is_start - && turn.both(operation_intersection) - && turn.operations[op_index].enriched.only_turn_on_ring) - { - // For an ii (usually interior ring), only turn on ring, - // reverse to take first exit - sbs.reverse(); - } - result = analyze_cluster_intersection(turn_index, op_index, sbs); } return result; @@ -594,26 +641,27 @@ struct traversal turn_type const& current_turn, segment_identifier const& previous_seg_id) { - sbs_type sbs; + sbs_type sbs(m_strategy); // Add this turn to the sort-by-side sorter - bool has_origin = false; for (int i = 0; i < 2; i++) { - turn_operation_type const& op = current_turn.operations[i]; - bool const is_origin = op.seg_id.source_index - == previous_seg_id.source_index; - has_origin = has_origin || is_origin; - sbs.add(op, turn_index, i, m_geometry1, m_geometry2, is_origin); + sbs.add(current_turn.operations[i], + turn_index, i, previous_seg_id, + m_geometry1, m_geometry2, + true); } - if (! has_origin) + if (! sbs.has_origin()) { return false; } sbs.apply(current_turn.point); - return analyze_cluster_intersection(turn_index, op_index, sbs); + + bool result = analyze_cluster_intersection(turn_index, op_index, sbs); + + return result; } inline void change_index_for_self_turn(signed_size_type& to_vertex_index, @@ -712,7 +760,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, @@ -737,8 +784,8 @@ struct traversal if (current_turn.cluster_id < 0 && current_turn.both(operation_intersection)) { - if (analyze_ii_intersection(turn_index, op_index, current_turn, - previous_seg_id)) + if (analyze_ii_intersection(turn_index, op_index, + current_turn, previous_seg_id)) { return true; } @@ -747,8 +794,8 @@ struct traversal if (current_turn.cluster_id >= 0) { - if (! select_turn_from_cluster(turn_index, op_index, is_touching, - start_turn_index, previous_seg_id, is_start)) + if (! select_turn_from_cluster(turn_index, op_index, + start_turn_index, previous_seg_id)) { return false; } @@ -786,6 +833,7 @@ private : Turns& m_turns; Clusters const& m_clusters; RobustPolicy const& m_robust_policy; + SideStrategy m_strategy; Visitor& m_visitor; }; diff --git a/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp new file mode 100644 index 0000000000..12279d762f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -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 diff --git a/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 9ab82a77c1..af643a822b 100644 --- a/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -49,9 +49,13 @@ template > struct traversal_ring_creator { - typedef traversal<Reverse1, Reverse2, OverlayType, - Geometry1, Geometry2, Turns, Clusters, RobustPolicy, Visitor> - traversal_type; + typedef traversal + < + Reverse1, Reverse2, OverlayType, + Geometry1, Geometry2, Turns, Clusters, + RobustPolicy, typename IntersectionStrategy::side_strategy_type, + Visitor + > traversal_type; typedef typename boost::range_value<Turns>::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; @@ -63,7 +67,9 @@ struct traversal_ring_creator Turns& turns, Clusters const& clusters, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Visitor& visitor) - : m_trav(geometry1, geometry2, turns, clusters, robust_policy,visitor) + : m_trav(geometry1, geometry2, turns, clusters, + robust_policy, intersection_strategy.get_side_strategy(), + visitor) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) @@ -103,12 +109,14 @@ struct traversal_ring_creator { geometry::copy_segments<Reverse1>(m_geometry1, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } else { geometry::copy_segments<Reverse2>(m_geometry2, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } } @@ -127,10 +135,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)) { @@ -154,6 +160,7 @@ struct traversal_ring_creator turn_type& current_turn = m_turns[turn_index]; turn_operation_type& op = current_turn.operations[op_index]; detail::overlay::append_no_dups_or_spikes(current_ring, current_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); // Register the visit @@ -171,6 +178,7 @@ struct traversal_ring_creator turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index]; detail::overlay::append_no_dups_or_spikes(ring, start_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); signed_size_type current_turn_index = start_turn_index; @@ -273,7 +281,9 @@ struct traversal_ring_creator if (geometry::num_points(ring) >= min_num_points) { - clean_closing_dups_and_spikes(ring, m_robust_policy); + clean_closing_dups_and_spikes(ring, + m_intersection_strategy.get_side_strategy(), + m_robust_policy); rings.push_back(ring); m_trav.finalize_visit_info(); @@ -307,11 +317,27 @@ struct traversal_ring_creator continue; } - for (int op_index = 0; op_index < 2; op_index++) + if (turn.both(operation_continue)) { + // Traverse only one turn, the one with the SMALLEST remaining distance + // to avoid skipping a turn in between, which can happen in rare cases + // (e.g. #130) + turn_operation_type const& op0 = turn.operations[0]; + turn_operation_type const& op1 = turn.operations[1]; + int const op_index + = op0.remaining_distance <= op1.remaining_distance ? 0 : 1; + traverse_with_operation(turn, turn_index, op_index, rings, finalized_ring_size, state); } + else + { + for (int op_index = 0; op_index < 2; op_index++) + { + traverse_with_operation(turn, turn_index, op_index, + rings, finalized_ring_size, state); + } + } } } diff --git a/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 183131c74b..0b4f393ef4 100644 --- a/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -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> @@ -47,10 +48,54 @@ template > struct traversal_switch_detector { + enum isolation_type { isolation_unknown = -1, isolation_no = 0, isolation_yes = 1 }; + 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; + std::set<signed_size_type> turn_indices; + + merged_ring_properties() + : region_id(-1) + {} + }; + + 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; + + // Maps from connected region_id to their properties + connection_map connected_region_counts; + + region_properties() + : region_id(-1) + , isolated(isolation_unknown) + {} + }; + + // 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, @@ -62,135 +107,309 @@ struct traversal_switch_detector , m_clusters(clusters) , m_robust_policy(robust_policy) , m_visitor(visitor) - , m_region_id(0) { + } + + isolation_type get_isolation(region_properties const& properties, + signed_size_type parent_region_id, + const std::set<signed_size_type>& visited) + { + if (properties.isolated != isolation_unknown) + { + 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, or if there are more connections + // but they are all colocated + std::size_t non_isolation_count = 0; + bool child_not_isolated = false; + 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; + connection_properties const& cprop = it->second; + + if (region_id == parent_region_id) + { + // Normal situation, skip its direct parent + continue; + } + if (visited.count(region_id) > 0) + { + // Find one of its ancestors again, this is a ring. Not isolated. + return isolation_no; + } + if (cprop.count > 1) + { + return isolation_no; + } + + typename region_connection_map::iterator mit = m_connected_regions.find(region_id); + if (mit == m_connected_regions.end()) + { + // Should not occur + continue; + } + + std::set<signed_size_type> vis = visited; + vis.insert(parent_region_id); + region_properties& prop = mit->second; + if (prop.isolated == isolation_unknown) + { + isolation_type const iso = get_isolation(prop, properties.region_id, vis); + prop.isolated = iso; + if (iso == isolation_no) + { + child_not_isolated = true; + } + } + if (prop.isolated == isolation_no) + { + non_isolation_count++; + } + } + + return child_not_isolated || non_isolation_count > 1 ? isolation_no : isolation_yes; } - static inline bool connects_same_zone(turn_type const& turn) + void get_isolated_regions() { + for (typename region_connection_map::iterator it = m_connected_regions.begin(); + it != m_connected_regions.end(); ++it) + { + region_properties& properties = it->second; + if (properties.isolated == isolation_unknown) + { + std::set<signed_size_type> visited; + properties.isolated = get_isolation(properties, properties.region_id, visited); + } + } + } + + void assign_isolation() + { + for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index) + { + turn_type& turn = m_turns[turn_index]; + + for (int op_index = 0; op_index < 2; op_index++) + { + turn_operation_type& op = turn.operations[op_index]; + typename region_connection_map::const_iterator mit = m_connected_regions.find(op.enriched.region_id); + if (mit != m_connected_regions.end()) + { + region_properties const& prop = mit->second; + op.enriched.isolated = prop.isolated == isolation_yes; + } + } + } + } + + void assign_regions() + { + for (typename merge_map::const_iterator it + = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) + { + ring_identifier const& ring_id = it->first; + merged_ring_properties const& properties = it->second; + + for (set_iterator sit = properties.turn_indices.begin(); + sit != properties.turn_indices.end(); ++sit) + { + turn_type& turn = m_turns[*sit]; + + for (int i = 0; i < 2; i++) + { + turn_operation_type& op = turn.operations[i]; + if (ring_id_by_seg_id(op.seg_id) == ring_id) + { + op.enriched.region_id = properties.region_id; + } + } + signed_size_type const& id0 = turn.operations[0].enriched.region_id; + signed_size_type const& id1 = turn.operations[1].enriched.region_id; + if (id0 != id1 && id0 != -1 && id1 != -1) + { + // Force insertion + m_connected_regions[id0].region_id = id0; + m_connected_regions[id1].region_id = id1; + + 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); + } + } + } + } + + 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 zone + // If it is a uu/ii-turn (non clustered), it is never same region return ! (turn.both(operation_union) || turn.both(operation_intersection)); } - // It is a cluster, check zones of both operations - return turn.operations[0].enriched.zone - == turn.operations[1].enriched.zone; + if (operation_from_overlay<OverlayType>::value == operation_union) + { + // It is a cluster, check zones + // (assigned by sort_by_side/handle colocations) of both operations + return turn.operations[0].enriched.zone + == turn.operations[1].enriched.zone; + } + + // If a cluster contains an ii/cc it is not same region (for intersection) + typename Clusters::const_iterator it = m_clusters.find(turn.cluster_id); + if (it == m_clusters.end()) + { + // Should not occur + return true; + } + + cluster_info const& cinfo = it->second; + for (set_iterator sit = cinfo.turn_indices.begin(); + sit != cinfo.turn_indices.end(); ++sit) + { + turn_type const& cluster_turn = m_turns[*sit]; + if (cluster_turn.both(operation_union) + || cluster_turn.both(operation_intersection)) + { + return false; + } + } + + // It is the same region + return false; } + inline int get_region_id(turn_operation_type const& op) const { - std::map<ring_identifier, int>::const_iterator it - = m_regions.find(ring_id_by_seg_id(op.seg_id)); - return it == m_regions.end() ? -1 : it->second; + return op.enriched.region_id; } - void create_region(ring_identifier const& ring_id, std::set<signed_size_type> const& ring_turn_indices, int region_id = -1) + + void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id, + merged_ring_properties& properties, int region_id = -1) { - std::map<ring_identifier, int>::const_iterator it = m_regions.find(ring_id); - if (it != m_regions.end()) + if (properties.region_id > 0) { - // The ring is already gathered in a region, quit + // Already handled return; } + + // Assign new id if this is a new region if (region_id == -1) { - region_id = m_region_id++; + region_id = new_region_id++; } // Assign this ring to specified region - m_regions[ring_id] = region_id; + properties.region_id = region_id; + #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) std::cout << " ADD " << ring_id << " TO REGION " << region_id << std::endl; #endif // Find connecting rings, recursively - for (set_iterator sit = ring_turn_indices.begin(); - sit != ring_turn_indices.end(); ++sit) + for (set_iterator sit = properties.turn_indices.begin(); + sit != properties.turn_indices.end(); ++sit) { signed_size_type const turn_index = *sit; turn_type const& turn = m_turns[turn_index]; - if (! connects_same_zone(turn)) + if (! connects_same_region(turn)) { // This is a non clustered uu/ii-turn, or a cluster connecting different 'zones' continue; } - // This turn connects two rings (interior connected), create the - // same region + // Union: This turn connects two rings (interior connected), create the region + // Intersection: This turn connects two rings, set same regions for these two rings for (int op_index = 0; op_index < 2; op_index++) { turn_operation_type const& op = turn.operations[op_index]; ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id); if (connected_ring_id != ring_id) { - propagate_region(connected_ring_id, region_id); + propagate_region(new_region_id, connected_ring_id, region_id); } } } } - void check_turns_per_ring(ring_identifier const& ring_id, - std::set<signed_size_type> const& ring_turn_indices) + void propagate_region(signed_size_type& new_region_id, + ring_identifier const& ring_id, int region_id) { - bool only_turn_on_ring = true; - if (ring_turn_indices.size() > 1) - { - // More turns on this ring. Only leave only_turn_on_ring true - // if they are all of the same cluster - int cluster_id = -1; - for (set_iterator sit = ring_turn_indices.begin(); - sit != ring_turn_indices.end(); ++sit) - { - turn_type const& turn = m_turns[*sit]; - if (turn.cluster_id == -1) - { - // Unclustered turn - and there are 2 or more turns - // so the ring has different turns - only_turn_on_ring = false; - break; - } - - // Clustered turn, check if it is the first or same as previous - if (cluster_id == -1) - { - cluster_id = turn.cluster_id; - } - else if (turn.cluster_id != cluster_id) - { - only_turn_on_ring = false; - break; - } - } - } - - // Assign result to matching operation (a turn is always on two rings) - for (set_iterator sit = ring_turn_indices.begin(); - sit != ring_turn_indices.end(); ++sit) - { - turn_type& turn = m_turns[*sit]; - for (int i = 0; i < 2; i++) - { - turn_operation_type& op = turn.operations[i]; - if (ring_id_by_seg_id(op.seg_id) == ring_id) - { - op.enriched.only_turn_on_ring = only_turn_on_ring; - } - } - } - } - - void propagate_region(ring_identifier const& ring_id, int region_id) - { - std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it = m_turns_per_ring.find(ring_id); + typename merge_map::iterator it = m_turns_per_ring.find(ring_id); if (it != m_turns_per_ring.end()) { - create_region(ring_id, it->second, region_id); + create_region(new_region_id, ring_id, it->second, region_id); } } + void iterate() { #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) @@ -199,26 +418,38 @@ struct traversal_switch_detector // Collect turns per ring m_turns_per_ring.clear(); - m_regions.clear(); - m_region_id = 1; + m_connected_regions.clear(); for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index) { 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]; - m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].insert(turn_index); + m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].turn_indices.insert(turn_index); } } // All rings having turns are in the map. Now iterate them - for (std::map<ring_identifier, std::set<signed_size_type> >::const_iterator it - = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) { - create_region(it->first, it->second); - check_turns_per_ring(it->first, it->second); + signed_size_type new_region_id = 1; + for (typename merge_map::iterator it + = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) + { + create_region(new_region_id, it->first, it->second); + } + + assign_regions(); + get_isolated_regions(); + assign_isolation(); } // Now that all regions are filled, assign switch_source property @@ -245,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]); @@ -252,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) @@ -326,13 +561,10 @@ private: Geometry2 const& m_geometry2; Turns& m_turns; Clusters& m_clusters; + merge_map m_turns_per_ring; + region_connection_map m_connected_regions; RobustPolicy const& m_robust_policy; Visitor& m_visitor; - - std::map<ring_identifier, int> m_regions; - std::map<ring_identifier, std::set<signed_size_type> > m_turns_per_ring; - int m_region_id; - }; }} // namespace detail::overlay diff --git a/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/boost/geometry/algorithms/detail/overlay/turn_info.hpp index e09af126c6..3a4c2e94a1 100644 --- a/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -89,18 +89,24 @@ struct turn_info Point point; method_type method; + bool touch_only; // True in case of method touch(interior) and lines do not cross signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 bool discarded; - bool colocated; + + // 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; inline turn_info() : method(method_none) + , touch_only(false) , cluster_id(-1) , discarded(false) - , colocated(false) + , colocated_ii(false) + , colocated_uu(false) , switch_source(false) {} @@ -133,7 +139,6 @@ struct turn_info return has(operation_blocked); } - private : inline bool has12(operation_type type1, operation_type type2) const { diff --git a/boost/geometry/algorithms/detail/partition.hpp b/boost/geometry/algorithms/detail/partition.hpp index 12c6a54661..db134d548d 100644 --- a/boost/geometry/algorithms/detail/partition.hpp +++ b/boost/geometry/algorithms/detail/partition.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2011-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2015, 2017. // Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. @@ -106,11 +107,11 @@ inline void expand_with_elements(Box& total, IteratorVector const& input, // Match forward_range with itself template <typename IteratorVector, typename VisitPolicy> -inline void handle_one(IteratorVector const& input, VisitPolicy& visitor) +inline bool handle_one(IteratorVector const& input, VisitPolicy& visitor) { - if (boost::size(input) == 0) + if (boost::empty(input)) { - return; + return true; } typedef typename boost::range_iterator<IteratorVector const>::type it_type; @@ -121,9 +122,14 @@ inline void handle_one(IteratorVector const& input, VisitPolicy& visitor) it_type it2 = it1; for (++it2; it2 != boost::end(input); ++it2) { - visitor.apply(**it1, **it2); + if (! visitor.apply(**it1, **it2)) + { + return false; // interrupt + } } } + + return true; } // Match forward range 1 with forward range 2 @@ -133,7 +139,7 @@ template typename IteratorVector2, typename VisitPolicy > -inline void handle_two(IteratorVector1 const& input1, +inline bool handle_two(IteratorVector1 const& input1, IteratorVector2 const& input2, VisitPolicy& visitor) { @@ -147,9 +153,9 @@ inline void handle_two(IteratorVector1 const& input1, IteratorVector2 const >::type iterator_type2; - if (boost::size(input1) == 0 || boost::size(input2) == 0) + if (boost::empty(input1) || boost::empty(input2)) { - return; + return true; } for(iterator_type1 it1 = boost::begin(input1); @@ -160,9 +166,14 @@ inline void handle_two(IteratorVector1 const& input1, it2 != boost::end(input2); ++it2) { - visitor.apply(**it1, **it2); + if (! visitor.apply(**it1, **it2)) + { + return false; // interrupt + } } } + + return true; } template <typename IteratorVector> @@ -223,7 +234,7 @@ class partition_one_range typename OverlapsPolicy, typename VisitBoxPolicy > - static inline void next_level(Box const& box, + static inline bool next_level(Box const& box, IteratorVector const& input, std::size_t level, std::size_t min_elements, VisitPolicy& visitor, @@ -233,7 +244,7 @@ class partition_one_range { if (recurse_ok(input, min_elements, level)) { - partition_one_range + return partition_one_range < 1 - Dimension, Box @@ -242,7 +253,7 @@ class partition_one_range } else { - handle_one(input, visitor); + return handle_one(input, visitor); } } @@ -256,18 +267,18 @@ class partition_one_range typename OverlapsPolicy, typename VisitBoxPolicy > - static inline void next_level2(Box const& box, - IteratorVector const& input1, - IteratorVector const& input2, - std::size_t level, std::size_t min_elements, - VisitPolicy& visitor, - ExpandPolicy const& expand_policy, - OverlapsPolicy const& overlaps_policy, - VisitBoxPolicy& box_policy) + static inline bool next_level2(Box const& box, + IteratorVector const& input1, + IteratorVector const& input2, + std::size_t level, std::size_t min_elements, + VisitPolicy& visitor, + ExpandPolicy const& expand_policy, + OverlapsPolicy const& overlaps_policy, + VisitBoxPolicy& box_policy) { if (recurse_ok(input1, input2, min_elements, level)) { - partition_two_ranges + return partition_two_ranges < 1 - Dimension, Box >::apply(box, input1, input2, level + 1, min_elements, @@ -276,7 +287,7 @@ class partition_one_range } else { - handle_two(input1, input2, visitor); + return handle_two(input1, input2, visitor); } } @@ -289,7 +300,7 @@ public : typename OverlapsPolicy, typename VisitBoxPolicy > - static inline void apply(Box const& box, + static inline bool apply(Box const& box, IteratorVector const& input, std::size_t level, std::size_t min_elements, @@ -308,29 +319,31 @@ public : input, lower, upper, exceeding, overlaps_policy); - if (boost::size(exceeding) > 0) + if (! boost::empty(exceeding)) { // Get the box of exceeding-only Box exceeding_box = get_new_box(exceeding, expand_policy); - // Recursively do exceeding elements only, in next dimension they - // will probably be less exceeding within the new box - next_level(exceeding_box, exceeding, level, min_elements, - visitor, expand_policy, overlaps_policy, box_policy); - - // Switch to two forward ranges, combine exceeding with - // lower resp upper, but not lower/lower, upper/upper - next_level2(exceeding_box, exceeding, lower, level, min_elements, - visitor, expand_policy, overlaps_policy, box_policy); - next_level2(exceeding_box, exceeding, upper, level, min_elements, - visitor, expand_policy, overlaps_policy, box_policy); + // Recursively do exceeding elements only, in next dimension they + // will probably be less exceeding within the new box + if (! (next_level(exceeding_box, exceeding, level, min_elements, + visitor, expand_policy, overlaps_policy, box_policy) + // Switch to two forward ranges, combine exceeding with + // lower resp upper, but not lower/lower, upper/upper + && next_level2(exceeding_box, exceeding, lower, level, min_elements, + visitor, expand_policy, overlaps_policy, box_policy) + && next_level2(exceeding_box, exceeding, upper, level, min_elements, + visitor, expand_policy, overlaps_policy, box_policy)) ) + { + return false; // interrupt + } } // Recursively call operation both parts - next_level(lower_box, lower, level, min_elements, - visitor, expand_policy, overlaps_policy, box_policy); - next_level(upper_box, upper, level, min_elements, - visitor, expand_policy, overlaps_policy, box_policy); + return next_level(lower_box, lower, level, min_elements, + visitor, expand_policy, overlaps_policy, box_policy) + && next_level(upper_box, upper, level, min_elements, + visitor, expand_policy, overlaps_policy, box_policy); } }; @@ -352,23 +365,23 @@ class partition_two_ranges typename OverlapsPolicy2, typename VisitBoxPolicy > - static inline void next_level(Box const& box, - IteratorVector1 const& input1, - IteratorVector2 const& input2, - std::size_t level, std::size_t min_elements, - VisitPolicy& visitor, - ExpandPolicy1 const& expand_policy1, - OverlapsPolicy1 const& overlaps_policy1, - ExpandPolicy2 const& expand_policy2, - OverlapsPolicy2 const& overlaps_policy2, - VisitBoxPolicy& box_policy) + static inline bool next_level(Box const& box, + IteratorVector1 const& input1, + IteratorVector2 const& input2, + std::size_t level, std::size_t min_elements, + VisitPolicy& visitor, + ExpandPolicy1 const& expand_policy1, + OverlapsPolicy1 const& overlaps_policy1, + ExpandPolicy2 const& expand_policy2, + OverlapsPolicy2 const& overlaps_policy2, + VisitBoxPolicy& box_policy) { - partition_two_ranges - < - 1 - Dimension, Box - >::apply(box, input1, input2, level + 1, min_elements, - visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + return partition_two_ranges + < + 1 - Dimension, Box + >::apply(box, input1, input2, level + 1, min_elements, + visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy); } template <typename IteratorVector, typename ExpandPolicy> @@ -408,17 +421,17 @@ public : typename OverlapsPolicy2, typename VisitBoxPolicy > - static inline void apply(Box const& box, - IteratorVector1 const& input1, - IteratorVector2 const& input2, - std::size_t level, - std::size_t min_elements, - VisitPolicy& visitor, - ExpandPolicy1 const& expand_policy1, - OverlapsPolicy1 const& overlaps_policy1, - ExpandPolicy2 const& expand_policy2, - OverlapsPolicy2 const& overlaps_policy2, - VisitBoxPolicy& box_policy) + static inline bool apply(Box const& box, + IteratorVector1 const& input1, + IteratorVector2 const& input2, + std::size_t level, + std::size_t min_elements, + VisitPolicy& visitor, + ExpandPolicy1 const& expand_policy1, + OverlapsPolicy1 const& overlaps_policy1, + ExpandPolicy2 const& expand_policy2, + OverlapsPolicy2 const& overlaps_policy2, + VisitBoxPolicy& box_policy) { box_policy.apply(box, level); @@ -434,7 +447,7 @@ public : input2, lower2, upper2, exceeding2, overlaps_policy2); - if (boost::size(exceeding1) > 0) + if (! boost::empty(exceeding1)) { // All exceeding from 1 with 2: @@ -442,13 +455,19 @@ public : { Box exceeding_box = get_new_box(exceeding1, exceeding2, expand_policy1, expand_policy2); - next_level(exceeding_box, exceeding1, exceeding2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + if (! next_level(exceeding_box, exceeding1, exceeding2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy)) + { + return false; // interrupt + } } else { - handle_two(exceeding1, exceeding2, visitor); + if (! handle_two(exceeding1, exceeding2, visitor)) + { + return false; // interrupt + } } // All exceeding from 1 with lower and upper of 2: @@ -458,60 +477,87 @@ public : if (recurse_ok(lower2, upper2, exceeding1, min_elements, level)) { Box exceeding_box = get_new_box(exceeding1, expand_policy1); - next_level(exceeding_box, exceeding1, lower2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); - next_level(exceeding_box, exceeding1, upper2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + if (! (next_level(exceeding_box, exceeding1, lower2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy) + && next_level(exceeding_box, exceeding1, upper2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy)) ) + { + return false; // interrupt + } } else { - handle_two(exceeding1, lower2, visitor); - handle_two(exceeding1, upper2, visitor); + if (! (handle_two(exceeding1, lower2, visitor) + && handle_two(exceeding1, upper2, visitor)) ) + { + return false; // interrupt + } } } - if (boost::size(exceeding2) > 0) + if (! boost::empty(exceeding2)) { // All exceeding from 2 with lower and upper of 1: if (recurse_ok(lower1, upper1, exceeding2, min_elements, level)) { Box exceeding_box = get_new_box(exceeding2, expand_policy2); - next_level(exceeding_box, lower1, exceeding2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); - next_level(exceeding_box, upper1, exceeding2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + if (! (next_level(exceeding_box, lower1, exceeding2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy) + && next_level(exceeding_box, upper1, exceeding2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy)) ) + { + return false; // interrupt + } } else { - handle_two(lower1, exceeding2, visitor); - handle_two(upper1, exceeding2, visitor); + if (! (handle_two(lower1, exceeding2, visitor) + && handle_two(upper1, exceeding2, visitor)) ) + { + return false; // interrupt + } } } if (recurse_ok(lower1, lower2, min_elements, level)) { - next_level(lower_box, lower1, lower2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + if (! next_level(lower_box, lower1, lower2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy) ) + { + return false; // interrupt + } } else { - handle_two(lower1, lower2, visitor); + if (! handle_two(lower1, lower2, visitor)) + { + return false; // interrupt + } } + if (recurse_ok(upper1, upper2, min_elements, level)) { - next_level(upper_box, upper1, upper2, level, - min_elements, visitor, expand_policy1, overlaps_policy1, - expand_policy2, overlaps_policy2, box_policy); + if (! next_level(upper_box, upper1, upper2, level, + min_elements, visitor, expand_policy1, overlaps_policy1, + expand_policy2, overlaps_policy2, box_policy) ) + { + return false; // interrupt + } } else { - handle_two(upper1, upper2, visitor); + if (! handle_two(upper1, upper2, visitor)) + { + return false; // interrupt + } } + + return true; } }; @@ -577,13 +623,13 @@ public: typename ExpandPolicy, typename OverlapsPolicy > - static inline void apply(ForwardRange const& forward_range, + static inline bool apply(ForwardRange const& forward_range, VisitPolicy& visitor, ExpandPolicy const& expand_policy, OverlapsPolicy const& overlaps_policy) { - apply(forward_range, visitor, expand_policy, overlaps_policy, - default_min_elements, detail::partition::visit_no_policy()); + return apply(forward_range, visitor, expand_policy, overlaps_policy, + default_min_elements, detail::partition::visit_no_policy()); } template @@ -593,14 +639,14 @@ public: typename ExpandPolicy, typename OverlapsPolicy > - static inline void apply(ForwardRange const& forward_range, + static inline bool apply(ForwardRange const& forward_range, VisitPolicy& visitor, ExpandPolicy const& expand_policy, OverlapsPolicy const& overlaps_policy, std::size_t min_elements) { - apply(forward_range, visitor, expand_policy, overlaps_policy, - min_elements, detail::partition::visit_no_policy()); + return apply(forward_range, visitor, expand_policy, overlaps_policy, + min_elements, detail::partition::visit_no_policy()); } template @@ -611,7 +657,7 @@ public: typename OverlapsPolicy, typename VisitBoxPolicy > - static inline void apply(ForwardRange const& forward_range, + static inline bool apply(ForwardRange const& forward_range, VisitPolicy& visitor, ExpandPolicy const& expand_policy, OverlapsPolicy const& overlaps_policy, @@ -631,7 +677,7 @@ public: expand_to_range<IncludePolicy1>(forward_range, total, iterator_vector, expand_policy); - detail::partition::partition_one_range + return detail::partition::partition_one_range < 0, Box >::apply(total, iterator_vector, 0, min_elements, @@ -646,10 +692,15 @@ public: iterator_type it2 = it1; for(++it2; it2 != boost::end(forward_range); ++it2) { - visitor.apply(*it1, *it2); + if (! visitor.apply(*it1, *it2)) + { + return false; // interrupt + } } } } + + return true; } template @@ -660,15 +711,15 @@ public: typename ExpandPolicy1, typename OverlapsPolicy1 > - static inline void apply(ForwardRange1 const& forward_range1, + static inline bool apply(ForwardRange1 const& forward_range1, ForwardRange2 const& forward_range2, VisitPolicy& visitor, ExpandPolicy1 const& expand_policy1, OverlapsPolicy1 const& overlaps_policy1) { - apply(forward_range1, forward_range2, visitor, - expand_policy1, overlaps_policy1, expand_policy1, overlaps_policy1, - default_min_elements, detail::partition::visit_no_policy()); + return apply(forward_range1, forward_range2, visitor, + expand_policy1, overlaps_policy1, expand_policy1, overlaps_policy1, + default_min_elements, detail::partition::visit_no_policy()); } template @@ -681,7 +732,7 @@ public: typename ExpandPolicy2, typename OverlapsPolicy2 > - static inline void apply(ForwardRange1 const& forward_range1, + static inline bool apply(ForwardRange1 const& forward_range1, ForwardRange2 const& forward_range2, VisitPolicy& visitor, ExpandPolicy1 const& expand_policy1, @@ -689,9 +740,9 @@ public: ExpandPolicy2 const& expand_policy2, OverlapsPolicy2 const& overlaps_policy2) { - apply(forward_range1, forward_range2, visitor, - expand_policy1, overlaps_policy1, expand_policy2, overlaps_policy2, - default_min_elements, detail::partition::visit_no_policy()); + return apply(forward_range1, forward_range2, visitor, + expand_policy1, overlaps_policy1, expand_policy2, overlaps_policy2, + default_min_elements, detail::partition::visit_no_policy()); } template @@ -704,7 +755,7 @@ public: typename ExpandPolicy2, typename OverlapsPolicy2 > - static inline void apply(ForwardRange1 const& forward_range1, + static inline bool apply(ForwardRange1 const& forward_range1, ForwardRange2 const& forward_range2, VisitPolicy& visitor, ExpandPolicy1 const& expand_policy1, @@ -713,9 +764,9 @@ public: OverlapsPolicy2 const& overlaps_policy2, std::size_t min_elements) { - apply(forward_range1, forward_range2, visitor, - expand_policy1, overlaps_policy1, expand_policy2, overlaps_policy1, - min_elements, detail::partition::visit_no_policy()); + return apply(forward_range1, forward_range2, visitor, + expand_policy1, overlaps_policy1, expand_policy2, overlaps_policy1, + min_elements, detail::partition::visit_no_policy()); } template @@ -729,7 +780,7 @@ public: typename OverlapsPolicy2, typename VisitBoxPolicy > - static inline void apply(ForwardRange1 const& forward_range1, + static inline bool apply(ForwardRange1 const& forward_range1, ForwardRange2 const& forward_range2, VisitPolicy& visitor, ExpandPolicy1 const& expand_policy1, @@ -761,7 +812,7 @@ public: expand_to_range<IncludePolicy2>(forward_range2, total, iterator_vector2, expand_policy2); - detail::partition::partition_two_ranges + return detail::partition::partition_two_ranges < 0, Box >::apply(total, iterator_vector1, iterator_vector2, @@ -779,10 +830,15 @@ public: it2 != boost::end(forward_range2); ++it2) { - visitor.apply(*it1, *it2); + if (! visitor.apply(*it1, *it2)) + { + return false; // interrupt + } } } } + + return true; } }; diff --git a/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index 607ba81531..b8ea5e30e6 100644 --- a/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -5,10 +5,11 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -19,11 +20,13 @@ #include <boost/geometry/algorithms/detail/direction_code.hpp> #include <boost/geometry/algorithms/detail/recalculate.hpp> +#include <boost/geometry/core/cs.hpp> #include <boost/geometry/policies/robustness/robust_point_type.hpp> #include <boost/geometry/strategies/side.hpp> #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> + namespace boost { namespace geometry { @@ -32,6 +35,26 @@ namespace boost { namespace geometry namespace detail { +template <typename Point1, typename Point2, typename Point3> +inline bool collinear_point_is_spike_or_equal(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) +{ + // Check if segment is equal + int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); + int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); + if (sgn_x1 == 0 && sgn_y1 == 0) + { + return true; + } + + // Check if segment moves forward + int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); + int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); + + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; +} + // Checks if a point ("last_point") causes a spike w.r.t. // the specified two other points (segment_a, segment_b) // @@ -42,33 +65,29 @@ namespace detail // So specify last point first, then (a,b) // The segment's orientation does matter: if lp is to the right of b // no spike is reported -template <typename Point1, typename Point2, typename Point3> -static inline bool point_is_spike_or_equal(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b) +template +< + typename Point1, typename Point2, typename Point3, + typename SideStrategy +> +static inline bool point_is_spike_or_equal(Point1 const& last_point, // prev | back + Point2 const& segment_a, // next | back - 2 + Point3 const& segment_b, // curr | back - 1 | spike's vertex + SideStrategy const& strategy) { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag<Point1>::type - >::type side_strategy; - - int const side = side_strategy::apply(last_point, segment_a, segment_b); + int const side = strategy.apply(segment_a, segment_b, last_point); if (side == 0) { // Last point is collinear w.r.t previous segment. - // Check if it is equal - int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); - int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); - if (sgn_x1 == 0 && sgn_y1 == 0) - { - return true; - } - - // Check if it moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; +#ifdef BOOST_GEOMETRY_ENABLE_POINT_IS_SPIKE_OR_EQUAL_TEST + bool r1 = collinear_point_is_spike_or_equal(last_point, segment_a, segment_b); + bool r2 = direction_code(segment_a, segment_b, last_point) < 1; + if (r1 != r2) + std::cout << "spike detection failure with: " << r1 << " " << r2 << std::endl; + return r2; +#else + return direction_code(segment_a, segment_b, last_point) < 1; +#endif } return false; } @@ -78,14 +97,16 @@ template typename Point1, typename Point2, typename Point3, + typename SideStrategy, typename RobustPolicy > static inline bool point_is_spike_or_equal(Point1 const& last_point, Point2 const& segment_a, Point3 const& segment_b, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { - if (point_is_spike_or_equal(last_point, segment_a, segment_b)) + if (point_is_spike_or_equal(last_point, segment_a, segment_b, strategy)) { return true; } @@ -111,7 +132,8 @@ static inline bool point_is_spike_or_equal(Point1 const& last_point, ( last_point_rob, segment_a_rob, - segment_b_rob + segment_b_rob, + strategy ); } diff --git a/boost/geometry/algorithms/detail/point_on_border.hpp b/boost/geometry/algorithms/detail/point_on_border.hpp index 1c751c23e4..831081aa69 100644 --- a/boost/geometry/algorithms/detail/point_on_border.hpp +++ b/boost/geometry/algorithms/detail/point_on_border.hpp @@ -4,6 +4,10 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -18,6 +22,7 @@ #include <cstddef> #include <boost/range.hpp> +#include <boost/static_assert.hpp> #include <boost/geometry/core/tags.hpp> #include <boost/geometry/core/point_type.hpp> @@ -29,6 +34,8 @@ #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp> #include <boost/geometry/algorithms/detail/equals/point_point.hpp> +#include <boost/geometry/util/condition.hpp> + namespace boost { namespace geometry { @@ -39,10 +46,10 @@ namespace detail { namespace point_on_border { -template<typename Point> struct get_point { - static inline bool apply(Point& destination, Point const& source, bool) + template <typename Point> + static inline bool apply(Point& destination, Point const& source) { destination = source; return true; @@ -74,66 +81,77 @@ struct midpoint_helper<Point, DimensionCount, DimensionCount> }; -template<typename Point, typename Range> +template <bool Midpoint> struct point_on_range { - static inline bool apply(Point& point, Range const& range, bool midpoint) + // Version with iterator + template<typename Point, typename Iterator> + static inline bool apply(Point& point, Iterator begin, Iterator end) { - const std::size_t n = boost::size(range); - if (midpoint && n > 1) + Iterator it = begin; + if (it == end) { - typedef typename boost::range_iterator - < - Range const - >::type iterator; - - iterator it = boost::begin(range); - iterator prev = it++; - while (it != boost::end(range) - && detail::equals::equals_point_point(*it, *prev)) - { - prev = it++; - } - if (it != boost::end(range)) - { - return midpoint_helper - < - Point, - 0, dimension<Point>::value - >::apply(point, *prev, *it); - } + return false; } - if (n > 0) + if (! Midpoint) { - geometry::detail::conversion::convert_point_to_point(*boost::begin(range), point); + geometry::detail::conversion::convert_point_to_point(*it, point); return true; } + + Iterator prev = it++; + + // Go to next non-duplicate point + while (it != end + && detail::equals::equals_point_point(*it, *prev)) + { + prev = it++; + } + if (it != end) + { + return midpoint_helper + < + Point, + 0, dimension<Point>::value + >::apply(point, *prev, *it); + } return false; } + + // Version with range + template<typename Point, typename Range> + static inline bool apply(Point& point, Range const& range) + { + typedef typename geometry::cs_tag<Point>::type cs_tag; + BOOST_STATIC_ASSERT((! Midpoint || boost::is_same<cs_tag, cartesian_tag>::value)); + + return apply(point, boost::begin(range), boost::end(range)); + } }; -template<typename Point, typename Polygon> +template <bool Midpoint> struct point_on_polygon { - static inline bool apply(Point& point, Polygon const& polygon, bool midpoint) + template<typename Point, typename Polygon> + static inline bool apply(Point& point, Polygon const& polygon) { return point_on_range < - Point, - typename ring_type<Polygon>::type - >::apply(point, exterior_ring(polygon), midpoint); + Midpoint + >::apply(point, exterior_ring(polygon)); } }; -template<typename Point, typename Box> +template <bool Midpoint> struct point_on_box { - static inline bool apply(Point& point, Box const& box, bool midpoint) + template<typename Point, typename Box> + static inline bool apply(Point& point, Box const& box) { - if (midpoint) + if (BOOST_GEOMETRY_CONDITION(Midpoint)) { Point p1, p2; detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, p1); @@ -154,15 +172,11 @@ struct point_on_box }; -template -< - typename Point, - typename MultiGeometry, - typename Policy -> +template <typename Policy> struct point_on_multi { - static inline bool apply(Point& point, MultiGeometry const& multi, bool midpoint) + template<typename Point, typename MultiGeometry> + static inline bool apply(Point& point, MultiGeometry const& multi) { // Take a point on the first multi-geometry // (i.e. the first that is not empty) @@ -173,7 +187,7 @@ struct point_on_multi it != boost::end(multi); ++it) { - if (Policy::apply(point, *it, midpoint)) + if (Policy::apply(point, *it)) { return true; } @@ -195,70 +209,57 @@ namespace dispatch template < typename GeometryTag, - typename Point, - typename Geometry + bool Midpoint > struct point_on_border {}; -template<typename Point> -struct point_on_border<point_tag, Point, Point> - : detail::point_on_border::get_point<Point> +template <bool Midpoint> +struct point_on_border<point_tag, Midpoint> + : detail::point_on_border::get_point {}; -template<typename Point, typename Linestring> -struct point_on_border<linestring_tag, Point, Linestring> - : detail::point_on_border::point_on_range<Point, Linestring> +template <bool Midpoint> +struct point_on_border<linestring_tag, Midpoint> + : detail::point_on_border::point_on_range<Midpoint> {}; -template<typename Point, typename Ring> -struct point_on_border<ring_tag, Point, Ring> - : detail::point_on_border::point_on_range<Point, Ring> +template <bool Midpoint> +struct point_on_border<ring_tag, Midpoint> + : detail::point_on_border::point_on_range<Midpoint> {}; -template<typename Point, typename Polygon> -struct point_on_border<polygon_tag, Point, Polygon> - : detail::point_on_border::point_on_polygon<Point, Polygon> +template <bool Midpoint> +struct point_on_border<polygon_tag, Midpoint> + : detail::point_on_border::point_on_polygon<Midpoint> {}; -template<typename Point, typename Box> -struct point_on_border<box_tag, Point, Box> - : detail::point_on_border::point_on_box<Point, Box> +template <bool Midpoint> +struct point_on_border<box_tag, Midpoint> + : detail::point_on_border::point_on_box<Midpoint> {}; -template<typename Point, typename Multi> -struct point_on_border<multi_polygon_tag, Point, Multi> +template <bool Midpoint> +struct point_on_border<multi_polygon_tag, Midpoint> : detail::point_on_border::point_on_multi < - Point, - Multi, - detail::point_on_border::point_on_polygon - < - Point, - typename boost::range_value<Multi>::type - > + detail::point_on_border::point_on_polygon<Midpoint> > {}; -template<typename Point, typename Multi> -struct point_on_border<multi_linestring_tag, Point, Multi> +template <bool Midpoint> +struct point_on_border<multi_linestring_tag, Midpoint> : detail::point_on_border::point_on_multi < - Point, - Multi, - detail::point_on_border::point_on_range - < - Point, - typename boost::range_value<Multi>::type - > + detail::point_on_border::point_on_range<Midpoint> > {}; @@ -273,18 +274,12 @@ struct point_on_border<multi_linestring_tag, Point, Multi> \tparam Geometry geometry type. This also defines the type of the output point \param point to assign \param geometry geometry to take point from -\param midpoint boolean flag, true if the point should not be a vertex, but some point - in between of two vertices \return TRUE if successful, else false. It is only false if polygon/line have no points \note for a polygon, it is always a point on the exterior ring -\note for take_midpoint, it is not taken from two consecutive duplicate vertices, - (unless there are no other). */ template <typename Point, typename Geometry> -inline bool point_on_border(Point& point, - Geometry const& geometry, - bool midpoint = false) +inline bool point_on_border(Point& point, Geometry const& geometry) { concepts::check<Point>(); concepts::check<Geometry const>(); @@ -292,12 +287,32 @@ inline bool point_on_border(Point& point, return dispatch::point_on_border < typename tag<Geometry>::type, - Point, - Geometry - >::apply(point, geometry, midpoint); + false + >::apply(point, geometry); } +/*! +\tparam Midpoint boolean flag, true if the point should not be a vertex, but some point + in between of two vertices +\note for Midpoint, it is not taken from two consecutive duplicate vertices, + (unless there are no other). + */ +/* +template <bool Midpoint, typename Point, typename Geometry> +inline bool point_on_border(Point& point, Geometry const& geometry) +{ + concepts::check<Point>(); + concepts::check<Geometry const>(); + + return dispatch::point_on_border + < + typename tag<Geometry>::type, + Midpoint + >::apply(point, geometry); +} +*/ + }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/relate/implementation.hpp b/boost/geometry/algorithms/detail/relate/implementation.hpp index 3bd0f806c1..8f7942d46e 100644 --- a/boost/geometry/algorithms/detail/relate/implementation.hpp +++ b/boost/geometry/algorithms/detail/relate/implementation.hpp @@ -23,6 +23,7 @@ #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp> #include <boost/geometry/algorithms/detail/relate/linear_linear.hpp> #include <boost/geometry/algorithms/detail/relate/linear_areal.hpp> +#include <boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp> #include <boost/geometry/algorithms/detail/relate/areal_areal.hpp> #include <boost/geometry/strategies/intersection.hpp> @@ -81,6 +82,16 @@ struct relate<Geometry, Point, Tag1, point_tag, TopDim1, 0, true> : detail::relate::geometry_point<Geometry, Point> {}; +template <typename MultiPoint, typename Geometry, typename Tag2, int TopDim2> +struct relate<MultiPoint, Geometry, multi_point_tag, Tag2, 0, TopDim2, false> + : detail::relate::multi_point_geometry<MultiPoint, Geometry> +{}; + +template <typename Geometry, typename MultiPoint, typename Tag1, int TopDim1> +struct relate<Geometry, MultiPoint, Tag1, multi_point_tag, TopDim1, 0, false> + : detail::relate::geometry_multi_point<Geometry, MultiPoint> +{}; + template <typename Linear1, typename Linear2, typename Tag1, typename Tag2> struct relate<Linear1, Linear2, Tag1, Tag2, 1, 1, true> diff --git a/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp b/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp new file mode 100644 index 0000000000..47c6963b87 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp @@ -0,0 +1,568 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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_RELATE_MULTI_POINT_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_MULTI_POINT_GEOMETRY_HPP + + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> +#include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp> +#include <boost/geometry/algorithms/detail/relate/result.hpp> +#include <boost/geometry/algorithms/detail/relate/topology_check.hpp> +#include <boost/geometry/algorithms/detail/within/point_in_geometry.hpp> +#include <boost/geometry/algorithms/envelope.hpp> + +#include <boost/geometry/core/is_areal.hpp> +#include <boost/geometry/core/point_type.hpp> + +#include <boost/geometry/geometries/box.hpp> + +#include <boost/geometry/index/rtree.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate +{ + +template +< + typename Geometry, + typename Tag = typename tag<Geometry>::type +> +struct multi_point_geometry_eb +{ + template <typename MultiPoint> + static inline bool apply(MultiPoint const& , + detail::relate::topology_check<Geometry> const& ) + { + return true; + } +}; + +template <typename Geometry> +struct multi_point_geometry_eb<Geometry, linestring_tag> +{ + template <typename Points> + struct boundary_visitor + { + boundary_visitor(Points const& points) + : m_points(points) + , m_boundary_found(false) + {} + + template <typename Point> + struct find_pred + { + find_pred(Point const& point) + : m_point(point) + {} + + template <typename Pt> + bool operator()(Pt const& pt) const + { + return detail::equals::equals_point_point(pt, m_point); + } + + Point const& m_point; + }; + + template <typename Point> + bool apply(Point const& boundary_point) + { + if (std::find_if(m_points.begin(), m_points.end(), find_pred<Point>(boundary_point)) == m_points.end()) + { + m_boundary_found = true; + return false; + } + return true; + } + + bool result() const { return m_boundary_found; } + + private: + Points const& m_points; + bool m_boundary_found; + }; + + template <typename MultiPoint> + static inline bool apply(MultiPoint const& multi_point, + detail::relate::topology_check<Geometry> const& tc) + { + boundary_visitor<MultiPoint> visitor(multi_point); + tc.for_each_boundary_point(visitor); + return visitor.result(); + } +}; + +template <typename Geometry> +struct multi_point_geometry_eb<Geometry, multi_linestring_tag> +{ + template <typename Points> + struct boundary_visitor + { + boundary_visitor(Points const& points) + : m_points(points) + , m_boundary_found(false) + {} + + template <typename Point> + bool apply(Point const& boundary_point) + { + if (! std::binary_search(m_points.begin(), m_points.end(), boundary_point, relate::less())) + { + m_boundary_found = true; + return false; + } + return true; + } + + bool result() const { return m_boundary_found; } + + private: + Points const& m_points; + bool m_boundary_found; + }; + + template <typename MultiPoint> + static inline bool apply(MultiPoint const& multi_point, + detail::relate::topology_check<Geometry> const& tc) + { + typedef typename boost::range_value<MultiPoint>::type point_type; + typedef std::vector<point_type> points_type; + points_type points(boost::begin(multi_point), boost::end(multi_point)); + std::sort(points.begin(), points.end(), relate::less()); + + boundary_visitor<points_type> visitor(points); + tc.for_each_boundary_point(visitor); + return visitor.result(); + } +}; + +// SingleGeometry - Linear or Areal +template <typename MultiPoint, typename SingleGeometry, bool Transpose = false> +struct multi_point_single_geometry +{ + static const bool interruption_enabled = true; + + template <typename Result, typename Strategy> + static inline void apply(MultiPoint const& multi_point, + SingleGeometry const& single_geometry, + Result & result, + Strategy const& strategy) + { + typedef typename point_type<SingleGeometry>::type point2_type; + typedef model::box<point2_type> box2_type; + + box2_type box2; + geometry::envelope(single_geometry, box2, strategy.get_envelope_strategy()); + geometry::detail::expand_by_epsilon(box2); + + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + if (! (relate::may_update<interior, interior, '0', Transpose>(result) + || relate::may_update<interior, boundary, '0', Transpose>(result) + || relate::may_update<interior, exterior, '0', Transpose>(result) ) ) + { + break; + } + + // The default strategy is enough for Point/Box + if (detail::disjoint::disjoint_point_box(*it, box2)) + { + relate::set<interior, exterior, '0', Transpose>(result); + } + else + { + int in_val = detail::within::point_in_geometry(*it, single_geometry, strategy); + + if (in_val > 0) // within + { + relate::set<interior, interior, '0', Transpose>(result); + } + else if (in_val == 0) + { + relate::set<interior, boundary, '0', Transpose>(result); + } + else // in_val < 0 - not within + { + relate::set<interior, exterior, '0', Transpose>(result); + } + } + + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) + { + return; + } + } + + typedef detail::relate::topology_check<SingleGeometry> tc_t; + if ( relate::may_update<exterior, interior, tc_t::interior, Transpose>(result) + || relate::may_update<exterior, boundary, tc_t::boundary, Transpose>(result) ) + { + tc_t tc(single_geometry); + + if ( relate::may_update<exterior, interior, tc_t::interior, Transpose>(result) + && tc.has_interior() ) + { + // TODO: this is not true if a linestring is degenerated to a point + // then the interior has topological dimension = 0, not 1 + relate::set<exterior, interior, tc_t::interior, Transpose>(result); + } + + if ( relate::may_update<exterior, boundary, tc_t::boundary, Transpose>(result) + && tc.has_boundary() ) + { + if (multi_point_geometry_eb<SingleGeometry>::apply(multi_point, tc)) + relate::set<exterior, boundary, tc_t::boundary, Transpose>(result); + } + } + + relate::set<exterior, exterior, result_dimension<MultiPoint>::value, Transpose>(result); + } +}; + + +// MultiGeometry - Linear or Areal +// part of the algorithm calculating II and IB when no IE has to be calculated +// using partition() +template <typename MultiPoint, typename MultiGeometry, bool Transpose> +class multi_point_multi_geometry_ii_ib +{ + struct expand_box_point + { + template <typename Box, typename Point> + static inline void apply(Box& total, Point const& point) + { + geometry::expand(total, point); + } + }; + + struct expand_box_box_pair + { + template <typename Box, typename BoxPair> + static inline void apply(Box& total, BoxPair const& box_pair) + { + geometry::expand(total, box_pair.first); + } + }; + + struct overlaps_box_point + { + template <typename Box, typename Point> + static inline bool apply(Box const& box, Point const& point) + { + // The default strategy is enough for Point/Box + return ! detail::disjoint::disjoint_point_box(point, box); + } + }; + + struct overlaps_box_box_pair + { + template <typename Box, typename BoxPair> + static inline bool apply(Box const& box, BoxPair const& box_pair) + { + // The default strategy is enough for Box/Box + return ! detail::disjoint::disjoint_box_box(box_pair.first, box); + } + }; + + template <typename Result, typename PtSegStrategy> + class item_visitor_type + { + public: + item_visitor_type(MultiGeometry const& multi_geometry, + detail::relate::topology_check<MultiGeometry> const& tc, + Result & result, + PtSegStrategy const& strategy) + : m_multi_geometry(multi_geometry) + , m_tc(tc) + , m_result(result) + , m_strategy(strategy) + {} + + template <typename Point, typename BoxPair> + inline bool apply(Point const& point, BoxPair const& box_pair) + { + // The default strategy is enough for Point/Box + if (! detail::disjoint::disjoint_point_box(point, box_pair.first)) + { + typename boost::range_value<MultiGeometry>::type const& + single = range::at(m_multi_geometry, box_pair.second); + + int in_val = detail::within::point_in_geometry(point, single, m_strategy); + + if (in_val > 0) // within + { + relate::set<interior, interior, '0', Transpose>(m_result); + } + else if (in_val == 0) + { + if (m_tc.check_boundary_point(point)) + relate::set<interior, boundary, '0', Transpose>(m_result); + else + relate::set<interior, interior, '0', Transpose>(m_result); + } + } + + if ( BOOST_GEOMETRY_CONDITION(m_result.interrupt) ) + { + return false; + } + + if (! (relate::may_update<interior, interior, '0', Transpose>(m_result) + || relate::may_update<interior, boundary, '0', Transpose>(m_result) ) ) + { + return false; + } + + return true; + } + + + private: + MultiGeometry const& m_multi_geometry; + detail::relate::topology_check<MultiGeometry> const& m_tc; + Result & m_result; + PtSegStrategy const& m_strategy; + }; + +public: + typedef typename point_type<MultiPoint>::type point1_type; + typedef typename point_type<MultiGeometry>::type point2_type; + typedef model::box<point1_type> box1_type; + typedef model::box<point2_type> box2_type; + typedef std::pair<box2_type, std::size_t> box_pair_type; + + template <typename Result, typename Strategy> + static inline void apply(MultiPoint const& multi_point, + MultiGeometry const& multi_geometry, + std::vector<box_pair_type> const& boxes, + detail::relate::topology_check<MultiGeometry> const& tc, + Result & result, + Strategy const& strategy) + { + item_visitor_type<Result, Strategy> visitor(multi_geometry, tc, result, strategy); + + geometry::partition + < + box1_type + >::apply(multi_point, boxes, visitor, + expand_box_point(), + overlaps_box_point(), + expand_box_box_pair(), + overlaps_box_box_pair()); + } + +}; + +// MultiGeometry - Linear or Areal +// part of the algorithm calculating II, IB and IE +// using rtree +template <typename MultiPoint, typename MultiGeometry, bool Transpose> +struct multi_point_multi_geometry_ii_ib_ie +{ + typedef typename point_type<MultiPoint>::type point1_type; + typedef typename point_type<MultiGeometry>::type point2_type; + typedef model::box<point1_type> box1_type; + typedef model::box<point2_type> box2_type; + typedef std::pair<box2_type, std::size_t> box_pair_type; + typedef std::vector<box_pair_type> boxes_type; + typedef typename boxes_type::const_iterator boxes_iterator; + + template <typename Result, typename Strategy> + static inline void apply(MultiPoint const& multi_point, + MultiGeometry const& multi_geometry, + std::vector<box_pair_type> const& boxes, + detail::relate::topology_check<MultiGeometry> const& tc, + Result & result, + Strategy const& strategy) + { + index::rtree<box_pair_type, index::rstar<4> > rt(boxes.begin(), boxes.end()); + + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + if (! (relate::may_update<interior, interior, '0', Transpose>(result) + || relate::may_update<interior, boundary, '0', Transpose>(result) + || relate::may_update<interior, exterior, '0', Transpose>(result) ) ) + { + return; + } + + typename boost::range_value<MultiPoint>::type const& point = *it; + + boxes_type boxes_found; + rt.query(index::intersects(point), std::back_inserter(boxes_found)); + + bool found_ii_or_ib = false; + for (boxes_iterator bi = boxes_found.begin() ; bi != boxes_found.end() ; ++bi) + { + typename boost::range_value<MultiGeometry>::type const& + single = range::at(multi_geometry, bi->second); + + int in_val = detail::within::point_in_geometry(point, single, strategy); + + if (in_val > 0) // within + { + relate::set<interior, interior, '0', Transpose>(result); + found_ii_or_ib = true; + } + else if (in_val == 0) // on boundary of single + { + if (tc.check_boundary_point(point)) + relate::set<interior, boundary, '0', Transpose>(result); + else + relate::set<interior, interior, '0', Transpose>(result); + found_ii_or_ib = true; + } + } + + // neither interior nor boundary found -> exterior + if (found_ii_or_ib == false) + { + relate::set<interior, exterior, '0', Transpose>(result); + } + + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) + { + return; + } + } + } +}; + +// MultiGeometry - Linear or Areal +template <typename MultiPoint, typename MultiGeometry, bool Transpose = false> +struct multi_point_multi_geometry +{ + static const bool interruption_enabled = true; + + template <typename Result, typename Strategy> + static inline void apply(MultiPoint const& multi_point, + MultiGeometry const& multi_geometry, + Result & result, + Strategy const& strategy) + { + typedef typename point_type<MultiGeometry>::type point2_type; + typedef model::box<point2_type> box2_type; + typedef std::pair<box2_type, std::size_t> box_pair_type; + + typename Strategy::envelope_strategy_type const + envelope_strategy = strategy.get_envelope_strategy(); + + std::size_t count2 = boost::size(multi_geometry); + std::vector<box_pair_type> boxes(count2); + for (std::size_t i = 0 ; i < count2 ; ++i) + { + geometry::envelope(range::at(multi_geometry, i), boxes[i].first, envelope_strategy); + geometry::detail::expand_by_epsilon(boxes[i].first); + boxes[i].second = i; + } + + typedef detail::relate::topology_check<MultiGeometry> tc_t; + tc_t tc(multi_geometry); + + if ( relate::may_update<interior, interior, '0', Transpose>(result) + || relate::may_update<interior, boundary, '0', Transpose>(result) + || relate::may_update<interior, exterior, '0', Transpose>(result) ) + { + // If there is no need to calculate IE, use partition + if (! relate::may_update<interior, exterior, '0', Transpose>(result) ) + { + multi_point_multi_geometry_ii_ib<MultiPoint, MultiGeometry, Transpose> + ::apply(multi_point, multi_geometry, boxes, tc, result, strategy); + } + else // otherwise use rtree + { + multi_point_multi_geometry_ii_ib_ie<MultiPoint, MultiGeometry, Transpose> + ::apply(multi_point, multi_geometry, boxes, tc, result, strategy); + } + } + + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) + { + return; + } + + if ( relate::may_update<exterior, interior, tc_t::interior, Transpose>(result) + || relate::may_update<exterior, boundary, tc_t::boundary, Transpose>(result) ) + { + if ( relate::may_update<exterior, interior, tc_t::interior, Transpose>(result) + && tc.has_interior() ) + { + // TODO: this is not true if a linestring is degenerated to a point + // then the interior has topological dimension = 0, not 1 + relate::set<exterior, interior, tc_t::interior, Transpose>(result); + } + + if ( relate::may_update<exterior, boundary, tc_t::boundary, Transpose>(result) + && tc.has_boundary() ) + { + if (multi_point_geometry_eb<MultiGeometry>::apply(multi_point, tc)) + relate::set<exterior, boundary, tc_t::boundary, Transpose>(result); + } + } + + relate::set<exterior, exterior, result_dimension<MultiPoint>::value, Transpose>(result); + } + +}; + + +template +< + typename MultiPoint, typename Geometry, + bool Transpose = false, + bool isMulti = boost::is_same + < + typename tag_cast + < + typename tag<Geometry>::type, multi_tag + >::type, + multi_tag + >::value +> +struct multi_point_geometry + : multi_point_single_geometry<MultiPoint, Geometry, Transpose> +{}; + +template <typename MultiPoint, typename Geometry, bool Transpose> +struct multi_point_geometry<MultiPoint, Geometry, Transpose, true> + : multi_point_multi_geometry<MultiPoint, Geometry, Transpose> +{}; + + +// transposed result of multi_point_geometry +template <typename Geometry, typename MultiPoint> +struct geometry_multi_point +{ + static const bool interruption_enabled = true; + + template <typename Result, typename Strategy> + static inline void apply(Geometry const& geometry, MultiPoint const& multi_point, + Result & result, Strategy const& strategy) + { + multi_point_geometry<MultiPoint, Geometry, true>::apply(multi_point, geometry, result, strategy); + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_MULTI_POINT_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/relate/point_geometry.hpp b/boost/geometry/algorithms/detail/relate/point_geometry.hpp index a0c6c0d49b..e78a404b21 100644 --- a/boost/geometry/algorithms/detail/relate/point_geometry.hpp +++ b/boost/geometry/algorithms/detail/relate/point_geometry.hpp @@ -60,29 +60,27 @@ struct point_geometry if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; - // the point is on the boundary - if ( pig == 0 ) + typedef detail::relate::topology_check<Geometry> tc_t; + if ( relate::may_update<exterior, interior, tc_t::interior, Transpose>(result) + || relate::may_update<exterior, boundary, tc_t::boundary, Transpose>(result) ) { - // NOTE: even for MLs, if there is at least one boundary point, - // somewhere there must be another one - - // check if there are other boundaries outside - typedef detail::relate::topology_check<Geometry> tc_t; - //tc_t tc(geometry, point); - //if ( tc.has_interior ) - relate::set<exterior, interior, tc_t::interior, Transpose>(result); - //if ( tc.has_boundary ) - relate::set<exterior, boundary, tc_t::boundary, Transpose>(result); - } - else - { - // check if there is a boundary in Geometry - typedef detail::relate::topology_check<Geometry> tc_t; - tc_t tc(geometry); - if ( tc.has_interior ) + // the point is on the boundary + if ( pig == 0 ) + { + // NOTE: even for MLs, if there is at least one boundary point, + // somewhere there must be another one relate::set<exterior, interior, tc_t::interior, Transpose>(result); - if ( tc.has_boundary ) relate::set<exterior, boundary, tc_t::boundary, Transpose>(result); + } + else + { + // check if there is a boundary in Geometry + tc_t tc(geometry); + if ( tc.has_interior() ) + relate::set<exterior, interior, tc_t::interior, Transpose>(result); + if ( tc.has_boundary() ) + relate::set<exterior, boundary, tc_t::boundary, Transpose>(result); + } } } }; diff --git a/boost/geometry/algorithms/detail/relate/point_point.hpp b/boost/geometry/algorithms/detail/relate/point_point.hpp index b41d346f0b..68d8be031e 100644 --- a/boost/geometry/algorithms/detail/relate/point_point.hpp +++ b/boost/geometry/algorithms/detail/relate/point_point.hpp @@ -165,22 +165,41 @@ struct multipoint_multipoint } } -// TODO: ADD A CHECK TO THE RESULT INDICATING IF THE FIRST AND/OR SECOND GEOMETRY MUST BE ANALYSED + // The geometry containing smaller number of points will be analysed first + if ( boost::size(multi_point1) < boost::size(multi_point2) ) + { + search_both<false>(multi_point1, multi_point2, result); + } + else + { + search_both<true>(multi_point2, multi_point1, result); + } -// TODO: if I/I is set for one MPt, this won't be changed when the other one in analysed -// so if e.g. only I/I must be analysed we musn't check the other MPt + relate::set<exterior, exterior, result_dimension<MultiPoint1>::value>(result); + } -// TODO: Also, the geometry with the smaller number of points may be analysed first - //if ( boost::size(multi_point1) < boost::size(multi_point2) ) + template <bool Transpose, typename MPt1, typename MPt2, typename Result> + static inline void search_both(MPt1 const& first_sorted_mpt, MPt2 const& first_iterated_mpt, + Result & result) + { + if ( relate::may_update<interior, interior, '0'>(result) + || relate::may_update<interior, exterior, '0'>(result) + || relate::may_update<exterior, interior, '0'>(result) ) + { + // NlogN + MlogN + bool is_disjoint = search<Transpose>(first_sorted_mpt, first_iterated_mpt, result); - // NlogN + MlogN - bool all_handled = search<false>(multi_point1, multi_point2, result); - - if ( BOOST_GEOMETRY_CONDITION(all_handled || result.interrupt) ) - return; + if ( BOOST_GEOMETRY_CONDITION(is_disjoint || result.interrupt) ) + return; + } - // MlogM + NlogM - search<true>(multi_point2, multi_point1, result); + if ( relate::may_update<interior, interior, '0'>(result) + || relate::may_update<interior, exterior, '0'>(result) + || relate::may_update<exterior, interior, '0'>(result) ) + { + // MlogM + NlogM + search<! Transpose>(first_iterated_mpt, first_sorted_mpt, result); + } } template <bool Transpose, @@ -215,9 +234,6 @@ struct multipoint_multipoint break; } - // an optimization - bool all_handled = false; - if ( found_inside ) // some point of MP2 is equal to some of MP1 { // TODO: if I/I is set for one MPt, this won't be changed when the other one in analysed @@ -234,14 +250,10 @@ struct multipoint_multipoint { relate::set<interior, exterior, '0', Transpose>(result); relate::set<exterior, interior, '0', Transpose>(result); - - // if no point is intersecting the other MPt then we musn't analyse the reversed case - all_handled = true; } - relate::set<exterior, exterior, result_dimension<point_type>::value, Transpose>(result); - - return all_handled; + // if no point is intersecting the other MPt then we musn't analyse the reversed case + return ! found_inside; } }; diff --git a/boost/geometry/algorithms/detail/relate/topology_check.hpp b/boost/geometry/algorithms/detail/relate/topology_check.hpp index caa8a3c22d..654999d8fb 100644 --- a/boost/geometry/algorithms/detail/relate/topology_check.hpp +++ b/boost/geometry/algorithms/detail/relate/topology_check.hpp @@ -1,22 +1,23 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // 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) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP -#include <boost/geometry/util/range.hpp> #include <boost/geometry/algorithms/detail/equals/point_point.hpp> -#include <boost/geometry/policies/compare.hpp> +#include <boost/geometry/algorithms/detail/relate/less.hpp> #include <boost/geometry/util/has_nan_coordinate.hpp> +#include <boost/geometry/util/range.hpp> + namespace boost { namespace geometry { @@ -51,31 +52,63 @@ struct topology_check<Linestring, linestring_tag> static const char interior = '1'; static const char boundary = '0'; - bool has_interior; - bool has_boundary; - topology_check(Linestring const& ls) + : m_ls(ls) + , m_is_initialized(false) + {} + + bool has_interior() const { - init(ls, 0); /*dummy param*/ + init(); + return m_has_interior; } - template <typename IgnoreBoundaryPoint> - topology_check(Linestring const& ls, IgnoreBoundaryPoint const& ibp) + bool has_boundary() const { - init(ls, ibp); /*dummy param, won't be used*/ + init(); + return m_has_boundary; } - // Even if some point is on the boundary, if the Linestring has the boundary, - // there will be second boundary point different than IgnoreBoundaryPoint - template <typename IgnoreBoundaryPoint> - void init(Linestring const& ls, IgnoreBoundaryPoint const&) + /*template <typename Point> + bool check_boundary_point(Point const& point) const { - std::size_t count = boost::size(ls); - has_interior = count > 0; + init(); + return m_has_boundary + && ( equals::equals_point_point(point, range::front(m_ls)) + || equals::equals_point_point(point, range::back(m_ls)) ); + }*/ + + template <typename Visitor> + void for_each_boundary_point(Visitor & visitor) const + { + init(); + if (m_has_boundary) + { + if (visitor.apply(range::front(m_ls))) + visitor.apply(range::back(m_ls)); + } + } + +private: + void init() const + { + if (m_is_initialized) + return; + + std::size_t count = boost::size(m_ls); + m_has_interior = count > 0; // NOTE: Linestring with all points equal is treated as 1d linear ring - has_boundary = count > 1 - && ! detail::equals::equals_point_point(range::front(ls), range::back(ls)); + m_has_boundary = count > 1 + && ! detail::equals::equals_point_point(range::front(m_ls), range::back(m_ls)); + + m_is_initialized = true; } + + Linestring const& m_ls; + mutable bool m_is_initialized; + + mutable bool m_has_interior; + mutable bool m_has_boundary; }; template <typename MultiLinestring> @@ -84,29 +117,58 @@ struct topology_check<MultiLinestring, multi_linestring_tag> static const char interior = '1'; static const char boundary = '0'; - bool has_interior; - bool has_boundary; - topology_check(MultiLinestring const& mls) + : m_mls(mls) + , m_is_initialized(false) + {} + + bool has_interior() const { - init(mls, not_ignoring_counter()); + init(); + return m_has_interior; } - template <typename IgnoreBoundaryPoint> - topology_check(MultiLinestring const& mls, IgnoreBoundaryPoint const& ibp) + bool has_boundary() const { - init(mls, ignoring_counter<IgnoreBoundaryPoint>(ibp)); + init(); + return m_has_boundary; } - template <typename OddCounter> - void init(MultiLinestring const& mls, OddCounter const& odd_counter) + template <typename Point> + bool check_boundary_point(Point const& point) const { - typedef typename geometry::point_type<MultiLinestring>::type point_type; - std::vector<point_type> endpoints; - endpoints.reserve(boost::size(mls) * 2); + init(); + + if (! m_has_boundary) + return false; + + std::size_t count = count_equal(m_endpoints.begin(), m_endpoints.end(), point); + + return count % 2 != 0; // odd count -> boundary + } + + template <typename Visitor> + void for_each_boundary_point(Visitor & visitor) const + { + init(); + if (m_has_boundary) + { + for_each_boundary_point(m_endpoints.begin(), m_endpoints.end(), visitor); + } + } + +private: + void init() const + { + if (m_is_initialized) + return; + + m_endpoints.reserve(boost::size(m_mls) * 2); + + m_has_interior = false; typedef typename boost::range_iterator<MultiLinestring const>::type ls_iterator; - for ( ls_iterator it = boost::begin(mls) ; it != boost::end(mls) ; ++it ) + for ( ls_iterator it = boost::begin(m_mls) ; it != boost::end(m_mls) ; ++it ) { typename boost::range_reference<MultiLinestring const>::type ls = *it; @@ -115,7 +177,7 @@ struct topology_check<MultiLinestring, multi_linestring_tag> if (count > 0) { - has_interior = true; + m_has_interior = true; } if (count > 1) @@ -138,62 +200,59 @@ struct topology_check<MultiLinestring, multi_linestring_tag> // is not used anywhere in the code, still it's safer this way if (! geometry::has_nan_coordinate(front_pt)) { - endpoints.push_back(front_pt); + m_endpoints.push_back(front_pt); } if (! geometry::has_nan_coordinate(back_pt)) { - endpoints.push_back(back_pt); + m_endpoints.push_back(back_pt); } } } } - has_boundary = false; + m_has_boundary = false; - if ( !endpoints.empty() ) + if (! m_endpoints.empty() ) { - std::sort(endpoints.begin(), endpoints.end(), geometry::less<point_type>()); - has_boundary = odd_counter(endpoints.begin(), endpoints.end()); + std::sort(m_endpoints.begin(), m_endpoints.end(), relate::less()); + m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end()); } + + m_is_initialized = true; } - struct not_ignoring_counter + template <typename It, typename Point> + static inline std::size_t count_equal(It first, It last, Point const& point) { - template <typename It> - bool operator()(It first, It last) const - { - return find_odd_count(first, last); - } - }; + std::pair<It, It> rng = std::equal_range(first, last, point, relate::less()); + return (std::size_t)std::distance(rng.first, rng.second); + } - template <typename Point> - struct ignoring_counter + template <typename It> + static inline bool find_odd_count(It first, It last) { - ignoring_counter(Point const& pt) : m_pt(pt) {} + interrupting_visitor visitor; + for_each_boundary_point(first, last, visitor); + return visitor.found; + } - template <typename It> - bool operator()(It first, It last) const + struct interrupting_visitor + { + bool found; + interrupting_visitor() : found(false) {} + template <typename Point> + bool apply(Point const&) { - typedef typename std::iterator_traits<It>::value_type point_type; - - std::pair<It, It> ignore_range - = std::equal_range(first, last, m_pt, - geometry::less<point_type>()); - - if ( find_odd_count(first, ignore_range.first) ) - return true; - - return find_odd_count(ignore_range.second, last); + found = true; + return false; } - - Point const& m_pt; }; - template <typename It> - static inline bool find_odd_count(It first, It last) + template <typename It, typename Visitor> + static void for_each_boundary_point(It first, It last, Visitor& visitor) { if ( first == last ) - return false; + return; std::size_t count = 1; It prev = first; @@ -203,8 +262,14 @@ struct topology_check<MultiLinestring, multi_linestring_tag> // the end of the equal points subrange if ( ! equals::equals_point_point(*first, *prev) ) { + // odd count -> boundary if ( count % 2 != 0 ) - return true; + { + if (! visitor.apply(*prev)) + { + return; + } + } count = 1; } @@ -214,8 +279,22 @@ struct topology_check<MultiLinestring, multi_linestring_tag> } } - return count % 2 != 0; + // odd count -> boundary + if ( count % 2 != 0 ) + { + visitor.apply(*prev); + } } + +private: + MultiLinestring const& m_mls; + mutable bool m_is_initialized; + + mutable bool m_has_interior; + mutable bool m_has_boundary; + + typedef typename geometry::point_type<MultiLinestring>::type point_type; + mutable std::vector<point_type> m_endpoints; }; template <typename Ring> @@ -223,12 +302,11 @@ struct topology_check<Ring, ring_tag> { static const char interior = '2'; static const char boundary = '1'; - static const bool has_interior = true; - static const bool has_boundary = true; topology_check(Ring const&) {} - template <typename P> - topology_check(Ring const&, P const&) {} + + static bool has_interior() { return true; } + static bool has_boundary() { return true; } }; template <typename Polygon> @@ -236,12 +314,11 @@ struct topology_check<Polygon, polygon_tag> { static const char interior = '2'; static const char boundary = '1'; - static const bool has_interior = true; - static const bool has_boundary = true; - + topology_check(Polygon const&) {} - template <typename P> - topology_check(Polygon const&, P const&) {} + + static bool has_interior() { return true; } + static bool has_boundary() { return true; } }; template <typename MultiPolygon> @@ -249,12 +326,13 @@ struct topology_check<MultiPolygon, multi_polygon_tag> { static const char interior = '2'; static const char boundary = '1'; - static const bool has_interior = true; - static const bool has_boundary = true; - + topology_check(MultiPolygon const&) {} - template <typename P> - topology_check(MultiPolygon const&, P const&) {} + + static bool has_interior() { return true; } + static bool has_boundary() { return true; } + template <typename Point> + static bool check_boundary_point(Point const& ) { return true; } }; }} // namespace detail::relate diff --git a/boost/geometry/algorithms/detail/sections/section_functions.hpp b/boost/geometry/algorithms/detail/sections/section_functions.hpp index 7bc5c08046..67df3060c4 100644 --- a/boost/geometry/algorithms/detail/sections/section_functions.hpp +++ b/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -30,18 +30,87 @@ namespace detail { namespace section template < std::size_t Dimension, + typename Geometry, + typename CastedCSTag = typename tag_cast + < + typename cs_tag<Geometry>::type, + spherical_tag + >::type +> +struct preceding_check +{ + template <typename Point, typename Box> + static inline bool apply(int dir, Point const& point, Box const& /*point_box*/, Box const& other_box) + { + return (dir == 1 && get<Dimension>(point) < get<min_corner, Dimension>(other_box)) + || (dir == -1 && get<Dimension>(point) > get<max_corner, Dimension>(other_box)); + } +}; + +template <typename Geometry> +struct preceding_check<0, Geometry, spherical_tag> +{ + template <typename Point, typename Box> + static inline bool apply(int dir, Point const& point, Box const& point_box, Box const& other_box) + { + typedef typename select_coordinate_type + < + Point, Box + >::type calc_t; + typedef typename coordinate_system<Point>::type::units units_t; + + calc_t const c0 = 0; + + if (dir == 1) + { + calc_t const diff_min = math::longitude_distance_signed + < + units_t, calc_t + >(get<min_corner, 0>(other_box), get<0>(point)); + + calc_t const diff_min_min = math::longitude_distance_signed + < + units_t, calc_t + >(get<min_corner, 0>(other_box), get<min_corner, 0>(point_box)); + + return diff_min < c0 && diff_min_min <= c0 && diff_min_min <= diff_min; + } + else if (dir == -1) + { + calc_t const diff_max = math::longitude_distance_signed + < + units_t, calc_t + >(get<max_corner, 0>(other_box), get<0>(point)); + + calc_t const diff_max_max = math::longitude_distance_signed + < + units_t, calc_t + >(get<max_corner, 0>(other_box), get<max_corner, 0>(point_box)); + + return diff_max > c0 && diff_max_max >= c0 && diff_max <= diff_max_max; + } + + return false; + } +}; + + +template +< + std::size_t Dimension, typename Point, typename RobustBox, typename RobustPolicy > -static inline bool preceding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool preceding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { typename geometry::robust_point_type<Point, RobustPolicy>::type robust_point; geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get<Dimension>(robust_point) < get<min_corner, Dimension>(robust_box)) - || (dir == -1 && get<Dimension>(robust_point) > get<max_corner, Dimension>(robust_box)); + return preceding_check<Dimension, Point>::apply(dir, robust_point, point_robust_box, other_robust_box); } template @@ -51,14 +120,13 @@ template typename RobustBox, typename RobustPolicy > -static inline bool exceeding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool exceeding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { - typename geometry::robust_point_type<Point, RobustPolicy>::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get<Dimension>(robust_point) > get<max_corner, Dimension>(robust_box)) - || (dir == -1 && get<Dimension>(robust_point) < get<min_corner, Dimension>(robust_box)); + return preceding<Dimension>(-dir, point, point_robust_box, other_robust_box, robust_policy); } diff --git a/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/boost/geometry/algorithms/detail/sections/sectionalize.hpp index 3ed5b8db07..f1d8e7d231 100644 --- a/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013, 2014, 2015. -// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -29,6 +29,8 @@ #include <boost/mpl/vector_c.hpp> #include <boost/range.hpp> #include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/type_traits/is_fundamental.hpp> #include <boost/geometry/algorithms/assign.hpp> #include <boost/geometry/algorithms/envelope.hpp> @@ -54,6 +56,7 @@ #include <boost/geometry/geometries/segment.hpp> #include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp> +#include <boost/geometry/strategies/envelope.hpp> namespace boost { namespace geometry { @@ -133,11 +136,21 @@ struct sections : std::vector<section<Box, DimensionCount> > namespace detail { namespace sectionalize { +// NOTE: This utility will NOT work for latitudes, dimension 1 in spherical +// and geographic coordinate system because in these coordinate systems +// e.g. a segment on northern hemisphere may go towards greater latitude +// and then towards lesser latitude. template < + typename Point, typename DimensionVector, std::size_t Index, - std::size_t Count + std::size_t Count, + typename CastedCSTag = typename tag_cast + < + typename cs_tag<Point>::type, + spherical_tag + >::type > struct get_direction_loop { @@ -158,21 +171,67 @@ struct get_direction_loop get_direction_loop < + Point, DimensionVector, Index + 1, - Count + Count, + CastedCSTag + >::apply(seg, directions); + } +}; + +template +< + typename Point, + typename DimensionVector, + std::size_t Count +> +struct get_direction_loop<Point, DimensionVector, 0, Count, spherical_tag> +{ + typedef typename boost::mpl::at_c<DimensionVector, 0>::type dimension; + + template <typename Segment> + static inline void apply(Segment const& seg, + int directions[Count]) + { + typedef typename coordinate_type<Segment>::type coordinate_type; + typedef typename coordinate_system<Point>::type::units units_t; + + coordinate_type const diff = math::longitude_distance_signed + < + units_t, coordinate_type + >(geometry::get<0, 0>(seg), + geometry::get<1, 0>(seg)); + + coordinate_type zero = coordinate_type(); + directions[0] = diff > zero ? 1 : diff < zero ? -1 : 0; + + get_direction_loop + < + Point, + DimensionVector, + 1, + Count, + spherical_tag >::apply(seg, directions); } }; -template <typename DimensionVector, std::size_t Count> -struct get_direction_loop<DimensionVector, Count, Count> +template +< + typename Point, + typename DimensionVector, + std::size_t Count, + typename CastedCSTag +> +struct get_direction_loop<Point, DimensionVector, Count, Count, CastedCSTag> { template <typename Segment> static inline void apply(Segment const&, int [Count]) {} }; + //! Copy one static array to another template <typename T, std::size_t Index, std::size_t Count> struct copy_loop @@ -272,19 +331,21 @@ struct assign_loop<T, Count, Count> template <typename CSTag> struct box_first_in_section { - template <typename Box, typename Point> - static inline void apply(Box & box, Point const& prev, Point const& curr) + template <typename Box, typename Point, typename Strategy> + static inline void apply(Box & box, Point const& prev, Point const& curr, + Strategy const& strategy) { geometry::model::referring_segment<Point const> seg(prev, curr); - geometry::envelope(seg, box); + geometry::envelope(seg, box, strategy); } }; template <> struct box_first_in_section<cartesian_tag> { - template <typename Box, typename Point> - static inline void apply(Box & box, Point const& prev, Point const& curr) + template <typename Box, typename Point, typename Strategy> + static inline void apply(Box & box, Point const& prev, Point const& curr, + Strategy const& ) { geometry::envelope(prev, box); geometry::expand(box, curr); @@ -294,19 +355,21 @@ struct box_first_in_section<cartesian_tag> template <typename CSTag> struct box_next_in_section { - template <typename Box, typename Point> - static inline void apply(Box & box, Point const& prev, Point const& curr) + template <typename Box, typename Point, typename Strategy> + static inline void apply(Box & box, Point const& prev, Point const& curr, + Strategy const& strategy) { geometry::model::referring_segment<Point const> seg(prev, curr); - geometry::expand(box, seg); + geometry::expand(box, seg, strategy); } }; template <> struct box_next_in_section<cartesian_tag> { - template <typename Box, typename Point> - static inline void apply(Box & box, Point const& , Point const& curr) + template <typename Box, typename Point, typename Strategy> + static inline void apply(Box & box, Point const& , Point const& curr, + Strategy const& ) { geometry::expand(box, curr); } @@ -335,6 +398,30 @@ struct sectionalize_part ring_identifier ring_id, std::size_t max_count) { + typedef typename strategy::envelope::services::default_strategy + < + typename cs_tag<typename Sections::box_type>::type + >::type envelope_strategy_type; + + apply(sections, begin, end, + robust_policy, envelope_strategy_type(), + ring_id, max_count); + } + + template + < + typename Iterator, + typename RobustPolicy, + typename Sections, + typename EnvelopeStrategy + > + static inline void apply(Sections& sections, + Iterator begin, Iterator end, + RobustPolicy const& robust_policy, + EnvelopeStrategy const& strategy, + ring_identifier ring_id, + std::size_t max_count) + { boost::ignore_unused_variable_warning(robust_policy); typedef typename boost::range_value<Sections>::type section_type; @@ -379,7 +466,7 @@ struct sectionalize_part int direction_classes[dimension_count] = {0}; get_direction_loop < - DimensionVector, 0, dimension_count + Point, DimensionVector, 0, dimension_count >::apply(robust_segment, direction_classes); // if "dir" == 0 for all point-dimensions, it is duplicate. @@ -449,14 +536,14 @@ struct sectionalize_part // In cartesian this is envelope of previous point expanded with current point // in non-cartesian this is envelope of a segment box_first_in_section<typename cs_tag<robust_point_type>::type> - ::apply(section.bounding_box, previous_robust_point, current_robust_point); + ::apply(section.bounding_box, previous_robust_point, current_robust_point, strategy); } else { // In cartesian this is expand with current point // in non-cartesian this is expand with a segment box_next_in_section<typename cs_tag<robust_point_type>::type> - ::apply(section.bounding_box, previous_robust_point, current_robust_point); + ::apply(section.bounding_box, previous_robust_point, current_robust_point, strategy); } section.end_index = index + 1; @@ -501,11 +588,13 @@ struct sectionalize_range < typename Range, typename RobustPolicy, - typename Sections + typename Sections, + typename EnvelopeStrategy > static inline void apply(Range const& range, RobustPolicy const& robust_policy, Sections& sections, + EnvelopeStrategy const& strategy, ring_identifier ring_id, std::size_t max_count) { @@ -534,7 +623,7 @@ struct sectionalize_range sectionalize_part<Point, DimensionVector>::apply(sections, boost::begin(view), boost::end(view), - robust_policy, ring_id, max_count); + robust_policy, strategy, ring_id, max_count); } }; @@ -549,12 +638,15 @@ struct sectionalize_polygon < typename Polygon, typename RobustPolicy, - typename Sections + typename Sections, + typename EnvelopeStrategy > static inline void apply(Polygon const& poly, RobustPolicy const& robust_policy, Sections& sections, - ring_identifier ring_id, std::size_t max_count) + EnvelopeStrategy const& strategy, + ring_identifier ring_id, + std::size_t max_count) { typedef typename point_type<Polygon>::type point_type; typedef sectionalize_range @@ -564,7 +656,7 @@ struct sectionalize_polygon > per_range; ring_id.ring_index = -1; - per_range::apply(exterior_ring(poly), robust_policy, sections, ring_id, max_count); + per_range::apply(exterior_ring(poly), robust_policy, sections, strategy, ring_id, max_count); ring_id.ring_index++; typename interior_return_type<Polygon const>::type @@ -572,7 +664,7 @@ struct sectionalize_polygon for (typename detail::interior_iterator<Polygon const>::type it = boost::begin(rings); it != boost::end(rings); ++it, ++ring_id.ring_index) { - per_range::apply(*it, robust_policy, sections, ring_id, max_count); + per_range::apply(*it, robust_policy, sections, strategy, ring_id, max_count); } } }; @@ -584,11 +676,13 @@ struct sectionalize_box < typename Box, typename RobustPolicy, - typename Sections + typename Sections, + typename EnvelopeStrategy > static inline void apply(Box const& box, RobustPolicy const& robust_policy, Sections& sections, + EnvelopeStrategy const& , ring_identifier const& ring_id, std::size_t max_count) { typedef typename point_type<Box>::type point_type; @@ -613,12 +707,15 @@ struct sectionalize_box points.push_back(lr); points.push_back(ll); + // NOTE: Use cartesian envelope strategy in all coordinate systems + // because edges of a box are not geodesic segments sectionalize_range < closed, false, point_type, DimensionVector >::apply(points, robust_policy, sections, + strategy::envelope::cartesian_segment<>(), ring_id, max_count); } }; @@ -630,11 +727,15 @@ struct sectionalize_multi < typename MultiGeometry, typename RobustPolicy, - typename Sections + typename Sections, + typename EnvelopeStrategy > static inline void apply(MultiGeometry const& multi, RobustPolicy const& robust_policy, - Sections& sections, ring_identifier ring_id, std::size_t max_count) + Sections& sections, + EnvelopeStrategy const& strategy, + ring_identifier ring_id, + std::size_t max_count) { ring_id.multi_index = 0; for (typename boost::range_iterator<MultiGeometry const>::type @@ -642,7 +743,7 @@ struct sectionalize_multi it != boost::end(multi); ++it, ++ring_id.multi_index) { - Policy::apply(*it, robust_policy, sections, ring_id, max_count); + Policy::apply(*it, robust_policy, sections, strategy, ring_id, max_count); } } }; @@ -814,14 +915,18 @@ template typename DimensionVector, typename Geometry, typename Sections, - typename RobustPolicy + typename RobustPolicy, + typename EnvelopeStrategy > inline void sectionalize(Geometry const& geometry, RobustPolicy const& robust_policy, Sections& sections, + EnvelopeStrategy const& strategy, int source_index = 0, std::size_t max_count = 10) { + BOOST_STATIC_ASSERT((! boost::is_fundamental<EnvelopeStrategy>::value)); + concepts::check<Geometry const>(); typedef typename boost::range_value<Sections>::type section_type; @@ -855,12 +960,39 @@ inline void sectionalize(Geometry const& geometry, Geometry, Reverse, DimensionVector - >::apply(geometry, robust_policy, sections, ring_id, max_count); + >::apply(geometry, robust_policy, sections, strategy, ring_id, max_count); detail::sectionalize::enlarge_sections(sections); } +template +< + bool Reverse, + typename DimensionVector, + typename Geometry, + typename Sections, + typename RobustPolicy +> +inline void sectionalize(Geometry const& geometry, + RobustPolicy const& robust_policy, + Sections& sections, + int source_index = 0, + std::size_t max_count = 10) +{ + typedef typename strategy::envelope::services::default_strategy + < + typename cs_tag<Geometry>::type + >::type envelope_strategy_type; + + boost::geometry::sectionalize + < + Reverse, DimensionVector + >(geometry, robust_policy, sections, + envelope_strategy_type(), + source_index, max_count); +} + }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/touches/implementation.hpp b/boost/geometry/algorithms/detail/touches/implementation.hpp new file mode 100644 index 0000000000..94f1fba581 --- /dev/null +++ b/boost/geometry/algorithms/detail/touches/implementation.hpp @@ -0,0 +1,459 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2015, 2017. +// Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_TOUCHES_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_TOUCHES_IMPLEMENTATION_HPP + + +#include <boost/geometry/algorithms/detail/for_each_range.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay.hpp> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> +#include <boost/geometry/algorithms/detail/sub_range.hpp> +#include <boost/geometry/algorithms/detail/relate/relate_impl.hpp> +#include <boost/geometry/algorithms/detail/touches/interface.hpp> +#include <boost/geometry/algorithms/disjoint.hpp> +#include <boost/geometry/algorithms/intersects.hpp> +#include <boost/geometry/algorithms/num_geometries.hpp> +#include <boost/geometry/algorithms/relate.hpp> + +#include <boost/geometry/policies/robustness/no_rescale_policy.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace touches +{ + +// Box/Box + +template +< + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_box_loop +{ + template <typename Box1, typename Box2> + static inline bool apply(Box1 const& b1, Box2 const& b2, bool & touch) + { + typedef typename coordinate_type<Box1>::type coordinate_type1; + typedef typename coordinate_type<Box2>::type coordinate_type2; + + coordinate_type1 const& min1 = get<min_corner, Dimension>(b1); + coordinate_type1 const& max1 = get<max_corner, Dimension>(b1); + coordinate_type2 const& min2 = get<min_corner, Dimension>(b2); + coordinate_type2 const& max2 = get<max_corner, Dimension>(b2); + + // TODO assert or exception? + //BOOST_GEOMETRY_ASSERT(min1 <= max1 && min2 <= max2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + + if (max1 == min2 || max2 == min1) + { + touch = true; + } + + return box_box_loop + < + Dimension + 1, + DimensionCount + >::apply(b1, b2, touch); + } +}; + +template +< + std::size_t DimensionCount +> +struct box_box_loop<DimensionCount, DimensionCount> +{ + template <typename Box1, typename Box2> + static inline bool apply(Box1 const& , Box2 const&, bool &) + { + return true; + } +}; + +struct box_box +{ + template <typename Box1, typename Box2, typename Strategy> + static inline bool apply(Box1 const& b1, Box2 const& b2, Strategy const& /*strategy*/) + { + BOOST_STATIC_ASSERT((boost::is_same + < + typename geometry::coordinate_system<Box1>::type, + typename geometry::coordinate_system<Box2>::type + >::value + )); + assert_dimension_equal<Box1, Box2>(); + + bool touches = false; + bool ok = box_box_loop + < + 0, + dimension<Box1>::type::value + >::apply(b1, b2, touches); + + return ok && touches; + } +}; + +// Areal/Areal + +struct areal_interrupt_policy +{ + static bool const enabled = true; + bool found_touch; + bool found_not_touch; + + // dummy variable required by self_get_turn_points::get_turns + static bool const has_intersections = false; + + inline bool result() + { + return found_touch && !found_not_touch; + } + + inline areal_interrupt_policy() + : found_touch(false), found_not_touch(false) + {} + + template <typename Range> + inline bool apply(Range const& range) + { + // if already rejected (temp workaround?) + if ( found_not_touch ) + return true; + + typedef typename boost::range_iterator<Range const>::type iterator; + for ( iterator it = boost::begin(range) ; it != boost::end(range) ; ++it ) + { + if ( it->has(overlay::operation_intersection) ) + { + found_not_touch = true; + return true; + } + + switch(it->method) + { + case overlay::method_crosses: + found_not_touch = true; + return true; + case overlay::method_equal: + // Segment spatially equal means: at the right side + // the polygon internally overlaps. So return false. + found_not_touch = true; + return true; + case overlay::method_touch: + case overlay::method_touch_interior: + case overlay::method_collinear: + if ( ok_for_touch(*it) ) + { + found_touch = true; + } + else + { + found_not_touch = true; + return true; + } + break; + case overlay::method_none : + case overlay::method_disjoint : + case overlay::method_error : + break; + } + } + + return false; + } + + template <typename Turn> + inline bool ok_for_touch(Turn const& turn) + { + return turn.both(overlay::operation_union) + || turn.both(overlay::operation_blocked) + || turn.combination(overlay::operation_union, overlay::operation_blocked) + ; + } +}; + +template<typename Geometry, typename PointInRingStrategy> +struct check_each_ring_for_within +{ + bool has_within; + Geometry const& m_geometry; + PointInRingStrategy const& m_strategy; + + inline check_each_ring_for_within(Geometry const& g, PointInRingStrategy const& strategy) + : has_within(false) + , m_geometry(g) + , m_strategy(strategy) + {} + + template <typename Range> + inline void apply(Range const& range) + { + typename geometry::point_type<Range>::type p; + geometry::point_on_border(p, range); + if ( !has_within && geometry::within(p, m_geometry, m_strategy) ) + { + has_within = true; + } + } +}; + +template <typename FirstGeometry, typename SecondGeometry, typename IntersectionStrategy> +inline bool rings_containing(FirstGeometry const& geometry1, + SecondGeometry const& geometry2, + IntersectionStrategy const& strategy) +{ + // NOTE: This strategy could be defined inside IntersectionStrategy + typedef typename IntersectionStrategy::template point_in_geometry_strategy + < + FirstGeometry, SecondGeometry + >::type point_in_ring_strategy_type; + + point_in_ring_strategy_type point_in_ring_strategy + = strategy.template get_point_in_geometry_strategy<FirstGeometry, SecondGeometry>(); + + check_each_ring_for_within + < + FirstGeometry, point_in_ring_strategy_type + > checker(geometry1, point_in_ring_strategy); + geometry::detail::for_each_range(geometry2, checker); + return checker.has_within; +} + +template <typename Geometry1, typename Geometry2> +struct areal_areal +{ + template <typename IntersectionStrategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + IntersectionStrategy const& strategy) + { + typedef detail::no_rescale_policy rescale_policy_type; + typedef typename geometry::point_type<Geometry1>::type point_type; + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type<point_type, rescale_policy_type>::type + > turn_info; + + std::deque<turn_info> turns; + detail::touches::areal_interrupt_policy policy; + rescale_policy_type robust_policy; + boost::geometry::get_turns + < + detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, + detail::overlay::assign_null_policy + >(geometry1, geometry2, strategy, robust_policy, turns, policy); + + return policy.result() + && ! geometry::detail::touches::rings_containing(geometry1, geometry2, strategy) + && ! geometry::detail::touches::rings_containing(geometry2, geometry1, strategy); + } +}; + +// P/* + +struct use_point_in_geometry +{ + template <typename Point, typename Geometry, typename Strategy> + static inline bool apply(Point const& point, Geometry const& geometry, Strategy const& strategy) + { + return detail::within::point_in_geometry(point, geometry, strategy) == 0; + } +}; + + +}} +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch { + +// P/P + +template <typename Geometry1, typename Geometry2, typename Tag2> +struct touches<Geometry1, Geometry2, point_tag, Tag2, pointlike_tag, pointlike_tag, false> +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& , Geometry2 const& , Strategy const&) + { + return false; + } +}; + +template <typename Geometry1, typename Geometry2, typename Tag2> +struct touches<Geometry1, Geometry2, multi_point_tag, Tag2, pointlike_tag, pointlike_tag, false> +{ + template <typename Strategy> + static inline bool apply(Geometry1 const&, Geometry2 const&, Strategy const&) + { + return false; + } +}; + +// P/* + +template <typename Point, typename Geometry, typename Tag2, typename CastedTag2> +struct touches<Point, Geometry, point_tag, Tag2, pointlike_tag, CastedTag2, false> + : detail::touches::use_point_in_geometry +{}; + +template <typename MultiPoint, typename MultiGeometry, typename Tag2, typename CastedTag2> +struct touches<MultiPoint, MultiGeometry, multi_point_tag, Tag2, pointlike_tag, CastedTag2, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + MultiPoint, + MultiGeometry + > +{}; + +template <typename Geometry, typename MultiPoint, typename Tag1, typename CastedTag1> +struct touches<Geometry, MultiPoint, Tag1, multi_point_tag, CastedTag1, pointlike_tag, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + Geometry, + MultiPoint + > +{}; + +// Box/Box + +template <typename Box1, typename Box2, typename CastedTag1, typename CastedTag2> +struct touches<Box1, Box2, box_tag, box_tag, CastedTag1, CastedTag2, false> + : detail::touches::box_box +{}; + +template <typename Box1, typename Box2> +struct touches<Box1, Box2, box_tag, box_tag, areal_tag, areal_tag, false> + : detail::touches::box_box +{}; + +// L/L + +template <typename Linear1, typename Linear2, typename Tag1, typename Tag2> +struct touches<Linear1, Linear2, Tag1, Tag2, linear_tag, linear_tag, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + Linear1, + Linear2 + > +{}; + +// L/A + +template <typename Linear, typename Areal, typename Tag1, typename Tag2> +struct touches<Linear, Areal, Tag1, Tag2, linear_tag, areal_tag, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + Linear, + Areal + > +{}; + +// A/L +template <typename Linear, typename Areal, typename Tag1, typename Tag2> +struct touches<Areal, Linear, Tag1, Tag2, areal_tag, linear_tag, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + Areal, + Linear + > +{}; + +// A/A + +template <typename Areal1, typename Areal2, typename Tag1, typename Tag2> +struct touches<Areal1, Areal2, Tag1, Tag2, areal_tag, areal_tag, false> + : detail::relate::relate_impl + < + detail::de9im::static_mask_touches_type, + Areal1, + Areal2 + > +{}; + +template <typename Areal1, typename Areal2> +struct touches<Areal1, Areal2, ring_tag, ring_tag, areal_tag, areal_tag, false> + : detail::touches::areal_areal<Areal1, Areal2> +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant { + +template <typename Geometry> +struct self_touches +{ + static bool apply(Geometry const& geometry) + { + concepts::check<Geometry const>(); + + typedef typename strategy::relate::services::default_strategy + < + Geometry, Geometry + >::type strategy_type; + typedef detail::no_rescale_policy rescale_policy_type; + typedef typename geometry::point_type<Geometry>::type point_type; + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type<point_type, rescale_policy_type>::type + > turn_info; + + typedef detail::overlay::get_turn_info + < + detail::overlay::assign_null_policy + > policy_type; + + std::deque<turn_info> turns; + detail::touches::areal_interrupt_policy policy; + strategy_type strategy; + rescale_policy_type robust_policy; + detail::self_get_turn_points::get_turns + < + false, policy_type + >::apply(geometry, strategy, robust_policy, turns, policy, 0); + + return policy.result(); + } +}; + +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_TOUCHES_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/touches/interface.hpp b/boost/geometry/algorithms/detail/touches/interface.hpp new file mode 100644 index 0000000000..d2e0cc8c4e --- /dev/null +++ b/boost/geometry/algorithms/detail/touches/interface.hpp @@ -0,0 +1,321 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2015, 2017. +// Modifications copyright (c) 2013-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_TOUCHES_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_TOUCHES_INTERFACE_HPP + + +#include <deque> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + +#include <boost/geometry/core/reverse_dispatch.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/strategies/default_strategy.hpp> +#include <boost/geometry/strategies/relate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch { + +// TODO: Since CastedTags are used is Reverse needed? + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type, + typename CastedTag1 = typename tag_cast<Tag1, pointlike_tag, linear_tag, areal_tag>::type, + typename CastedTag2 = typename tag_cast<Tag2, pointlike_tag, linear_tag, areal_tag>::type, + bool Reverse = reverse_dispatch<Geometry1, Geometry2>::type::value +> +struct touches + : not_implemented<Tag1, Tag2> +{}; + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, + typename Tag1, typename Tag2, + typename CastedTag1, typename CastedTag2 +> +struct touches<Geometry1, Geometry2, Tag1, Tag2, CastedTag1, CastedTag2, true> + : touches<Geometry2, Geometry1, Tag2, Tag1, CastedTag2, CastedTag1, false> +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2, Strategy const& strategy) + { + return touches<Geometry2, Geometry1>::apply(g2, g1, strategy); + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy +{ + +struct touches +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return dispatch::touches + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy); + } + + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename strategy::relate::services::default_strategy + < + Geometry1, + Geometry2 + >::type strategy_type; + + return dispatch::touches + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant { + +template <typename Geometry1, typename Geometry2> +struct touches +{ + template <typename Strategy> + static bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + + return resolve_strategy::touches::apply(geometry1, geometry2, strategy); + } +}; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T), typename Geometry2> +struct touches<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Geometry2> +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, Strategy const& strategy) + : m_geometry2(geometry2) + , m_strategy(strategy) + {} + + template <typename Geometry1> + bool operator()(Geometry1 const& geometry1) const + { + return touches<Geometry1, Geometry2>::apply(geometry1, m_geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry2, strategy), geometry1); + } +}; + +template <typename Geometry1, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct touches<Geometry1, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, Strategy const& strategy) + : m_geometry1(geometry1) + , m_strategy(strategy) + {} + + template <typename Geometry2> + bool operator()(Geometry2 const& geometry2) const + { + return touches<Geometry1, Geometry2>::apply(m_geometry1, geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry1, strategy), geometry2); + } +}; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2)> +struct touches<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) + : m_strategy(strategy) + {} + + template <typename Geometry1, typename Geometry2> + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return touches<Geometry1, Geometry2>::apply(geometry1, geometry2, m_strategy); + } + }; + + template <typename Strategy> + static inline bool apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(strategy), geometry1, geometry2); + } +}; + +template <typename Geometry> +struct self_touches; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct self_touches<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > +{ + struct visitor: boost::static_visitor<bool> + { + template <typename Geometry> + bool operator()(Geometry const& geometry) const + { + return self_touches<Geometry>::apply(geometry); + } + }; + + static inline bool + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry) + { + return boost::apply_visitor(visitor(), geometry); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_check{has at least one touching point (self-tangency)} +\note This function can be called for one geometry (self-tangency) and + also for two geometries (touch) +\ingroup touches +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is self-touching} + +\qbk{distinguish,one geometry} +\qbk{[def __one_parameter__]} +\qbk{[include reference/algorithms/touches.qbk]} +*/ +template <typename Geometry> +inline bool touches(Geometry const& geometry) +{ + return resolve_variant::self_touches<Geometry>::apply(geometry); +} + + +/*! +\brief \brief_check2{have at least one touching point (tangent - non overlapping)} +\ingroup touches +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{touch each other} + +\qbk{distinguish,two geometries} +\qbk{[include reference/algorithms/touches.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline bool touches(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + return resolve_variant::touches + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, default_strategy()); +} + +/*! +\brief \brief_check2{have at least one touching point (tangent - non overlapping)} +\ingroup touches +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Touches} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{touches} +\return \return_check2{touch each other} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/touches.qbk]} + */ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline bool touches(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + return resolve_variant::touches + < + Geometry1, Geometry2 + >::apply(geometry1, geometry2, strategy); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_TOUCHES_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/within/implementation.hpp b/boost/geometry/algorithms/detail/within/implementation.hpp new file mode 100644 index 0000000000..8f1eba62e6 --- /dev/null +++ b/boost/geometry/algorithms/detail/within/implementation.hpp @@ -0,0 +1,306 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013, 2014, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_WITHIN_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_IMPLEMENTATION_HPP + + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/within/interface.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/order_as_direction.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + +#include <boost/geometry/algorithms/detail/within/multi_point.hpp> +#include <boost/geometry/algorithms/detail/within/point_in_geometry.hpp> +#include <boost/geometry/algorithms/relate.hpp> + +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp> +#include <deque> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace within { + +struct use_point_in_geometry +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + return detail::within::point_in_geometry(geometry1, geometry2, strategy) == 1; + } +}; + +struct use_relate +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + typedef typename detail::de9im::static_mask_within_type + < + Geometry1, Geometry2 + >::type within_mask; + return geometry::relate(geometry1, geometry2, within_mask(), strategy); + } +}; + +}} // namespace detail::within +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename Point, typename Box> +struct within<Point, Box, point_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Box const& box, Strategy const& strategy) + { + boost::ignore_unused_variable_warning(strategy); + return strategy.apply(point, box); + } +}; + +template <typename Box1, typename Box2> +struct within<Box1, Box2, box_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + assert_dimension_equal<Box1, Box2>(); + boost::ignore_unused_variable_warning(strategy); + return strategy.apply(box1, box2); + } +}; + +// P/P + +template <typename Point1, typename Point2> +struct within<Point1, Point2, point_tag, point_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename Point, typename MultiPoint> +struct within<Point, MultiPoint, point_tag, multi_point_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Point> +struct within<MultiPoint, Point, multi_point_tag, point_tag> + : public detail::within::multi_point_point +{}; + +template <typename MultiPoint1, typename MultiPoint2> +struct within<MultiPoint1, MultiPoint2, multi_point_tag, multi_point_tag> + : public detail::within::multi_point_multi_point +{}; + +// P/L + +template <typename Point, typename Segment> +struct within<Point, Segment, point_tag, segment_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename Point, typename Linestring> +struct within<Point, Linestring, point_tag, linestring_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename Point, typename MultiLinestring> +struct within<Point, MultiLinestring, point_tag, multi_linestring_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Segment> +struct within<MultiPoint, Segment, multi_point_tag, segment_tag> + : public detail::within::multi_point_single_geometry<true> +{}; + +template <typename MultiPoint, typename Linestring> +struct within<MultiPoint, Linestring, multi_point_tag, linestring_tag> + : public detail::within::multi_point_single_geometry<true> +{}; + +template <typename MultiPoint, typename MultiLinestring> +struct within<MultiPoint, MultiLinestring, multi_point_tag, multi_linestring_tag> + : public detail::within::multi_point_multi_geometry<true> +{}; + +// P/A + +template <typename Point, typename Ring> +struct within<Point, Ring, point_tag, ring_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename Point, typename Polygon> +struct within<Point, Polygon, point_tag, polygon_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename Point, typename MultiPolygon> +struct within<Point, MultiPolygon, point_tag, multi_polygon_tag> + : public detail::within::use_point_in_geometry +{}; + +template <typename MultiPoint, typename Ring> +struct within<MultiPoint, Ring, multi_point_tag, ring_tag> + : public detail::within::multi_point_single_geometry<true> +{}; + +template <typename MultiPoint, typename Polygon> +struct within<MultiPoint, Polygon, multi_point_tag, polygon_tag> + : public detail::within::multi_point_single_geometry<true> +{}; + +template <typename MultiPoint, typename MultiPolygon> +struct within<MultiPoint, MultiPolygon, multi_point_tag, multi_polygon_tag> + : public detail::within::multi_point_multi_geometry<true> +{}; + +// L/L + +template <typename Linestring1, typename Linestring2> +struct within<Linestring1, Linestring2, linestring_tag, linestring_tag> + : public detail::within::use_relate +{}; + +template <typename Linestring, typename MultiLinestring> +struct within<Linestring, MultiLinestring, linestring_tag, multi_linestring_tag> + : public detail::within::use_relate +{}; + +template <typename MultiLinestring, typename Linestring> +struct within<MultiLinestring, Linestring, multi_linestring_tag, linestring_tag> + : public detail::within::use_relate +{}; + +template <typename MultiLinestring1, typename MultiLinestring2> +struct within<MultiLinestring1, MultiLinestring2, multi_linestring_tag, multi_linestring_tag> + : public detail::within::use_relate +{}; + +// L/A + +template <typename Linestring, typename Ring> +struct within<Linestring, Ring, linestring_tag, ring_tag> + : public detail::within::use_relate +{}; + +template <typename MultiLinestring, typename Ring> +struct within<MultiLinestring, Ring, multi_linestring_tag, ring_tag> + : public detail::within::use_relate +{}; + +template <typename Linestring, typename Polygon> +struct within<Linestring, Polygon, linestring_tag, polygon_tag> + : public detail::within::use_relate +{}; + +template <typename MultiLinestring, typename Polygon> +struct within<MultiLinestring, Polygon, multi_linestring_tag, polygon_tag> + : public detail::within::use_relate +{}; + +template <typename Linestring, typename MultiPolygon> +struct within<Linestring, MultiPolygon, linestring_tag, multi_polygon_tag> + : public detail::within::use_relate +{}; + +template <typename MultiLinestring, typename MultiPolygon> +struct within<MultiLinestring, MultiPolygon, multi_linestring_tag, multi_polygon_tag> + : public detail::within::use_relate +{}; + +// A/A + +template <typename Ring1, typename Ring2> +struct within<Ring1, Ring2, ring_tag, ring_tag> + : public detail::within::use_relate +{}; + +template <typename Ring, typename Polygon> +struct within<Ring, Polygon, ring_tag, polygon_tag> + : public detail::within::use_relate +{}; + +template <typename Polygon, typename Ring> +struct within<Polygon, Ring, polygon_tag, ring_tag> + : public detail::within::use_relate +{}; + +template <typename Polygon1, typename Polygon2> +struct within<Polygon1, Polygon2, polygon_tag, polygon_tag> + : public detail::within::use_relate +{}; + +template <typename Ring, typename MultiPolygon> +struct within<Ring, MultiPolygon, ring_tag, multi_polygon_tag> + : public detail::within::use_relate +{}; + +template <typename MultiPolygon, typename Ring> +struct within<MultiPolygon, Ring, multi_polygon_tag, ring_tag> + : public detail::within::use_relate +{}; + +template <typename Polygon, typename MultiPolygon> +struct within<Polygon, MultiPolygon, polygon_tag, multi_polygon_tag> + : public detail::within::use_relate +{}; + +template <typename MultiPolygon, typename Polygon> +struct within<MultiPolygon, Polygon, multi_polygon_tag, polygon_tag> + : public detail::within::use_relate +{}; + +template <typename MultiPolygon1, typename MultiPolygon2> +struct within<MultiPolygon1, MultiPolygon2, multi_polygon_tag, multi_polygon_tag> + : public detail::within::use_relate +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#include <boost/geometry/index/rtree.hpp> + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/within/interface.hpp b/boost/geometry/algorithms/detail/within/interface.hpp new file mode 100644 index 0000000000..23263604c2 --- /dev/null +++ b/boost/geometry/algorithms/detail/within/interface.hpp @@ -0,0 +1,304 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2013, 2014, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_WITHIN_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_INTERFACE_HPP + + +#include <boost/concept_check.hpp> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/concepts/within_concept.hpp> +#include <boost/geometry/strategies/default_strategy.hpp> +#include <boost/geometry/strategies/within.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type +> +struct within + : not_implemented<Tag1, Tag2> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy +{ + +struct within +{ + template <typename Geometry1, typename Geometry2, typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + concepts::within::check + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + Strategy + >(); + + return dispatch::within<Geometry1, Geometry2>::apply(geometry1, geometry2, strategy); + } + + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename strategy::within::services::default_strategy + < + Geometry1, + Geometry2 + >::type strategy_type; + + return apply(geometry1, geometry2, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant +{ + +template <typename Geometry1, typename Geometry2> +struct within +{ + template <typename Strategy> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + concepts::check<Geometry1 const>(); + concepts::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + return resolve_strategy::within::apply(geometry1, + geometry2, + strategy); + } +}; + +template <BOOST_VARIANT_ENUM_PARAMS(typename T), typename Geometry2> +struct within<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Geometry2> +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, Strategy const& strategy) + : m_geometry2(geometry2) + , m_strategy(strategy) + {} + + template <typename Geometry1> + bool operator()(Geometry1 const& geometry1) const + { + return within<Geometry1, Geometry2>::apply(geometry1, + m_geometry2, + m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry2, strategy), + geometry1); + } +}; + +template <typename Geometry1, BOOST_VARIANT_ENUM_PARAMS(typename T)> +struct within<Geometry1, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, Strategy const& strategy) + : m_geometry1(geometry1) + , m_strategy(strategy) + {} + + template <typename Geometry2> + bool operator()(Geometry2 const& geometry2) const + { + return within<Geometry1, Geometry2>::apply(m_geometry1, + geometry2, + m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(Geometry1 const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(geometry1, strategy), + geometry2 + ); + } +}; + +template < + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) +> +struct within< + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> +> +{ + template <typename Strategy> + struct visitor: boost::static_visitor<bool> + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + + template <typename Geometry1, typename Geometry2> + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return within<Geometry1, Geometry2>::apply(geometry1, + geometry2, + m_strategy); + } + }; + + template <typename Strategy> + static inline bool + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor<Strategy>(strategy), + geometry1, + geometry2); + } +}; + +} + + +/*! +\brief \brief_check12{is completely inside} +\ingroup within +\details \details_check12{within, is completely inside}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be within the second geometry +\param geometry2 \param_geometry which might contain the first geometry +\return true if geometry1 is completely contained within geometry2, + else false +\note The default strategy is used for within detection + + +\qbk{[include reference/algorithms/within.qbk]} + +\qbk{ +[heading Example] +[within] +[within_output] +} + */ +template<typename Geometry1, typename Geometry2> +inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + return resolve_variant::within + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, default_strategy()); +} + +/*! +\brief \brief_check12{is completely inside} \brief_strategy +\ingroup within +\details \details_check12{within, is completely inside}, \brief_strategy. \details_strategy_reasons +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be within the second geometry +\param geometry2 \param_geometry which might contain the first geometry +\param strategy strategy to be used +\return true if geometry1 is completely contained within geometry2, + else false + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/within.qbk]} +\qbk{ +[heading Available Strategies] +\* [link geometry.reference.strategies.strategy_within_winding Winding (coordinate system agnostic)] +\* [link geometry.reference.strategies.strategy_within_franklin Franklin (cartesian)] +\* [link geometry.reference.strategies.strategy_within_crossings_multiply Crossings Multiply (cartesian)] + +[heading Example] +[within_strategy] +[within_strategy_output] + +} +*/ +template<typename Geometry1, typename Geometry2, typename Strategy> +inline bool within(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + return resolve_variant::within + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/within/multi_point.hpp b/boost/geometry/algorithms/detail/within/multi_point.hpp new file mode 100644 index 0000000000..7e85f33383 --- /dev/null +++ b/boost/geometry/algorithms/detail/within/multi_point.hpp @@ -0,0 +1,268 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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_WITHIN_MULTI_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP + + +#include <algorithm> +#include <vector> + +#include <boost/range.hpp> +#include <boost/type_traits/is_same.hpp> + +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> +#include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp> +#include <boost/geometry/algorithms/detail/relate/less.hpp> +#include <boost/geometry/algorithms/detail/within/point_in_geometry.hpp> +#include <boost/geometry/algorithms/envelope.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/box.hpp> + +#include <boost/geometry/index/rtree.hpp> + +#include <boost/geometry/strategies/covered_by.hpp> +#include <boost/geometry/strategies/disjoint.hpp> + + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace within { + +struct multi_point_point +{ + template <typename MultiPoint, typename Point, typename Strategy> + static inline bool apply(MultiPoint const& multi_point, + Point const& point, + Strategy const& strategy) + { + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + if (! strategy.apply(*it, point)) + { + return false; + } + } + + // all points of MultiPoint inside Point + return true; + } +}; + +// NOTE: currently the strategy is ignored, math::equals() is used inside relate::less +struct multi_point_multi_point +{ + template <typename MultiPoint1, typename MultiPoint2, typename Strategy> + static inline bool apply(MultiPoint1 const& multi_point1, + MultiPoint2 const& multi_point2, + Strategy const& /*strategy*/) + { + typedef typename boost::range_value<MultiPoint2>::type point2_type; + + relate::less const less = relate::less(); + + std::vector<point2_type> points2(boost::begin(multi_point2), boost::end(multi_point2)); + std::sort(points2.begin(), points2.end(), less); + + bool result = false; + + typedef typename boost::range_const_iterator<MultiPoint1>::type iterator; + for ( iterator it = boost::begin(multi_point1) ; it != boost::end(multi_point1) ; ++it ) + { + if (! std::binary_search(points2.begin(), points2.end(), *it, less)) + { + return false; + } + else + { + result = true; + } + } + + return result; + } +}; + + +// TODO: the complexity could be lesser +// the second geometry could be "prepared"/sorted +// For Linear geometries partition could be used +// For Areal geometries point_in_geometry() would have to call the winding +// strategy differently, currently it linearly calls the strategy for each +// segment. So the segments would have to be sorted in a way consistent with +// the strategy and then the strategy called only for the segments in range. +template <bool Within> +struct multi_point_single_geometry +{ + template <typename MultiPoint, typename LinearOrAreal, typename Strategy> + static inline bool apply(MultiPoint const& multi_point, + LinearOrAreal const& linear_or_areal, + Strategy const& strategy) + { + typedef typename boost::range_value<MultiPoint>::type point1_type; + typedef typename point_type<LinearOrAreal>::type point2_type; + typedef model::box<point2_type> box2_type; + + // Create envelope of geometry + box2_type box; + geometry::envelope(linear_or_areal, box, strategy.get_envelope_strategy()); + geometry::detail::expand_by_epsilon(box); + + typedef typename strategy::covered_by::services::default_strategy + < + point1_type, box2_type + >::type point_in_box_type; + + // Test each Point with envelope and then geometry if needed + // If in the exterior, break + bool result = false; + + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + int in_val = 0; + + // exterior of box and of geometry + if (! point_in_box_type::apply(*it, box) + || (in_val = point_in_geometry(*it, linear_or_areal, strategy)) < 0) + { + result = false; + break; + } + + // interior : interior/boundary + if (Within ? in_val > 0 : in_val >= 0) + { + result = true; + } + } + + return result; + } +}; + + +// TODO: same here, probably the complexity could be lesser +template <bool Within> +struct multi_point_multi_geometry +{ + template <typename MultiPoint, typename LinearOrAreal, typename Strategy> + static inline bool apply(MultiPoint const& multi_point, + LinearOrAreal const& linear_or_areal, + Strategy const& strategy) + { + typedef typename point_type<LinearOrAreal>::type point2_type; + typedef model::box<point2_type> box2_type; + static const bool is_linear = is_same + < + typename tag_cast + < + typename tag<LinearOrAreal>::type, + linear_tag + >::type, + linear_tag + >::value; + + typename Strategy::envelope_strategy_type const + envelope_strategy = strategy.get_envelope_strategy(); + + // TODO: box pairs could be constructed on the fly, inside the rtree + + // Prepare range of envelopes and ids + std::size_t count2 = boost::size(linear_or_areal); + typedef std::pair<box2_type, std::size_t> box_pair_type; + typedef std::vector<box_pair_type> box_pair_vector; + box_pair_vector boxes(count2); + for (std::size_t i = 0 ; i < count2 ; ++i) + { + geometry::envelope(linear_or_areal, boxes[i].first, envelope_strategy); + geometry::detail::expand_by_epsilon(boxes[i].first); + boxes[i].second = i; + } + + // Create R-tree + index::rtree<box_pair_type, index::rstar<4> > rtree(boxes.begin(), boxes.end()); + + // For each point find overlapping envelopes and test corresponding single geometries + // If a point is in the exterior break + bool result = false; + + typedef typename boost::range_const_iterator<MultiPoint>::type iterator; + for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) + { + // TODO: investigate the possibility of using satisfies + // TODO: investigate the possibility of using iterative queries (optimization below) + box_pair_vector inters_boxes; + rtree.query(index::intersects(*it), std::back_inserter(inters_boxes)); + + bool found_interior = false; + bool found_boundary = false; + int boundaries = 0; + + typedef typename box_pair_vector::const_iterator iterator; + for ( iterator box_it = inters_boxes.begin() ; box_it != inters_boxes.end() ; ++box_it ) + { + int in_val = point_in_geometry(*it, range::at(linear_or_areal, box_it->second), strategy); + + if (in_val > 0) + found_interior = true; + else if (in_val == 0) + ++boundaries; + + // If the result was set previously (interior or + // interior/boundary found) the only thing that needs to be + // done for other points is to make sure they're not + // overlapping the exterior no need to analyse boundaries. + if (result && in_val >= 0) + { + break; + } + } + + if ( boundaries > 0) + { + if (is_linear && boundaries % 2 == 0) + found_interior = true; + else + found_boundary = true; + } + + // exterior + if (! found_interior && ! found_boundary) + { + result = false; + break; + } + + // interior : interior/boundary + if (Within ? found_interior : (found_interior || found_boundary)) + { + result = true; + } + } + + return result; + } +}; + +}} // namespace detail::within +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP |