Merge pull request #987 from awulkiew/feature/gc7

Two GC-related utilities
This commit is contained in:
Adam Wulkiewicz 2022-05-24 16:42:50 +02:00 committed by GitHub
commit dd32a9578b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 857 additions and 22 deletions

View File

@ -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<Geometry2>::value
// which is Destination.
template
<
typename Geometry1, typename Geometry2,

View File

@ -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 <typename Source, typename Destination, std::size_t Dimension, std::size_t DimensionCount>
struct point_to_point
{

View File

@ -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 <boost/range/end.hpp>
#include <boost/static_assert.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/ring_type.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/ring_type.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/views/detail/indexed_point_view.hpp>
namespace boost { namespace geometry
@ -49,10 +47,10 @@ namespace detail { namespace point_on_border
struct get_point
{
template <typename Point>
static inline bool apply(Point& destination, Point const& source)
template <typename Destination, typename Source>
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<typename Point, typename Box>
static inline bool apply(Point& point, Box const& box)
template<typename Point, typename SegmentOrBox>
static inline bool apply(Point& point, SegmentOrBox const& segment_or_box)
{
detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, point);
detail::indexed_point_view<SegmentOrBox const, 0> view(segment_or_box);
detail::conversion::convert_point_to_point(view, point);
return true;
}
};
@ -146,6 +145,11 @@ struct point_on_border<point_tag>
: detail::point_on_border::get_point
{};
template <>
struct point_on_border<segment_tag>
: detail::point_on_border::point_on_segment_or_box
{};
template <>
struct point_on_border<linestring_tag>
: detail::point_on_border::point_on_range
@ -163,10 +167,15 @@ struct point_on_border<polygon_tag>
template <>
struct point_on_border<box_tag>
: detail::point_on_border::point_on_box
: detail::point_on_border::point_on_segment_or_box
{};
template <>
struct point_on_border<multi_point_tag>
: detail::point_on_border::point_on_range
{};
template <>
struct point_on_border<multi_polygon_tag>
: detail::point_on_border::point_on_multi
@ -189,6 +198,9 @@ struct point_on_border<multi_linestring_tag>
#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

View File

@ -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 <vector>
#include <boost/geometry/algorithms/detail/point_on_border.hpp>
#include <boost/geometry/algorithms/detail/visit.hpp>
#include <boost/geometry/algorithms/difference.hpp>
#include <boost/geometry/algorithms/union.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/core/visit.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/policies/compare.hpp>
#include <boost/geometry/util/range.hpp>
#include <boost/geometry/strategies/relate/cartesian.hpp>
#include <boost/geometry/strategies/relate/geographic.hpp>
#include <boost/geometry/strategies/relate/spherical.hpp>
#include <boost/geometry/strategies/spherical/compare.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace merge_elements
{
template <typename T>
using is_pla = util::bool_constant<util::is_pointlike<T>::value || util::is_linear<T>::value || util::is_areal<T>::value>;
template <typename T, typename ...Ts>
struct are_areal : util::bool_constant<util::is_areal<T>::value && are_areal<Ts...>::value> {};
template <typename T>
struct are_areal<T> : util::is_areal<T> {};
template <typename T, typename ...Ts>
struct are_linear : util::bool_constant<util::is_linear<T>::value && are_linear<Ts...>::value> {};
template <typename T>
struct are_linear<T> : util::is_linear<T> {};
template <typename T, typename ...Ts>
struct are_pointlike : util::bool_constant<util::is_pointlike<T>::value && are_pointlike<Ts...>::value> {};
template <typename T>
struct are_pointlike<T> : util::is_pointlike<T> {};
template <typename ...Ts>
using are_same_kind = util::bool_constant<are_areal<Ts...>::value || are_linear<Ts...>::value || are_pointlike<Ts...>::value>;
template
<
typename Geometry, typename It, typename PointLike, typename Linear, typename Areal,
std::enable_if_t<util::is_areal<Geometry>::value, int> = 0
>
inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear&, Areal& areal)
{
typename geometry::point_type<Geometry>::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<util::is_linear<Geometry>::value, int> = 0
>
inline void distribute_element(Geometry const& geometry, It it, PointLike& , Linear& linear, Areal& )
{
typename geometry::point_type<Geometry>::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<util::is_pointlike<Geometry>::value, int> = 0
>
inline void distribute_element(Geometry const& geometry, It it, PointLike& pointlike, Linear& , Areal& )
{
typename geometry::point_type<Geometry>::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<! is_pla<Geometry>::value, int> = 0
>
inline void distribute_element(Geometry const& , It , PointLike& , Linear&, Areal&)
{}
template
<
typename Geometry, typename MultiGeometry,
std::enable_if_t<are_same_kind<Geometry, MultiGeometry>::value, int> = 0
>
inline void convert(Geometry const& geometry, MultiGeometry& result)
{
geometry::convert(geometry, result);
}
template
<
typename Geometry, typename MultiGeometry,
std::enable_if_t<! are_same_kind<Geometry, MultiGeometry>::value, int> = 0
>
inline void convert(Geometry const& , MultiGeometry& )
{}
template
<
typename Geometry1, typename Geometry2, typename MultiGeometry, typename Strategy,
std::enable_if_t<are_same_kind<Geometry1, Geometry2, MultiGeometry>::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<! are_same_kind<Geometry1, Geometry2, MultiGeometry>::value, int> = 0
>
inline void union_(Geometry1 const& , Geometry2 const& , MultiGeometry& , Strategy const&)
{}
template <typename It>
struct merge_data
{
merge_data(It first_, It last_)
: first(first_), last(last_)
{}
It first, last;
bool merge_results = false;
};
template <typename GeometryCollection, typename RandomIt, typename MultiGeometry, typename Strategy>
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<void, -1, typename Strategy::cs_tag>()(l.first, r.first);
};
std::vector<merge_data<RandomIt>> stack_in;
std::vector<MultiGeometry> 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<GeometryCollection>::apply([&](auto const& g1)
{
traits::iter_visit<GeometryCollection>::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<GeometryCollection>::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 <typename GeometryCollection, typename Strategy>
static void apply(GeometryCollection const& geometry_collection,
GeometryCollection & out,
Strategy const& strategy)
{
using original_point_t = typename geometry::point_type<GeometryCollection>::type;
using iterator_t = typename boost::range_iterator<GeometryCollection const>::type;
using coordinate_t = typename geometry::coordinate_type<original_point_t>::type;
using cs_t = typename geometry::coordinate_system<original_point_t>::type;
using point_t = model::point<coordinate_t, 2, cs_t>;
using multi_point_t = typename util::sequence_find_if
<
typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::type,
util::is_multi_point
>::type;
using multi_linestring_t = typename util::sequence_find_if
<
typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::type,
util::is_multi_linestring
>::type;
using multi_polygon_t = typename util::sequence_find_if
<
typename traits::geometry_types<std::remove_const_t<GeometryCollection>>::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<std::pair<point_t, iterator_t>> pointlike;
std::vector<std::pair<point_t, iterator_t>> linear;
std::vector<std::pair<point_t, iterator_t>> areal;
detail::visit_breadth_first_impl<true>::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<GeometryCollection>(pointlike.begin(), pointlike.end(), multi_point, strategy);
merge<GeometryCollection>(linear.begin(), linear.end(), multi_linestring, strategy);
merge<GeometryCollection>(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 <typename Geometry, typename Tag = typename tag<Geometry>::type>
struct merge_elements
: not_implemented<Geometry, Tag>
{};
template <typename GeometryCollection>
struct merge_elements<GeometryCollection, geometry_collection_tag>
: geometry::detail::merge_elements::merge_gc
{};
} // namespace dispatch
#endif
namespace resolve_strategy
{
template <typename Strategy>
struct merge_elements
{
template <typename Geometry>
static void apply(Geometry const& geometry, Geometry & out, Strategy const& strategy)
{
dispatch::merge_elements
<
Geometry
>::apply(geometry, out, strategy);
}
};
template <>
struct merge_elements<default_strategy>
{
template <typename Geometry>
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 <typename Geometry, typename Strategy>
inline void merge_elements(Geometry const& geometry, Geometry & out, Strategy const& strategy)
{
resolve_strategy::merge_elements
<
Strategy
>::apply(geometry, out, strategy);
}
template <typename Geometry>
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

View File

@ -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 <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/size.hpp>
#include <boost/geometry/algorithms/detail/visit.hpp>
#include <boost/geometry/core/geometry_types.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/util/sequence.hpp>
#include <boost/geometry/util/type_traits.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename Range>
struct is_random_access_range
: std::is_convertible
<
typename boost::iterator_traversal
<
typename boost::range_iterator<Range>::type
>::type,
boost::random_access_traversal_tag
>
{};
template <typename Geometry>
struct is_geometry_collection_recursive
: util::bool_constant
<
util::is_geometry_collection
<
typename util::sequence_find_if
<
typename traits::geometry_types<std::remove_const_t<Geometry>>::type,
util::is_geometry_collection
>::type
>::value
>
{};
template
<
typename GeometryCollection,
bool IsRandomAccess = is_random_access_range<GeometryCollection>::value,
bool IsRecursive = is_geometry_collection_recursive<GeometryCollection>::value
>
class random_access_view
: public std::vector<typename boost::range_iterator<GeometryCollection>::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<typename boost::range_iterator<GeometryCollection>::type>;
public:
random_access_view(GeometryCollection & geometry)
{
this->reserve(boost::size(geometry));
detail::visit_breadth_first_impl<true>::apply([&](auto&&, auto iter)
{
this->push_back(iter);
return true;
}, geometry);
}
};
template <typename GeometryCollection>
class random_access_view<GeometryCollection, true, false>
{
public:
using iterator = typename boost::range_iterator<GeometryCollection>::type;
using const_iterator = typename boost::range_const_iterator<GeometryCollection>::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 <typename GeometryCollection>
struct random_access_view_iter_visit
{
template <typename Function, typename Iterator>
static void apply(Function && function, Iterator iterator)
{
geometry::traits::iter_visit
<
std::remove_const_t<GeometryCollection>
>::apply(std::forward<Function>(function), *iterator);
}
};
template <typename ...Ts>
struct remove_geometry_collections_pack
{
using type = util::type_sequence<>;
};
template <typename T, typename ...Ts>
struct remove_geometry_collections_pack<T, Ts...>
{
using next_sequence = typename remove_geometry_collections_pack<Ts...>::type;
using type = std::conditional_t
<
util::is_geometry_collection<T>::value,
next_sequence,
typename util::sequence_merge<util::type_sequence<T>, next_sequence>::type
>;
};
template <typename Types>
struct remove_geometry_collections;
template <typename ...Ts>
struct remove_geometry_collections<util::type_sequence<Ts...>>
: remove_geometry_collections_pack<Ts...>
{};
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS
namespace traits
{
template<typename GeometryCollection, bool IsRandomAccess, bool IsRecursive>
struct tag<geometry::detail::random_access_view<GeometryCollection, IsRandomAccess, IsRecursive>>
{
using type = geometry_collection_tag;
};
template <typename GeometryCollection>
struct iter_visit<geometry::detail::random_access_view<GeometryCollection, false, false>>
: geometry::detail::random_access_view_iter_visit<GeometryCollection>
{};
template <typename GeometryCollection>
struct iter_visit<geometry::detail::random_access_view<GeometryCollection, true, true>>
: geometry::detail::random_access_view_iter_visit<GeometryCollection>
{};
template <typename GeometryCollection>
struct iter_visit<geometry::detail::random_access_view<GeometryCollection, false, true>>
: geometry::detail::random_access_view_iter_visit<GeometryCollection>
{};
template <typename GeometryCollection, bool IsRandomAccess>
struct geometry_types<geometry::detail::random_access_view<GeometryCollection, IsRandomAccess, false>>
: geometry_types<GeometryCollection>
{};
template <typename GeometryCollection, bool IsRandomAccess>
struct geometry_types<geometry::detail::random_access_view<GeometryCollection, IsRandomAccess, true>>
: geometry::detail::remove_geometry_collections
<
typename traits::geometry_types
<
std::remove_const_t<GeometryCollection>
>::type
>
{};
} // namespace traits
#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_VIEWS_DETAIL_RANDOM_ACCESS_VIEW_HPP

View File

@ -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 ]

View File

@ -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 <geometry_test_common.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/length.hpp>
#include <boost/geometry/algorithms/merge_elements.hpp>
#include <boost/geometry/algorithms/perimeter.hpp>
#include <boost/geometry/geometries/adapted/boost_variant2.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
template <typename P>
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<pt_t>;
using seg_t = bg::model::segment<pt_t>;
using ls_t = bg::model::linestring<pt_t>;
using mls_t = bg::model::multi_linestring<ls_t>;
using box_t = bg::model::box<pt_t>;
using ring_t = bg::model::ring<pt_t>;
using poly_t = bg::model::polygon<pt_t>;
using mpoly_t = bg::model::multi_polygon<poly_t>;
using var_t = boost::variant<pt_t, mpt_t/*, seg_t*/, ls_t, mls_t, /*box_t,*/ ring_t, poly_t, mpoly_t>;
//using var_t = boost::variant2::variant<pt_t, mpt_t/*, seg_t*/, ls_t, mls_t, /*box_t,*/ ring_t, poly_t, mpoly_t>;
using gc_t = bg::model::geometry_collection<var_t>;
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<mpt_t>(result[0]).size() == points_count);
BOOST_CHECK(boost::get<mls_t>(result[1]).size() == linestrings_count);
BOOST_CHECK(boost::get<mpoly_t>(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<bg::model::point<double, 2, bg::cs::cartesian>>(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<bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree>>>(4, 6, 2, 0.48141804683843953, 1.2506937915396685, 0.029392562222852522);
test_all<bg::model::point<double, 2, bg::cs::geographic<bg::degree>>>(4, 6, 2, 3058383.6297531724, 7951118.1434133006, 1187967114570.5911);
return 0;
}

View File

@ -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 ]
;

View File

@ -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 <geometry_test_common.hpp>
#include <boost/variant.hpp>
#include <boost/geometry/views/detail/random_access_view.hpp>
#include <boost/geometry/geometries/geometries.hpp>
using point_t = bg::model::point<double, 2, bg::cs::cartesian>;
using linestring_t = bg::model::linestring<point_t>;
using polygon_t = bg::model::polygon<point_t>;
using variant_t = boost::variant<point_t, linestring_t, polygon_t>;
using gc_t = bg::model::geometry_collection<variant_t>;
using nra_gc_t = bg::model::geometry_collection<variant_t, std::list>;
struct rec_gc_t;
using rec_var_t = boost::variant<point_t, linestring_t, polygon_t, rec_gc_t>;
struct rec_gc_t : std::vector<rec_var_t>
{
rec_gc_t() = default;
rec_gc_t(std::initializer_list<rec_var_t> l) : std::vector<rec_var_t>(l) {}
};
struct rec_nra_gc_t;
using rec_nra_var_t = boost::variant<point_t, linestring_t, polygon_t, rec_nra_gc_t>;
struct rec_nra_gc_t : std::list<rec_nra_var_t>
{
rec_nra_gc_t() = default;
rec_nra_gc_t(std::initializer_list<rec_nra_var_t> l) : std::list<rec_nra_var_t>(l) {}
};
namespace boost { namespace geometry { namespace traits {
template<> struct tag<rec_gc_t> { typedef geometry_collection_tag type; };
template<> struct geometry_types<rec_gc_t> { typedef util::type_sequence<point_t, linestring_t, rec_gc_t, polygon_t> type; };
}}}
namespace boost { namespace geometry { namespace traits {
template<> struct tag<rec_nra_gc_t> { typedef geometry_collection_tag type; };
template<> struct geometry_types<rec_nra_gc_t> { typedef util::type_sequence<point_t, linestring_t, rec_nra_gc_t, polygon_t> type; };
}}}
template <typename GC>
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 <typename GC>
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 <typename GC, typename Make>
void test_all(Make make)
{
using view_t = bg::detail::random_access_view<GC>;
GC gc = make();
view_t rav{gc};
using non_gc_types = bg::util::type_sequence<point_t, linestring_t, polygon_t>;
using view_types = typename bg::traits::geometry_types<view_t>::type;
BOOST_STATIC_ASSERT((std::is_same<non_gc_types, view_types>::value));
BOOST_STATIC_ASSERT(bg::detail::is_random_access_range<view_t>::value);
BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive<view_t>::value);
size_t s = boost::size(gc);
for (size_t i = 0 ; i < s ; ++i)
{
bg::traits::iter_visit<bg::detail::random_access_view<GC>>::apply([&](auto&& g)
{
using geom_t = bg::util::remove_cref_t<decltype(g)>;
if (i == 0) { BOOST_CHECK(bg::util::is_point<geom_t>::value); }
else if (i == 1) { BOOST_CHECK(bg::util::is_linestring<geom_t>::value); }
else { BOOST_CHECK(bg::util::is_polygon<geom_t>::value); }
}, rav.begin() + i);
}
}
int test_main(int, char* [])
{
BOOST_STATIC_ASSERT(bg::detail::is_random_access_range<gc_t>::value);
BOOST_STATIC_ASSERT(! bg::detail::is_random_access_range<nra_gc_t>::value);
BOOST_STATIC_ASSERT(bg::detail::is_random_access_range<rec_gc_t>::value);
BOOST_STATIC_ASSERT(! bg::detail::is_random_access_range<rec_nra_gc_t>::value);
BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive<gc_t>::value);
BOOST_STATIC_ASSERT(! bg::detail::is_geometry_collection_recursive<nra_gc_t>::value);
BOOST_STATIC_ASSERT(bg::detail::is_geometry_collection_recursive<rec_gc_t>::value);
BOOST_STATIC_ASSERT(bg::detail::is_geometry_collection_recursive<rec_nra_gc_t>::value);
test_all<gc_t>(make_gc<gc_t>);
test_all<nra_gc_t>(make_gc<nra_gc_t>);
test_all<rec_gc_t>(make_gc<rec_gc_t>);
test_all<rec_nra_gc_t>(make_gc<rec_nra_gc_t>);
return 0;
}