mirror of
https://github.com/boostorg/geometry.git
synced 2025-05-10 07:34:03 +00:00
Merge pull request #987 from awulkiew/feature/gc7
Two GC-related utilities
This commit is contained in:
commit
dd32a9578b
@ -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,
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
403
include/boost/geometry/algorithms/merge_elements.hpp
Normal file
403
include/boost/geometry/algorithms/merge_elements.hpp
Normal 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
|
204
include/boost/geometry/views/detail/random_access_view.hpp
Normal file
204
include/boost/geometry/views/detail/random_access_view.hpp
Normal 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
|
@ -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 ]
|
||||
|
77
test/algorithms/merge_elements.cpp
Normal file
77
test/algorithms/merge_elements.cpp
Normal 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;
|
||||
}
|
@ -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 ]
|
||||
;
|
||||
|
123
test/views/random_access_view.cpp
Normal file
123
test/views/random_access_view.cpp
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user