From 0ad05bfef445d1a29db16f1f03498439fa20010a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 31 May 2022 18:58:35 +0200 Subject: [PATCH] [algorithms] Add support for GC in union. --- include/boost/geometry/algorithms/union.hpp | 562 ++++++++++++++++---- 1 file changed, 448 insertions(+), 114 deletions(-) diff --git a/include/boost/geometry/algorithms/union.hpp b/include/boost/geometry/algorithms/union.hpp index 1a9bcd64d..cf3ff872a 100644 --- a/include/boost/geometry/algorithms/union.hpp +++ b/include/boost/geometry/algorithms/union.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014-2021. -// Modifications copyright (c) 2014-2021 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2022. +// Modifications copyright (c) 2014-2022 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 @@ -18,7 +18,9 @@ #include +#include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +37,9 @@ #include #include #include - +#include +#include +#include namespace boost { namespace geometry @@ -375,28 +380,133 @@ inline OutputIterator union_insert(Geometry1 const& geometry1, } +struct gc_element_id +{ + gc_element_id(unsigned int source_id_, std::size_t gc_id_) + : source_id(source_id_), gc_id(gc_id_) + {} + + unsigned int source_id; + std::size_t gc_id; + + friend bool operator<(gc_element_id const& left, gc_element_id const& right) + { + return left.source_id < right.source_id + || (left.source_id == right.source_id && left.gc_id < right.gc_id); + } +}; + +template +inline void gc_group_elements(GC1View const& gc1_view, GC2View const& gc2_view, Strategy const& strategy, + IntersectingFun&& intersecting_fun, + DisjointFun&& disjoint_fun) +{ + // NOTE: could be replaced with unordered_map and unordered_set + std::map> adjacent; + + auto const rtree2 = detail::make_rtree_indexes(gc2_view, strategy); + // Create adjacency list based on intersecting envelopes of GC elements + for (std::size_t i = 0; i < boost::size(gc1_view); ++i) + { + traits::iter_visit::apply([&](auto const& g1) + { + using g1_t = util::remove_cref_t; + using box1_t = detail::make_rtree_box_t; + box1_t b1 = geometry::return_envelope(g1, strategy); + detail::expand_by_epsilon(b1); + + gc_element_id id1 = {0, i}; + for (auto qit = rtree2.qbegin(index::intersects(b1)); qit != rtree2.qend(); ++qit) + { + gc_element_id id2 = {1, qit->second}; + adjacent[id1].insert(id2); + adjacent[id2].insert(id1); + } + }, boost::begin(gc1_view) + i); + } + + // Traverse the graph and build connected groups i.e. groups of intersecting envelopes + std::deque queue; + std::array, 2> visited = { + std::vector(boost::size(gc1_view), false), + std::vector(boost::size(gc2_view), false) + }; + for (auto const& elem : adjacent) + { + std::vector group; + if (! visited[elem.first.source_id][elem.first.gc_id]) + { + queue.push_back(elem.first); + visited[elem.first.source_id][elem.first.gc_id] = true; + group.push_back(elem.first); + while (! queue.empty()) + { + gc_element_id e = queue.front(); + queue.pop_front(); + for (auto const& n : adjacent[e]) + { + if (! visited[n.source_id][n.gc_id]) + { + queue.push_back(n); + visited[n.source_id][n.gc_id] = true; + group.push_back(n); + } + } + } + } + if (! group.empty()) + { + intersecting_fun(group); + } + } + + { + std::vector group; + for (std::size_t i = 0; i < visited[0].size(); ++i) + { + if (! visited[0][i]) + { + group.emplace_back(0, i); + } + } + for (std::size_t i = 0; i < visited[1].size(); ++i) + { + if (! visited[1][i]) + { + group.emplace_back(1, i); + } + } + if (! group.empty()) + { + disjoint_fun(group); + } + } +} + + }} // namespace detail::union_ #endif // DOXYGEN_NO_DETAIL -namespace resolve_strategy { +namespace resolve_collection +{ template < - typename Strategy, - bool IsUmbrella = strategies::detail::is_umbrella_strategy::value + typename Geometry1, typename Geometry2, typename GeometryOut, + typename Tag1 = typename geometry::tag::type, + typename Tag2 = typename geometry::tag::type, + typename TagOut = typename geometry::tag::type > struct union_ { - template - static inline void apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, - Collection & output_collection, - Strategy const& strategy) + template + static void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, + GeometryOut & geometry_out, Strategy const& strategy) { typedef typename geometry::detail::output_geometry_value < - Collection + GeometryOut >::type single_out; typedef typename geometry::rescale_overlay_policy_type @@ -414,11 +524,300 @@ struct union_ < Geometry1, Geometry2, single_out >::apply(geometry1, geometry2, robust_policy, - geometry::detail::output_geometry_back_inserter(output_collection), + geometry::detail::output_geometry_back_inserter(geometry_out), strategy); } }; +template +< + typename Geometry1, typename Geometry2, typename GeometryOut +> +struct union_ + < + Geometry1, Geometry2, GeometryOut, + geometry_collection_tag, geometry_collection_tag, geometry_collection_tag + > +{ + // NOTE: for now require all of the possible output types + // technically only a subset could be needed. + using multi_point_t = typename util::sequence_find_if + < + typename traits::geometry_types::type, + util::is_multi_point + >::type; + using multi_linestring_t = typename util::sequence_find_if + < + typename traits::geometry_types::type, + util::is_multi_linestring + >::type; + using multi_polygon_t = typename util::sequence_find_if + < + typename traits::geometry_types::type, + util::is_multi_polygon + >::type; + using tuple_out_t = boost::tuple; + + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + detail::random_access_view gc1_view(geometry1); + detail::random_access_view gc2_view(geometry2); + + detail::union_::gc_group_elements(gc1_view, gc2_view, strategy, + [&](auto const& inters_group) + { + tuple_out_t out; + merge_group(gc1_view, gc2_view, strategy, inters_group, out); + detail::intersection::gc_move_multi_back(geometry_out, boost::get<0>(out)); + detail::intersection::gc_move_multi_back(geometry_out, boost::get<1>(out)); + detail::intersection::gc_move_multi_back(geometry_out, boost::get<2>(out)); + }, + [&](auto const& disjoint_group) + { + copy_disjoint(gc1_view, gc2_view, disjoint_group, geometry_out); + }); + } + +private: + template + static inline void merge_group(GC1View const& gc1_view, GC2View const& gc2_view, + Strategy const& strategy, Group const& inters_group, + tuple_out_t& out) + { + for (auto const& id : inters_group) + { + if (id.source_id == 0) + { + traits::iter_visit::apply([&](auto const& g1) + { + merge_one(out, g1, strategy); + }, boost::begin(gc1_view) + id.gc_id); + } + else + { + traits::iter_visit::apply([&](auto const& g2) + { + merge_one(out, g2, strategy); + }, boost::begin(gc2_view) + id.gc_id); + } + } + + // L = L \ A + { + multi_linestring_t l; + subtract_greater_topodim(boost::get<1>(out), boost::get<2>(out), l, strategy); + boost::get<1>(out) = std::move(l); + } + // P = P \ A + { + multi_point_t p; + subtract_greater_topodim(boost::get<0>(out), boost::get<2>(out), p, strategy); + boost::get<0>(out) = std::move(p); + } + // P = P \ L + { + multi_point_t p; + subtract_greater_topodim(boost::get<0>(out), boost::get<1>(out), p, strategy); + boost::get<0>(out) = std::move(p); + } + } + + template ::value, int> = 0> + static inline void merge_one(tuple_out_t& out, G const& g, Strategy const& strategy) + { + multi_point_t p; + union_::apply(boost::get<0>(out), g, p, strategy); + boost::get<0>(out) = std::move(p); + } + + template ::value, int> = 0> + static inline void merge_one(tuple_out_t& out, G const& g, Strategy const& strategy) + { + multi_linestring_t l; + union_::apply(boost::get<1>(out), g, l, strategy); + boost::get<1>(out) = std::move(l); + } + + template ::value, int> = 0> + static inline void merge_one(tuple_out_t& out, G const& g, Strategy const& strategy) + { + multi_polygon_t a; + union_::apply(boost::get<2>(out), g, a, strategy); + boost::get<2>(out) = std::move(a); + } + + template + static inline void copy_disjoint(GC1View const& gc1_view, GC2View const& gc2_view, + Group const& disjoint_group, GeometryOut& geometry_out) + { + for (auto const& id : disjoint_group) + { + if (id.source_id == 0) + { + traits::iter_visit::apply([&](auto const& g1) + { + copy_one(g1, geometry_out); + }, boost::begin(gc1_view) + id.gc_id); + } + else + { + traits::iter_visit::apply([&](auto const& g2) + { + copy_one(g2, geometry_out); + }, boost::begin(gc2_view) + id.gc_id); + } + } + } + + template ::value, int> = 0> + static inline void copy_one(G const& g, GeometryOut& geometry_out) + { + multi_point_t p; + geometry::convert(g, p); + detail::intersection::gc_move_multi_back(geometry_out, p); + } + + template ::value, int> = 0> + static inline void copy_one(G const& g, GeometryOut& geometry_out) + { + multi_linestring_t l; + geometry::convert(g, l); + detail::intersection::gc_move_multi_back(geometry_out, l); + } + + template ::value, int> = 0> + static inline void copy_one(G const& g, GeometryOut& geometry_out) + { + multi_polygon_t a; + geometry::convert(g, a); + detail::intersection::gc_move_multi_back(geometry_out, a); + } + + template + static inline void subtract_greater_topodim(Multi1 const& multi1, Multi2 const& multi2, Multi1& multi_out, Strategy const& strategy) + { + using rescale_policy_type = typename geometry::rescale_overlay_policy_type + < + Multi1, Multi2 + >::type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy( + multi1, multi2, strategy); + + geometry::dispatch::intersection_insert + < + Multi1, Multi2, + typename boost::range_value::type, + overlay_difference, + geometry::detail::overlay::do_reverse::value>::value, + geometry::detail::overlay::do_reverse::value, true>::value + >::apply(multi1, multi2, robust_policy, range::back_inserter(multi_out), strategy); + } +}; + +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, typename Tag1 +> +struct union_ + < + Geometry1, Geometry2, GeometryOut, + Tag1, geometry_collection_tag, geometry_collection_tag + > +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + using gc_view_t = geometry::detail::geometry_collection_view; + union_ + < + gc_view_t, Geometry2, GeometryOut + >::apply(gc_view_t(geometry1), geometry2, geometry_out, strategy); + } +}; + +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, typename Tag2 +> +struct union_ + < + Geometry1, Geometry2, GeometryOut, + geometry_collection_tag, Tag2, geometry_collection_tag + > +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + using gc_view_t = geometry::detail::geometry_collection_view; + union_ + < + Geometry1, gc_view_t, GeometryOut + >::apply(geometry1, gc_view_t(geometry2), geometry_out, strategy); + } +}; + +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, typename Tag1, typename Tag2 +> +struct union_ + < + Geometry1, Geometry2, GeometryOut, + Tag1, Tag2, geometry_collection_tag + > +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + using gc1_view_t = geometry::detail::geometry_collection_view; + using gc2_view_t = geometry::detail::geometry_collection_view; + union_ + < + gc1_view_t, gc2_view_t, GeometryOut + >::apply(gc1_view_t(geometry1), gc2_view_t(geometry2), geometry_out, strategy); + } +}; + +} // namespace resolve_collection + + +namespace resolve_strategy { + +template +< + typename Strategy, + bool IsUmbrella = strategies::detail::is_umbrella_strategy::value +> +struct union_ +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection & output_collection, + Strategy const& strategy) + { + resolve_collection::union_ + < + Geometry1, Geometry2, Collection + >::apply(geometry1, geometry2, output_collection, strategy); + } +}; + template struct union_ { @@ -463,10 +862,15 @@ struct union_ } // resolve_strategy -namespace resolve_variant +namespace resolve_dynamic { -template +template +< + typename Geometry1, typename Geometry2, + typename Tag1 = typename geometry::tag::type, + typename Tag2 = typename geometry::tag::type +> struct union_ { template @@ -494,134 +898,64 @@ struct union_ }; -template -struct union_, Geometry2> +template +struct union_ { template - struct visitor: static_visitor<> + static inline void apply(DynamicGeometry1 const& geometry1, Geometry2 const& geometry2, + Collection& output_collection, Strategy const& strategy) { - Geometry2 const& m_geometry2; - Collection& m_output_collection; - Strategy const& m_strategy; - - visitor(Geometry2 const& geometry2, - Collection& output_collection, - Strategy const& strategy) - : m_geometry2(geometry2) - , m_output_collection(output_collection) - , m_strategy(strategy) - {} - - template - void operator()(Geometry1 const& geometry1) const + traits::visit::apply([&](auto const& g1) { union_ < - Geometry1, + util::remove_cref_t, Geometry2 - >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); - } - }; - - template - static inline void - apply(variant const& geometry1, - Geometry2 const& geometry2, - Collection& output_collection, - Strategy const& strategy) - { - boost::apply_visitor(visitor(geometry2, - output_collection, - strategy), - geometry1); + >::apply(g1, geometry2, output_collection, strategy); + }, geometry1); } }; -template -struct union_ > +template +struct union_ { template - struct visitor: static_visitor<> + static inline void apply(Geometry1 const& geometry1, DynamicGeometry2 const& geometry2, + Collection& output_collection, Strategy const& strategy) { - Geometry1 const& m_geometry1; - Collection& m_output_collection; - Strategy const& m_strategy; - - visitor(Geometry1 const& geometry1, - Collection& output_collection, - Strategy const& strategy) - : m_geometry1(geometry1) - , m_output_collection(output_collection) - , m_strategy(strategy) - {} - - template - void operator()(Geometry2 const& geometry2) const + traits::visit::apply([&](auto const& g2) { union_ < Geometry1, - Geometry2 - >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); - } - }; - - template - static inline void - apply(Geometry1 const& geometry1, - variant const& geometry2, - Collection& output_collection, - Strategy const& strategy) - { - boost::apply_visitor(visitor(geometry1, - output_collection, - strategy), - geometry2); + util::remove_cref_t + >::apply(geometry1, g2, output_collection, strategy); + }, geometry2); } }; -template -struct union_, variant > +template +struct union_ { template - struct visitor: static_visitor<> + static inline void apply(DynamicGeometry1 const& geometry1, DynamicGeometry2 const& geometry2, + Collection& output_collection, Strategy const& strategy) { - Collection& m_output_collection; - Strategy const& m_strategy; - - visitor(Collection& output_collection, Strategy const& strategy) - : m_output_collection(output_collection) - , m_strategy(strategy) - {} - - template - void operator()(Geometry1 const& geometry1, - Geometry2 const& geometry2) const + traits::visit::apply([&](auto const& g1, auto const& g2) { union_ < - Geometry1, - Geometry2 - >::apply(geometry1, geometry2, m_output_collection, m_strategy); - } - }; - - template - static inline void - apply(variant const& geometry1, - variant const& geometry2, - Collection& output_collection, - Strategy const& strategy) - { - boost::apply_visitor(visitor(output_collection, - strategy), - geometry1, geometry2); + util::remove_cref_t, + util::remove_cref_t + >::apply(g1, g2, output_collection, strategy); + }, geometry1, geometry2); } }; + -} // namespace resolve_variant +} // namespace resolve_dynamic /*! @@ -654,7 +988,7 @@ inline void union_(Geometry1 const& geometry1, Collection& output_collection, Strategy const& strategy) { - resolve_variant::union_ + resolve_dynamic::union_ < Geometry1, Geometry2 @@ -687,7 +1021,7 @@ inline void union_(Geometry1 const& geometry1, Geometry2 const& geometry2, Collection& output_collection) { - resolve_variant::union_ + resolve_dynamic::union_ < Geometry1, Geometry2