// Boost.Geometry // Copyright (c) 2022, Oracle and/or its affiliates. // 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 #ifndef BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP #define BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace merge_elements { template using is_pla = util::bool_constant::value || util::is_linear::value || util::is_areal::value>; template struct are_areal : util::bool_constant::value && are_areal::value> {}; template struct are_areal : util::is_areal {}; template struct are_linear : util::bool_constant::value && are_linear::value> {}; template struct are_linear : util::is_linear {}; template struct are_pointlike : util::bool_constant::value && are_pointlike::value> {}; template struct are_pointlike : util::is_pointlike {}; template using are_same_kind = util::bool_constant::value || are_linear::value || are_pointlike::value>; template < typename Geometry, typename It, typename PointLike, typename Linear, typename Areal, std::enable_if_t::value, int> = 0 > inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear&, Areal& areal) { typename geometry::point_type::type point; if (geometry::point_on_border(point, geometry)) { using point_t = typename Areal::value_type::first_type; areal.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it); } } template < typename Geometry, typename It, typename PointLike, typename Linear, typename Areal, std::enable_if_t::value, int> = 0 > inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear& linear, Areal& ) { typename geometry::point_type::type point; if (geometry::point_on_border(point, geometry)) { using point_t = typename Linear::value_type::first_type; linear.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it); } } template < typename Geometry, typename It, typename PointLike, typename Linear, typename Areal, std::enable_if_t::value, int> = 0 > inline void distribute_element(Geometry const& geometry, It it, PointLike& pointlike, Linear& , Areal& ) { typename geometry::point_type::type point; if (geometry::point_on_border(point, geometry)) { using point_t = typename Linear::value_type::first_type; pointlike.emplace_back(point_t(geometry::get<0>(point), geometry::get<1>(point)), it); } } template < typename Geometry, typename It, typename PointLike, typename Linear, typename Areal, std::enable_if_t::value, int> = 0 > inline void distribute_element(Geometry const& , It const&, PointLike const& , Linear const&, Areal const&) {} template < typename Geometry, typename MultiGeometry, std::enable_if_t::value, int> = 0 > inline void convert(Geometry const& geometry, MultiGeometry& result) { geometry::convert(geometry, result); } template < typename Geometry, typename MultiGeometry, std::enable_if_t::value, int> = 0 > inline void convert(Geometry const& , MultiGeometry const& ) {} template < typename Geometry1, typename Geometry2, typename MultiGeometry, typename Strategy, std::enable_if_t::value, int> = 0 > inline void union_(Geometry1 const& geometry1, Geometry2 const& geometry2, MultiGeometry& result, Strategy const& strategy) { geometry::union_(geometry1, geometry2, result, strategy); } template < typename Geometry1, typename Geometry2, typename MultiGeometry, typename Strategy, std::enable_if_t::value, int> = 0 > inline void union_(Geometry1 const& , Geometry2 const& , MultiGeometry const& , Strategy const&) {} template struct merge_data { merge_data(It first_, It last_) : first(first_), last(last_) {} It first, last; bool merge_results = false; }; template inline void merge(RandomIt const first, RandomIt const last, MultiGeometry& out, Strategy const& strategy) { auto const size = last - first; if (size <= 0) { return; } auto const less = [](auto const& l, auto const& r) { return geometry::less()(l.first, r.first); }; std::vector> stack_in; std::vector stack_out; stack_in.reserve(size / 2 + 1); stack_out.reserve(size / 2 + 1); stack_in.emplace_back(first, last); while (! stack_in.empty()) { auto & b = stack_in.back(); if (! b.merge_results) { auto const s = b.last - b.first; if (s > 2) { RandomIt const mid = b.first + s / 2; std::nth_element(b.first, mid, b.last, less); RandomIt const fir = b.first; RandomIt const las = b.last; b.merge_results = true; stack_in.emplace_back(fir, mid); stack_in.emplace_back(mid, las); } else if (s == 2) { MultiGeometry result; // VERSION 1 // traits::iter_visit::apply([&](auto const& g1) // { // traits::iter_visit::apply([&](auto const& g2) // { // merge_elements::union_(g1, g2, result, strategy); // }, (b.first + 1)->second); // }, b.first->second); // VERSION 2 // calling iter_visit non-recursively seems to decrease compilation time // greately with GCC MultiGeometry temp1, temp2; traits::iter_visit::apply([&](auto const& g1) { merge_elements::convert(g1, temp1); }, b.first->second); traits::iter_visit::apply([&](auto const& g2) { merge_elements::convert(g2, temp2); }, (b.first + 1)->second); geometry::union_(temp1, temp2, result, strategy); stack_out.push_back(std::move(result)); stack_in.pop_back(); } else if (s == 1) { MultiGeometry result; traits::iter_visit::apply([&](auto const& g) { merge_elements::convert(g, result); }, b.first->second); stack_out.push_back(std::move(result)); stack_in.pop_back(); } } else if (b.merge_results) { MultiGeometry m2 = std::move(stack_out.back()); stack_out.pop_back(); MultiGeometry m1 = std::move(stack_out.back()); stack_out.pop_back(); MultiGeometry result; geometry::union_(m1, m2, result, strategy); stack_out.push_back(std::move(result)); stack_in.pop_back(); } } out = std::move(stack_out.back()); } template inline void subtract(MultiGeometry & multi, Geometry const& geometry, Strategy const& strategy) { MultiGeometry temp; geometry::difference(multi, geometry, temp, strategy); multi = std::move(temp); } struct merge_gc { template static void apply(GeometryCollection const& geometry_collection, GeometryCollection & out, Strategy const& strategy) { using original_point_t = typename geometry::point_type::type; using iterator_t = typename boost::range_iterator::type; using coordinate_t = typename geometry::coordinate_type::type; using cs_t = typename geometry::coordinate_system::type; using point_t = model::point; 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; // NOTE: Right now GC containing all of the above is required but technically // we could allow only some combinations and the algorithm below could // normalize GC accordingly. multi_point_t multi_point; multi_linestring_t multi_linestring; multi_polygon_t multi_polygon; std::vector> pointlike; std::vector> linear; std::vector> areal; detail::visit_breadth_first_impl::apply([&](auto const& g, auto it) { merge_elements::distribute_element(g, it, pointlike, linear, areal); return true; }, geometry_collection); // TODO: make this optional? // TODO: merge linear at the end? (difference can break linear rings, their parts would be joined) merge(pointlike.begin(), pointlike.end(), multi_point, strategy); merge(linear.begin(), linear.end(), multi_linestring, strategy); merge(areal.begin(), areal.end(), multi_polygon, strategy); // L \ A subtract(multi_linestring, multi_polygon, strategy); // P \ A subtract(multi_point, multi_polygon, strategy); // P \ L subtract(multi_point, multi_linestring, strategy); if (! geometry::is_empty(multi_point)) { range::emplace_back(out, std::move(multi_point)); } if (! geometry::is_empty(multi_linestring)) { range::emplace_back(out, std::move(multi_linestring)); } if (! geometry::is_empty(multi_polygon)) { range::emplace_back(out, std::move(multi_polygon)); } } }; }} // namespace detail::merge_elements #endif // DOXYGEN_NO_DETAIL #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { template ::type> struct merge_elements : not_implemented {}; template struct merge_elements : geometry::detail::merge_elements::merge_gc {}; } // namespace dispatch #endif namespace resolve_strategy { template struct merge_elements { template static void apply(Geometry const& geometry, Geometry & out, Strategy const& strategy) { dispatch::merge_elements < Geometry >::apply(geometry, out, strategy); } }; template <> struct merge_elements { template static void apply(Geometry const& geometry, Geometry & out, default_strategy) { using strategy_type = typename strategies::relate::services::default_strategy < Geometry, Geometry >::type; dispatch::merge_elements < Geometry >::apply(geometry, out, strategy_type()); } }; } // namespace resolve_strategy template inline void merge_elements(Geometry const& geometry, Geometry & out, Strategy const& strategy) { resolve_strategy::merge_elements < Strategy >::apply(geometry, out, strategy); } template inline void merge_elements(Geometry const& geometry, Geometry & out) { resolve_strategy::merge_elements < default_strategy >::apply(geometry, out, default_strategy()); } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_MERGE_ELEMENTS_HPP