diff --git a/include/boost/geometry/algorithms/convert.hpp b/include/boost/geometry/algorithms/convert.hpp index 166441e1e..ac9cffa45 100644 --- a/include/boost/geometry/algorithms/convert.hpp +++ b/include/boost/geometry/algorithms/convert.hpp @@ -284,6 +284,10 @@ struct multi_to_multi: private Policy namespace dispatch { +// TODO: We could use std::is_assignable instead of std::is_same. +// Then we should rather check ! std::is_array::value +// which is Destination. + template < typename Geometry1, typename Geometry2, diff --git a/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp b/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp index c7d37b6ca..8762cd668 100644 --- a/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp +++ b/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp @@ -32,6 +32,12 @@ namespace detail { namespace conversion { +// TODO: Use assignment if possible. +// WARNING: This utility is called in various places for a subset of dimensions. +// In such cases only some of the coordinates should be copied. Alternatively +// there should be a different utility for that called differently than +// convert_xxx, e.g. set_coordinates. + template struct point_to_point { diff --git a/include/boost/geometry/algorithms/detail/point_on_border.hpp b/include/boost/geometry/algorithms/detail/point_on_border.hpp index 3969939b4..aa77a0a65 100644 --- a/include/boost/geometry/algorithms/detail/point_on_border.hpp +++ b/include/boost/geometry/algorithms/detail/point_on_border.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2017-2020. -// Modifications copyright (c) 2017-2020 Oracle and/or its affiliates. +// This file was modified by Oracle on 2017-2022. +// Modifications copyright (c) 2017-2022 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 @@ -25,17 +25,15 @@ #include #include -#include -#include -#include - -#include - #include #include #include - +#include +#include +#include +#include #include +#include namespace boost { namespace geometry @@ -49,10 +47,10 @@ namespace detail { namespace point_on_border struct get_point { - template - static inline bool apply(Point& destination, Point const& source) + template + static inline bool apply(Destination& destination, Source const& source) { - destination = source; + detail::conversion::convert_point_to_point(source, destination); return true; } }; @@ -69,7 +67,7 @@ struct point_on_range return false; } - geometry::detail::conversion::convert_point_to_point(*begin, point); + detail::conversion::convert_point_to_point(*begin, point); return true; } @@ -92,12 +90,13 @@ struct point_on_polygon }; -struct point_on_box +struct point_on_segment_or_box { - template - static inline bool apply(Point& point, Box const& box) + template + static inline bool apply(Point& point, SegmentOrBox const& segment_or_box) { - detail::assign::assign_box_2d_corner(box, point); + detail::indexed_point_view view(segment_or_box); + detail::conversion::convert_point_to_point(view, point); return true; } }; @@ -146,6 +145,11 @@ struct point_on_border : detail::point_on_border::get_point {}; +template <> +struct point_on_border + : detail::point_on_border::point_on_segment_or_box +{}; + template <> struct point_on_border : detail::point_on_border::point_on_range @@ -163,10 +167,15 @@ struct point_on_border template <> struct point_on_border - : detail::point_on_border::point_on_box + : detail::point_on_border::point_on_segment_or_box {}; +template <> +struct point_on_border + : detail::point_on_border::point_on_range +{}; + template <> struct point_on_border : detail::point_on_border::point_on_multi @@ -189,6 +198,9 @@ struct point_on_border #endif // DOXYGEN_NO_DISPATCH +// TODO: We should probably rename this utility because it can return point +// which is in the interior of a geometry (for PointLike and LinearRings). + /*! \brief Take point on a border \ingroup overlay diff --git a/include/boost/geometry/algorithms/merge_elements.hpp b/include/boost/geometry/algorithms/merge_elements.hpp new file mode 100644 index 000000000..bbf098659 --- /dev/null +++ b/include/boost/geometry/algorithms/merge_elements.hpp @@ -0,0 +1,403 @@ +// 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 , PointLike& , Linear&, Areal&) +{} + + +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& ) +{} + + +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& , 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; + 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); + 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()); +} + +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_point2; + multi_linestring_t multi_linestring, multi_linestring2; + 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 + geometry::difference(multi_linestring, multi_polygon, multi_linestring2, strategy); + + // (P \ A) \ L + geometry::difference(multi_point, multi_polygon, multi_point2, strategy); + range::clear(multi_point); + geometry::difference(multi_point2, multi_linestring2, multi_point, strategy); + + if (! geometry::is_empty(multi_point)) + { + range::emplace_back(out, std::move(multi_point)); + } + if (! geometry::is_empty(multi_linestring2)) + { + range::emplace_back(out, std::move(multi_linestring2)); + } + 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 diff --git a/include/boost/geometry/views/detail/random_access_view.hpp b/include/boost/geometry/views/detail/random_access_view.hpp new file mode 100644 index 000000000..874c71638 --- /dev/null +++ b/include/boost/geometry/views/detail/random_access_view.hpp @@ -0,0 +1,204 @@ +// 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_VIEWS_DETAIL_RANDOM_ACCESS_VIEW_HPP +#define BOOST_GEOMETRY_VIEWS_DETAIL_RANDOM_ACCESS_VIEW_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +struct is_random_access_range + : std::is_convertible + < + typename boost::iterator_traversal + < + typename boost::range_iterator::type + >::type, + boost::random_access_traversal_tag + > +{}; + +template +struct is_geometry_collection_recursive + : util::bool_constant + < + util::is_geometry_collection + < + typename util::sequence_find_if + < + typename traits::geometry_types>::type, + util::is_geometry_collection + >::type + >::value + > +{}; + +template +< + typename GeometryCollection, + bool IsRandomAccess = is_random_access_range::value, + bool IsRecursive = is_geometry_collection_recursive::value +> +class random_access_view + : public std::vector::type> +{ + // NOTE: An alternative would be to implement iterator holding base iterators + // to geometry collections of lower levels to process after the current level + // of bfs traversal is finished. + + using base_t = std::vector::type>; + +public: + random_access_view(GeometryCollection & geometry) + { + this->reserve(boost::size(geometry)); + detail::visit_breadth_first_impl::apply([&](auto&&, auto iter) + { + this->push_back(iter); + return true; + }, geometry); + } +}; + +template +class random_access_view +{ +public: + using iterator = typename boost::range_iterator::type; + using const_iterator = typename boost::range_const_iterator::type; + + random_access_view(GeometryCollection & geometry) + : m_begin(boost::begin(geometry)) + , m_end(boost::end(geometry)) + {} + + iterator begin() { return m_begin; } + iterator end() { return m_end; } + const_iterator begin() const { return m_begin; } + const_iterator end() const { return m_end; } + +private: + iterator m_begin, m_end; +}; + + +template +struct random_access_view_iter_visit +{ + template + static void apply(Function && function, Iterator iterator) + { + geometry::traits::iter_visit + < + std::remove_const_t + >::apply(std::forward(function), *iterator); + } +}; + + +template +struct remove_geometry_collections_pack +{ + using type = util::type_sequence<>; +}; + +template +struct remove_geometry_collections_pack +{ + using next_sequence = typename remove_geometry_collections_pack::type; + using type = std::conditional_t + < + util::is_geometry_collection::value, + next_sequence, + typename util::sequence_merge, next_sequence>::type + >; +}; + +template +struct remove_geometry_collections; + +template +struct remove_geometry_collections> + : remove_geometry_collections_pack +{}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS +namespace traits +{ + +template +struct tag> +{ + using type = geometry_collection_tag; +}; + + +template +struct iter_visit> + : geometry::detail::random_access_view_iter_visit +{}; + +template +struct iter_visit> + : geometry::detail::random_access_view_iter_visit +{}; + +template +struct iter_visit> + : geometry::detail::random_access_view_iter_visit +{}; + + +template +struct geometry_types> + : geometry_types +{}; + +template +struct geometry_types> + : geometry::detail::remove_geometry_collections + < + typename traits::geometry_types + < + std::remove_const_t + >::type + > +{}; + +} // namespace traits +#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_VIEWS_DETAIL_RANDOM_ACCESS_VIEW_HPP diff --git a/test/algorithms/Jamfile b/test/algorithms/Jamfile index 52cbb6313..65931a6f9 100644 --- a/test/algorithms/Jamfile +++ b/test/algorithms/Jamfile @@ -5,8 +5,8 @@ # Copyright (c) 2009-2015 Mateusz Loskot, London, UK. # Copyright (c) 2018 Adam Wulkiewicz, Lodz, Poland. # -# 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 Vissarion Fisikopoulos, on behalf of Oracle # Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -45,6 +45,7 @@ test-suite boost-geometry-algorithms [ run line_interpolate.cpp : : : : algorithms_line_interpolate ] [ run make.cpp : : : : algorithms_make ] [ run maximum_gap.cpp : : : : algorithms_maximum_gap ] + [ run merge_elements.cpp : : : : algorithms_merge_elements ] [ run num_geometries.cpp : : : : algorithms_num_geometries ] [ run num_geometries_multi.cpp : : : : algorithms_num_geometries_multi ] [ run num_interior_rings.cpp : : : : algorithms_num_interior_rings ] diff --git a/test/algorithms/merge_elements.cpp b/test/algorithms/merge_elements.cpp new file mode 100644 index 000000000..78722e536 --- /dev/null +++ b/test/algorithms/merge_elements.cpp @@ -0,0 +1,77 @@ +// Boost.Geometry +// Unit Test + +// 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 + + +#include + +#include +#include +#include +#include +#include +#include +#include + + +template +void test_all(std::size_t points_count, std::size_t linestrings_count, std::size_t polygons_count, + double length, double perimeter, double area) +{ + using pt_t = P; + using mpt_t = bg::model::multi_point; + using seg_t = bg::model::segment; + using ls_t = bg::model::linestring; + using mls_t = bg::model::multi_linestring; + using box_t = bg::model::box; + using ring_t = bg::model::ring; + using poly_t = bg::model::polygon; + using mpoly_t = bg::model::multi_polygon; + using var_t = boost::variant; + //using var_t = boost::variant2::variant; + using gc_t = bg::model::geometry_collection; + + gc_t gc{ + poly_t{{{0, 0},{0, 10},{10, 10},{10, 0},{0, 0}}, {{1, 1},{5, 1},{5, 5},{1, 5},{1, 1}}}, + mpoly_t{{{{4, 4},{4, 6},{6, 6},{6, 4},{4, 4}}}, {{{10, 10},{10, 12},{12, 12},{12, 10},{10, 10}}}}, + ring_t{{11, 11},{11, 14},{14, 14},{14, 11},{11, 11}}, + //box_t{{13, 13}, {16, 16}}, + mls_t{{{0, 0},{20, 20}}, {{3, 3},{3, 6},{6, 6},{6, 3},{3, 3}}}, + ls_t{{3, 3},{3, 20}}, + mpt_t{{2, 2},{2, 2},{5, 5},{9, 9},{11, 11},{0, 20},{1, 20}}, + pt_t{0, 20} + }; + + gc_t result; + bg::merge_elements(gc, result); + + BOOST_CHECK(boost::get(result[0]).size() == points_count); + BOOST_CHECK(boost::get(result[1]).size() == linestrings_count); + BOOST_CHECK(boost::get(result[2]).size() == polygons_count); + auto l = bg::length(result); + auto a = bg::area(result); + auto p = bg::perimeter(result); + decltype(l) l_expected = length; + decltype(p) p_expected = perimeter; + decltype(a) a_expected = area; + BOOST_CHECK_CLOSE(l, l_expected, 0.000001); + BOOST_CHECK_CLOSE(p, p_expected, 0.000001); + BOOST_CHECK_CLOSE(a, a_expected, 0.000001); +} + +int test_main(int, char* []) +{ + test_all>(2, 5, 2, std::sqrt(2.0) * (3 + 6) + 4 + 10, 72, 97); + // TODO: right now the elements of multi geometries are not merged, + // only different geometries stored in a GC. Hence duplicated point in the result. + test_all>>(4, 6, 2, 0.48141804683843953, 1.2506937915396685, 0.029392562222852522); + test_all>>(4, 6, 2, 3058383.6297531724, 7951118.1434133006, 1187967114570.5911); + + return 0; +} diff --git a/test/views/Jamfile b/test/views/Jamfile index 3b23c2f36..ef716392c 100644 --- a/test/views/Jamfile +++ b/test/views/Jamfile @@ -5,15 +5,20 @@ # Copyright (c) 2009-2015 Mateusz Loskot, London, UK. # Copyright (c) 2015 Adam Wulkiewicz, Lodz, Poland. # +# This file was modified by Oracle on 2022. +# Modifications copyright (c) 2022 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) test-suite boost-geometry-views : - [ run segment_view.cpp : : : : views_segment_view ] [ run box_view.cpp : : : : views_box_view ] - [ run reversible_view.cpp : : : : views_reversible_view ] [ run closeable_view.cpp : : : : views_closeable_view ] + [ run random_access_view.cpp : : : : views_random_access_view ] [ run reversible_closeable.cpp : : : : views_reversible_closeable ] + [ run reversible_view.cpp : : : : views_reversible_view ] + [ run segment_view.cpp : : : : views_segment_view ] ; diff --git a/test/views/random_access_view.cpp b/test/views/random_access_view.cpp new file mode 100644 index 000000000..d847cbd18 --- /dev/null +++ b/test/views/random_access_view.cpp @@ -0,0 +1,123 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2022 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) + + +#include + +#include + +#include +#include + +using point_t = bg::model::point; +using linestring_t = bg::model::linestring; +using polygon_t = bg::model::polygon; +using variant_t = boost::variant; +using gc_t = bg::model::geometry_collection; +using nra_gc_t = bg::model::geometry_collection; + +struct rec_gc_t; +using rec_var_t = boost::variant; +struct rec_gc_t : std::vector +{ + rec_gc_t() = default; + rec_gc_t(std::initializer_list l) : std::vector(l) {} +}; + +struct rec_nra_gc_t; +using rec_nra_var_t = boost::variant; +struct rec_nra_gc_t : std::list +{ + rec_nra_gc_t() = default; + rec_nra_gc_t(std::initializer_list l) : std::list(l) {} +}; + +namespace boost { namespace geometry { namespace traits { + template<> struct tag { typedef geometry_collection_tag type; }; + template<> struct geometry_types { typedef util::type_sequence type; }; +}}} + +namespace boost { namespace geometry { namespace traits { + template<> struct tag { typedef geometry_collection_tag type; }; + template<> struct geometry_types { typedef util::type_sequence type; }; +}}} + +template +GC make_gc() +{ + return GC{ + point_t{0, 0}, + linestring_t{{1, 1}, {2, 2}}, + polygon_t{{{3, 3}, {3, 4}, {4, 4}, {4, 3}, {3, 3}}}, + polygon_t{{{5, 5}, {5, 6}, {6, 6}, {6, 5}, {5, 5}}} + }; +} + +template +GC make_rec_gc() +{ + return GC{ + point_t{0, 0}, + GC { + // This should be put at the end because GC is processed in a BFS manner. + // So both GCs should be equivalent. + polygon_t{{{5, 5}, {5, 6}, {6, 6}, {6, 5}, {5, 5}}} + }, + linestring_t{{1, 1}, {2, 2}}, + polygon_t{{{3, 3}, {3, 4}, {4, 4}, {4, 3}, {3, 3}}}, + }; +} + +template +void test_all(Make make) +{ + using view_t = bg::detail::random_access_view; + + GC gc = make(); + view_t rav{gc}; + + using non_gc_types = bg::util::type_sequence; + using view_types = typename bg::traits::geometry_types::type; + BOOST_STATIC_ASSERT((std::is_same::value)); + BOOST_STATIC_ASSERT(bg::detail::is_random_access_range::value); + BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive::value); + + size_t s = boost::size(gc); + for (size_t i = 0 ; i < s ; ++i) + { + bg::traits::iter_visit>::apply([&](auto&& g) + { + using geom_t = bg::util::remove_cref_t; + if (i == 0) { BOOST_CHECK(bg::util::is_point::value); } + else if (i == 1) { BOOST_CHECK(bg::util::is_linestring::value); } + else { BOOST_CHECK(bg::util::is_polygon::value); } + }, rav.begin() + i); + } +} + +int test_main(int, char* []) +{ + BOOST_STATIC_ASSERT(bg::detail::is_random_access_range::value); + BOOST_STATIC_ASSERT(! bg::detail::is_random_access_range::value); + BOOST_STATIC_ASSERT(bg::detail::is_random_access_range::value); + BOOST_STATIC_ASSERT(! bg::detail::is_random_access_range::value); + + BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive::value); + BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive::value); + BOOST_STATIC_ASSERT(bg::detail::is_geometry_collection_recursive::value); + BOOST_STATIC_ASSERT(bg::detail::is_geometry_collection_recursive::value); + + test_all(make_gc); + test_all(make_gc); + test_all(make_gc); + test_all(make_gc); + + return 0; +}