libcarla/include/system/boost/geometry/algorithms/detail/sections/sectionalize.hpp

968 lines
28 KiB
C++
Raw Normal View History

2024-10-18 13:19:59 +08:00
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2013-2021.
// Modifications copyright (c) 2013-2021 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
// 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)
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP
#include <cstddef>
#include <type_traits>
#include <vector>
#include <boost/concept/requires.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>
#include <boost/range/value_type.hpp>
#include <boost/geometry/core/config.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
#include <boost/geometry/algorithms/detail/interior_iterator.hpp>
#include <boost/geometry/algorithms/detail/recalculate.hpp>
#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/core/exterior_ring.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/geometry/core/static_assert.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
#include <boost/geometry/policies/robustness/robust_point_type.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/util/sequence.hpp>
#include <boost/geometry/views/detail/closed_clockwise_view.hpp>
// TEMP
#include <boost/geometry/strategy/envelope.hpp>
#include <boost/geometry/strategy/expand.hpp>
namespace boost { namespace geometry
{
/*!
\brief Structure containing section information
\details Section information consists of a bounding box, direction
information (if it is increasing or decreasing, per dimension),
index information (begin-end, ring, multi) and the number of
segments in this section
\tparam Box box-type
\tparam DimensionCount number of dimensions for this section
\ingroup sectionalize
*/
template
<
typename Box,
std::size_t DimensionCount
>
struct section
{
using box_type = Box;
static std::size_t const dimension_count = DimensionCount;
int directions[DimensionCount];
ring_identifier ring_id;
Box bounding_box;
// NOTE: size_type could be passed as template parameter
// NOTE: these probably also could be of type std::size_t
signed_size_type begin_index;
signed_size_type end_index;
std::size_t count;
std::size_t range_count;
bool duplicate;
signed_size_type non_duplicate_index;
bool is_non_duplicate_first;
bool is_non_duplicate_last;
inline section()
: begin_index(-1)
, end_index(-1)
, count(0)
, range_count(0)
, duplicate(false)
, non_duplicate_index(-1)
, is_non_duplicate_first(false)
, is_non_duplicate_last(false)
{
assign_inverse(bounding_box);
for (std::size_t i = 0; i < DimensionCount; i++)
{
directions[i] = 0;
}
}
};
/*!
\brief Structure containing a collection of sections
\note Derived from a vector, proves to be faster than of deque
\note vector might be templated in the future
\ingroup sectionalize
*/
template <typename Box, std::size_t DimensionCount>
struct sections : std::vector<section<Box, DimensionCount> >
{
using box_type = Box;
static std::size_t const value = DimensionCount;
};
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace sectionalize
{
// NOTE: This utility will NOT work for latitudes, dimension 1 in spherical
// and geographic coordinate system because in these coordinate systems
// e.g. a segment on northern hemisphere may go towards greater latitude
// and then towards lesser latitude.
template
<
typename Point,
typename DimensionVector,
std::size_t Index,
std::size_t Count,
typename CastedCSTag = typename tag_cast
<
typename cs_tag<Point>::type,
spherical_tag
>::type
>
struct get_direction_loop
{
using dimension = typename util::sequence_element<Index, DimensionVector>::type;
template <typename Segment>
static inline void apply(Segment const& seg,
int directions[Count])
{
auto const& c0 = geometry::get<0, dimension::value>(seg);
auto const& c1 = geometry::get<1, dimension::value>(seg);
directions[Index] = c1 > c0 ? 1 : c1 < c0 ? -1 : 0;
get_direction_loop
<
Point,
DimensionVector,
Index + 1,
Count,
CastedCSTag
>::apply(seg, directions);
}
};
template
<
typename Point,
typename DimensionVector,
std::size_t Count
>
struct get_direction_loop<Point, DimensionVector, 0, Count, spherical_tag>
{
using dimension = typename util::sequence_element<0, DimensionVector>::type;
template <typename Segment>
static inline void apply(Segment const& seg,
int directions[Count])
{
using coordinate_type = typename coordinate_type<Segment>::type;
using units_t = typename coordinate_system<Point>::type::units;
coordinate_type const diff = math::longitude_distance_signed
<
units_t, coordinate_type
>(geometry::get<0, 0>(seg),
geometry::get<1, 0>(seg));
coordinate_type zero = coordinate_type();
directions[0] = diff > zero ? 1 : diff < zero ? -1 : 0;
get_direction_loop
<
Point,
DimensionVector,
1,
Count,
spherical_tag
>::apply(seg, directions);
}
};
template
<
typename Point,
typename DimensionVector,
std::size_t Count,
typename CastedCSTag
>
struct get_direction_loop<Point, DimensionVector, Count, Count, CastedCSTag>
{
template <typename Segment>
static inline void apply(Segment const&, int [Count])
{}
};
//! Copy one static array to another
template <typename T, std::size_t Index, std::size_t Count>
struct copy_loop
{
static inline void apply(T const source[Count], T target[Count])
{
target[Index] = source[Index];
copy_loop<T, Index + 1, Count>::apply(source, target);
}
};
template <typename T, std::size_t Count>
struct copy_loop<T, Count, Count>
{
static inline void apply(T const [Count], T [Count])
{}
};
//! Compare two static arrays
template <typename T, std::size_t Index, std::size_t Count>
struct compare_loop
{
static inline bool apply(T const array1[Count], T const array2[Count])
{
return array1[Index] != array2[Index]
? false
: compare_loop
<
T, Index + 1, Count
>::apply(array1, array2);
}
};
template <typename T, std::size_t Count>
struct compare_loop<T, Count, Count>
{
static inline bool apply(T const [Count], T const [Count])
{
return true;
}
};
template <std::size_t Dimension, std::size_t DimensionCount>
struct check_duplicate_loop
{
template <typename Segment>
static inline bool apply(Segment const& seg)
{
if (! geometry::math::equals
(
geometry::get<0, Dimension>(seg),
geometry::get<1, Dimension>(seg)
)
)
{
return false;
}
return check_duplicate_loop
<
Dimension + 1, DimensionCount
>::apply(seg);
}
};
template <std::size_t DimensionCount>
struct check_duplicate_loop<DimensionCount, DimensionCount>
{
template <typename Segment>
static inline bool apply(Segment const&)
{
return true;
}
};
//! Assign a value to a static array
template <typename T, std::size_t Index, std::size_t Count>
struct assign_loop
{
static inline void apply(T dims[Count], T const value)
{
dims[Index] = value;
assign_loop<T, Index + 1, Count>::apply(dims, value);
}
};
template <typename T, std::size_t Count>
struct assign_loop<T, Count, Count>
{
static inline void apply(T [Count], T const)
{
}
};
template <typename CSTag>
struct box_first_in_section
{
template <typename Box, typename Point, typename Strategy>
static inline void apply(Box & box, Point const& prev, Point const& curr,
Strategy const& strategy)
{
geometry::model::referring_segment<Point const> seg(prev, curr);
geometry::envelope(seg, box, strategy);
}
};
template <>
struct box_first_in_section<cartesian_tag>
{
template <typename Box, typename Point, typename Strategy>
static inline void apply(Box & box, Point const& prev, Point const& curr,
Strategy const& strategy)
{
geometry::envelope(prev, box, strategy);
geometry::expand(box, curr, strategy);
}
};
template <typename CSTag>
struct box_next_in_section
{
template <typename Box, typename Point, typename Strategy>
static inline void apply(Box & box, Point const& prev, Point const& curr,
Strategy const& strategy)
{
geometry::model::referring_segment<Point const> seg(prev, curr);
geometry::expand(box, seg, strategy);
}
};
template <>
struct box_next_in_section<cartesian_tag>
{
template <typename Box, typename Point, typename Strategy>
static inline void apply(Box & box, Point const& , Point const& curr,
Strategy const& strategy)
{
geometry::expand(box, curr, strategy);
}
};
/// @brief Helper class to create sections of a part of a range, on the fly
template<typename DimensionVector>
struct sectionalize_part
{
static const std::size_t dimension_count
= util::sequence_size<DimensionVector>::value;
template
<
typename Iterator,
typename RobustPolicy,
typename Sections,
typename Strategy
>
static inline void apply(Sections& sections,
Iterator begin, Iterator end,
RobustPolicy const& robust_policy,
Strategy const& strategy,
ring_identifier ring_id,
std::size_t max_count)
{
boost::ignore_unused(robust_policy);
using section_type = typename boost::range_value<Sections>::type;
using box_type = typename section_type::box_type;
using point_type = typename geometry::point_type<box_type>::type;
BOOST_STATIC_ASSERT
(
section_type::dimension_count
== util::sequence_size<DimensionVector>::value
);
using robust_point_type = typename geometry::robust_point_type
<
point_type,
RobustPolicy
>::type;
std::size_t const count = std::distance(begin, end);
if (count == 0)
{
return;
}
signed_size_type index = 0;
signed_size_type ndi = 0; // non duplicate index
section_type section;
bool mark_first_non_duplicated = true;
std::size_t last_non_duplicate_index = sections.size();
Iterator it = begin;
robust_point_type previous_robust_point;
geometry::recalculate(previous_robust_point, *it, robust_policy);
for(Iterator previous = it++;
it != end;
++previous, ++it, index++)
{
robust_point_type current_robust_point;
geometry::recalculate(current_robust_point, *it, robust_policy);
model::referring_segment<robust_point_type> robust_segment(
previous_robust_point, current_robust_point);
int direction_classes[dimension_count] = {0};
get_direction_loop
<
point_type, DimensionVector, 0, dimension_count
>::apply(robust_segment, direction_classes);
// if "dir" == 0 for all point-dimensions, it is duplicate.
// Those sections might be omitted, if wished, lateron
bool duplicate = false;
if (direction_classes[0] == 0)
{
// Recheck because ALL dimensions should be checked,
// not only first one.
// (dimension_count might be < dimension<P>::value)
if (check_duplicate_loop
<
0, geometry::dimension<point_type>::type::value
>::apply(robust_segment)
)
{
duplicate = true;
// Change direction-info to force new section
// Note that wo consecutive duplicate segments will generate
// only one duplicate-section.
// Actual value is not important as long as it is not -1,0,1
assign_loop
<
int, 0, dimension_count
>::apply(direction_classes, -99);
}
}
if (section.count > 0
&& (! compare_loop
<
int, 0, dimension_count
>::apply(direction_classes, section.directions)
|| section.count > max_count)
)
{
if (! section.duplicate)
{
last_non_duplicate_index = sections.size();
}
sections.push_back(section);
section = section_type();
}
if (section.count == 0)
{
section.begin_index = index;
section.ring_id = ring_id;
section.duplicate = duplicate;
section.non_duplicate_index = ndi;
section.range_count = count;
if (mark_first_non_duplicated && ! duplicate)
{
section.is_non_duplicate_first = true;
mark_first_non_duplicated = false;
}
copy_loop
<
int, 0, dimension_count
>::apply(direction_classes, section.directions);
// In cartesian this is envelope of previous point expanded with current point
// in non-cartesian this is envelope of a segment
box_first_in_section<typename cs_tag<robust_point_type>::type>
::apply(section.bounding_box, previous_robust_point, current_robust_point, strategy);
}
else
{
// In cartesian this is expand with current point
// in non-cartesian this is expand with a segment
box_next_in_section<typename cs_tag<robust_point_type>::type>
::apply(section.bounding_box, previous_robust_point, current_robust_point, strategy);
}
section.end_index = index + 1;
section.count++;
if (! duplicate)
{
ndi++;
}
previous_robust_point = current_robust_point;
}
// Add last section if applicable
if (section.count > 0)
{
if (! section.duplicate)
{
last_non_duplicate_index = sections.size();
}
sections.push_back(section);
}
if (last_non_duplicate_index < sections.size()
&& ! sections[last_non_duplicate_index].duplicate)
{
sections[last_non_duplicate_index].is_non_duplicate_last = true;
}
}
};
template
<
closure_selector Closure,
bool Reverse,
typename DimensionVector
>
struct sectionalize_range
{
template
<
typename Range,
typename RobustPolicy,
typename Sections,
typename Strategy
>
static inline void apply(Range const& range,
RobustPolicy const& robust_policy,
Sections& sections,
Strategy const& strategy,
ring_identifier ring_id,
std::size_t max_count)
{
detail::closed_clockwise_view
<
Range const,
Closure,
Reverse ? counterclockwise : clockwise
> const view(range);
std::size_t const n = boost::size(view);
if (n == 0)
{
// Zero points, no section
return;
}
if (n == 1)
{
// Line with one point ==> no sections
return;
}
sectionalize_part<DimensionVector>::apply(sections,
boost::begin(view), boost::end(view),
robust_policy, strategy,
ring_id, max_count);
}
};
template
<
bool Reverse,
typename DimensionVector
>
struct sectionalize_polygon
{
template
<
typename Polygon,
typename RobustPolicy,
typename Sections,
typename Strategy
>
static inline void apply(Polygon const& poly,
RobustPolicy const& robust_policy,
Sections& sections,
Strategy const& strategy,
ring_identifier ring_id,
std::size_t max_count)
{
using sectionalizer = sectionalize_range
<
closure<Polygon>::value, Reverse, DimensionVector
>;
ring_id.ring_index = -1;
sectionalizer::apply(exterior_ring(poly), robust_policy, sections,
strategy, ring_id, max_count);
ring_id.ring_index++;
auto const& rings = interior_rings(poly);
for (auto it = boost::begin(rings); it != boost::end(rings);
++it, ++ring_id.ring_index)
{
sectionalizer::apply(*it, robust_policy, sections,
strategy, ring_id, max_count);
}
}
};
template <typename DimensionVector>
struct sectionalize_box
{
template
<
typename Box,
typename RobustPolicy,
typename Sections,
typename Strategy
>
static inline void apply(Box const& box,
RobustPolicy const& robust_policy,
Sections& sections,
Strategy const& strategy,
ring_identifier const& ring_id, std::size_t max_count)
{
using point_type = typename point_type<Box>::type;
assert_dimension<Box, 2>();
// Add all four sides of the 2D-box as separate section.
// Easiest is to convert it to a polygon.
// However, we don't have the polygon type
// (or polygon would be a helper-type).
// Therefore we mimic a linestring/std::vector of 5 points
// TODO: might be replaced by assign_box_corners_oriented
// or just "convert"
point_type ll, lr, ul, ur;
geometry::detail::assign_box_corners(box, ll, lr, ul, ur);
std::vector<point_type> points;
points.push_back(ll);
points.push_back(ul);
points.push_back(ur);
points.push_back(lr);
points.push_back(ll);
// NOTE: Use cartesian envelope strategy in all coordinate systems
// because edges of a box are not geodesic segments
sectionalize_range
<
closed, false, DimensionVector
>::apply(points, robust_policy, sections,
strategy, ring_id, max_count);
}
};
template <typename DimensionVector, typename Policy>
struct sectionalize_multi
{
template
<
typename MultiGeometry,
typename RobustPolicy,
typename Sections,
typename Strategy
>
static inline void apply(MultiGeometry const& multi,
RobustPolicy const& robust_policy,
Sections& sections,
Strategy const& strategy,
ring_identifier ring_id,
std::size_t max_count)
{
ring_id.multi_index = 0;
for (typename boost::range_iterator<MultiGeometry const>::type
it = boost::begin(multi);
it != boost::end(multi);
++it, ++ring_id.multi_index)
{
Policy::apply(*it, robust_policy, sections,
strategy,
ring_id, max_count);
}
}
};
template <typename Sections, typename Strategy>
inline void enlarge_sections(Sections& sections, Strategy const&)
{
// Expand the box to avoid missing any intersection. The amount is
// should be larger than epsilon. About the value itself: the smaller
// it is, the higher the risk to miss intersections. The larger it is,
// the more comparisons are made, which is not harmful for the result
// (but it might be for the performance).
// So it should be on the high side.
// Use a compilable and workable epsilon for all types, for example:
// - for double :~ 2.22e-13
// - for float :~ 1e-4
// - for Boost.Multiprecision (50) :~ 5.35e-48
// - for Boost.Rational : 0/1
for (auto& section : sections)
{
using gt = decltype(section.bounding_box);
using ct = typename geometry::coordinate_type<gt>::type;
static ct const eps = math::scaled_epsilon<ct>(1000);
expand_by_epsilon(section.bounding_box, eps);
}
}
}} // namespace detail::sectionalize
#endif // DOXYGEN_NO_DETAIL
#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{
template
<
typename Tag,
typename Geometry,
bool Reverse,
typename DimensionVector
>
struct sectionalize
{
BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
"Not or not yet implemented for this Geometry type.",
Tag, Geometry);
};
template
<
typename Box,
bool Reverse,
typename DimensionVector
>
struct sectionalize<box_tag, Box, Reverse, DimensionVector>
: detail::sectionalize::sectionalize_box<DimensionVector>
{};
template
<
typename LineString,
typename DimensionVector
>
struct sectionalize
<
linestring_tag,
LineString,
false,
DimensionVector
>
: detail::sectionalize::sectionalize_range<closed, false, DimensionVector>
{};
template
<
typename Ring,
bool Reverse,
typename DimensionVector
>
struct sectionalize<ring_tag, Ring, Reverse, DimensionVector>
: detail::sectionalize::sectionalize_range
<
geometry::closure<Ring>::value, Reverse,
DimensionVector
>
{};
template
<
typename Polygon,
bool Reverse,
typename DimensionVector
>
struct sectionalize<polygon_tag, Polygon, Reverse, DimensionVector>
: detail::sectionalize::sectionalize_polygon
<
Reverse, DimensionVector
>
{};
template
<
typename MultiPolygon,
bool Reverse,
typename DimensionVector
>
struct sectionalize<multi_polygon_tag, MultiPolygon, Reverse, DimensionVector>
: detail::sectionalize::sectionalize_multi
<
DimensionVector,
detail::sectionalize::sectionalize_polygon
<
Reverse,
DimensionVector
>
>
{};
template
<
typename MultiLinestring,
bool Reverse,
typename DimensionVector
>
struct sectionalize<multi_linestring_tag, MultiLinestring, Reverse, DimensionVector>
: detail::sectionalize::sectionalize_multi
<
DimensionVector,
detail::sectionalize::sectionalize_range<closed, false, DimensionVector>
>
{};
} // namespace dispatch
#endif
/*!
\brief Split a geometry into monotonic sections
\ingroup sectionalize
\tparam Geometry type of geometry to check
\tparam Sections type of sections to create
\param geometry geometry to create sections from
\param robust_policy policy to handle robustness issues
\param sections structure with sections
\param strategy strategy for envelope calculation
\param expand_strategy strategy for partitions
\param source_index index to assign to the ring_identifiers
\param max_count maximal number of points per section
(defaults to 10, this seems to give the fastest results)
*/
template
<
bool Reverse,
typename DimensionVector,
typename Geometry,
typename Sections,
typename RobustPolicy,
typename Strategy
>
inline void sectionalize(Geometry const& geometry,
RobustPolicy const& robust_policy,
Sections& sections,
Strategy const& strategy,
int source_index = 0,
std::size_t max_count = 10)
{
concepts::check<Geometry const>();
using section_type = typename boost::range_value<Sections>::type;
// Compiletime check for point type of section boxes
// and point type related to robust policy
typedef typename geometry::coordinate_type
<
typename section_type::box_type
>::type ctype1;
typedef typename geometry::coordinate_type
<
typename geometry::robust_point_type
<
typename geometry::point_type<Geometry>::type,
RobustPolicy
>::type
>::type ctype2;
BOOST_STATIC_ASSERT((std::is_same<ctype1, ctype2>::value));
sections.clear();
ring_identifier ring_id;
ring_id.source_index = source_index;
dispatch::sectionalize
<
typename tag<Geometry>::type,
Geometry,
Reverse,
DimensionVector
>::apply(geometry, robust_policy, sections,
strategy,
ring_id, max_count);
detail::sectionalize::enlarge_sections(sections, strategy);
}
template
<
bool Reverse,
typename DimensionVector,
typename Geometry,
typename Sections,
typename RobustPolicy
>
inline void sectionalize(Geometry const& geometry,
RobustPolicy const& robust_policy,
Sections& sections,
int source_index = 0,
std::size_t max_count = 10)
{
using box_type = typename Sections::box_type;
using strategy_type = typename strategies::envelope::services::default_strategy
<
Geometry,
box_type
>::type;
boost::geometry::sectionalize
<
Reverse, DimensionVector
>(geometry, robust_policy, sections,
strategy_type(),
source_index, max_count);
}
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP