507 lines
15 KiB
C++
507 lines
15 KiB
C++
// Boost.Geometry
|
|
|
|
// Copyright (c) 2017-2021, 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_DENSIFY_HPP
|
|
#define BOOST_GEOMETRY_ALGORITHMS_DENSIFY_HPP
|
|
|
|
|
|
#include <boost/range/size.hpp>
|
|
#include <boost/range/value_type.hpp>
|
|
#include <boost/throw_exception.hpp>
|
|
|
|
#include <boost/geometry/algorithms/clear.hpp>
|
|
#include <boost/geometry/algorithms/convert.hpp>
|
|
#include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
|
|
#include <boost/geometry/algorithms/detail/visit.hpp>
|
|
#include <boost/geometry/algorithms/not_implemented.hpp>
|
|
#include <boost/geometry/core/closure.hpp>
|
|
#include <boost/geometry/core/cs.hpp>
|
|
#include <boost/geometry/core/exception.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/adapted/boost_variant.hpp> // For backward compatibility
|
|
#include <boost/geometry/strategies/default_strategy.hpp>
|
|
#include <boost/geometry/strategies/densify/cartesian.hpp>
|
|
#include <boost/geometry/strategies/densify/geographic.hpp>
|
|
#include <boost/geometry/strategies/densify/spherical.hpp>
|
|
#include <boost/geometry/strategies/detail.hpp>
|
|
#include <boost/geometry/util/condition.hpp>
|
|
#include <boost/geometry/util/range.hpp>
|
|
|
|
|
|
namespace boost { namespace geometry
|
|
{
|
|
|
|
|
|
#ifndef DOXYGEN_NO_DETAIL
|
|
namespace detail { namespace densify
|
|
{
|
|
|
|
template <typename Range>
|
|
struct push_back_policy
|
|
{
|
|
typedef typename boost::range_value<Range>::type point_type;
|
|
|
|
inline explicit push_back_policy(Range & rng)
|
|
: m_rng(rng)
|
|
{}
|
|
|
|
inline void apply(point_type const& p)
|
|
{
|
|
range::push_back(m_rng, p);
|
|
}
|
|
|
|
private:
|
|
Range & m_rng;
|
|
};
|
|
|
|
template <typename Range, typename Point>
|
|
inline void convert_and_push_back(Range & range, Point const& p)
|
|
{
|
|
typename boost::range_value<Range>::type p2;
|
|
geometry::detail::conversion::convert_point_to_point(p, p2);
|
|
range::push_back(range, p2);
|
|
}
|
|
|
|
template <bool AppendLastPoint = true>
|
|
struct densify_range
|
|
{
|
|
template <typename FwdRng, typename MutRng, typename T, typename Strategies>
|
|
static inline void apply(FwdRng const& rng, MutRng & rng_out,
|
|
T const& len, Strategies const& strategies)
|
|
{
|
|
typedef typename boost::range_iterator<FwdRng const>::type iterator_t;
|
|
typedef typename boost::range_value<FwdRng>::type point_t;
|
|
|
|
iterator_t it = boost::begin(rng);
|
|
iterator_t end = boost::end(rng);
|
|
|
|
if (it == end) // empty(rng)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto strategy = strategies.densify(rng);
|
|
push_back_policy<MutRng> policy(rng_out);
|
|
|
|
iterator_t prev = it;
|
|
for ( ++it ; it != end ; prev = it++)
|
|
{
|
|
point_t const& p0 = *prev;
|
|
point_t const& p1 = *it;
|
|
|
|
convert_and_push_back(rng_out, p0);
|
|
|
|
strategy.apply(p0, p1, policy, len);
|
|
}
|
|
|
|
if (BOOST_GEOMETRY_CONDITION(AppendLastPoint))
|
|
{
|
|
convert_and_push_back(rng_out, *prev); // back(rng)
|
|
}
|
|
}
|
|
};
|
|
|
|
template <bool IsClosed1, bool IsClosed2> // false, X
|
|
struct densify_ring
|
|
{
|
|
template <typename Geometry, typename GeometryOut, typename T, typename Strategies>
|
|
static inline void apply(Geometry const& ring, GeometryOut & ring_out,
|
|
T const& len, Strategies const& strategies)
|
|
{
|
|
geometry::detail::densify::densify_range<true>
|
|
::apply(ring, ring_out, len, strategies);
|
|
|
|
if (boost::size(ring) <= 1)
|
|
return;
|
|
|
|
typedef typename point_type<Geometry>::type point_t;
|
|
point_t const& p0 = range::back(ring);
|
|
point_t const& p1 = range::front(ring);
|
|
|
|
auto strategy = strategies.densify(ring);
|
|
push_back_policy<GeometryOut> policy(ring_out);
|
|
|
|
strategy.apply(p0, p1, policy, len);
|
|
|
|
if (BOOST_GEOMETRY_CONDITION(IsClosed2))
|
|
{
|
|
convert_and_push_back(ring_out, p1);
|
|
}
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct densify_ring<true, true>
|
|
: densify_range<true>
|
|
{};
|
|
|
|
template <>
|
|
struct densify_ring<true, false>
|
|
: densify_range<false>
|
|
{};
|
|
|
|
struct densify_convert
|
|
{
|
|
template <typename GeometryIn, typename GeometryOut, typename T, typename Strategy>
|
|
static void apply(GeometryIn const& in, GeometryOut &out,
|
|
T const& , Strategy const& )
|
|
{
|
|
geometry::convert(in, out);
|
|
}
|
|
};
|
|
|
|
}} // namespace detail::densify
|
|
#endif // DOXYGEN_NO_DETAIL
|
|
|
|
|
|
#ifndef DOXYGEN_NO_DISPATCH
|
|
namespace dispatch
|
|
{
|
|
|
|
|
|
template
|
|
<
|
|
typename Geometry,
|
|
typename GeometryOut,
|
|
typename Tag1 = typename tag<Geometry>::type,
|
|
typename Tag2 = typename tag<GeometryOut>::type
|
|
>
|
|
struct densify
|
|
: not_implemented<Tag1, Tag2>
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, point_tag, point_tag>
|
|
: geometry::detail::densify::densify_convert
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, segment_tag, segment_tag>
|
|
: geometry::detail::densify::densify_convert
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, box_tag, box_tag>
|
|
: geometry::detail::densify::densify_convert
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, multi_point_tag, multi_point_tag>
|
|
: geometry::detail::densify::densify_convert
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, linestring_tag, linestring_tag>
|
|
: geometry::detail::densify::densify_range<>
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, multi_linestring_tag, multi_linestring_tag>
|
|
{
|
|
template <typename T, typename Strategy>
|
|
static void apply(Geometry const& mls, GeometryOut & mls_out,
|
|
T const& len, Strategy const& strategy)
|
|
{
|
|
std::size_t count = boost::size(mls);
|
|
range::resize(mls_out, count);
|
|
|
|
for (std::size_t i = 0 ; i < count ; ++i)
|
|
{
|
|
geometry::detail::densify::densify_range<>
|
|
::apply(range::at(mls, i), range::at(mls_out, i),
|
|
len, strategy);
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, ring_tag, ring_tag>
|
|
: geometry::detail::densify::densify_ring
|
|
<
|
|
geometry::closure<Geometry>::value != geometry::open,
|
|
geometry::closure<GeometryOut>::value != geometry::open
|
|
>
|
|
{};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, polygon_tag, polygon_tag>
|
|
{
|
|
template <typename T, typename Strategy>
|
|
static void apply(Geometry const& poly, GeometryOut & poly_out,
|
|
T const& len, Strategy const& strategy)
|
|
{
|
|
apply_ring(exterior_ring(poly), exterior_ring(poly_out),
|
|
len, strategy);
|
|
|
|
std::size_t count = boost::size(interior_rings(poly));
|
|
range::resize(interior_rings(poly_out), count);
|
|
|
|
for (std::size_t i = 0 ; i < count ; ++i)
|
|
{
|
|
apply_ring(range::at(interior_rings(poly), i),
|
|
range::at(interior_rings(poly_out), i),
|
|
len, strategy);
|
|
}
|
|
}
|
|
|
|
template <typename Ring, typename RingOut, typename T, typename Strategy>
|
|
static void apply_ring(Ring const& ring, RingOut & ring_out,
|
|
T const& len, Strategy const& strategy)
|
|
{
|
|
densify<Ring, RingOut, ring_tag, ring_tag>
|
|
::apply(ring, ring_out, len, strategy);
|
|
}
|
|
};
|
|
|
|
template <typename Geometry, typename GeometryOut>
|
|
struct densify<Geometry, GeometryOut, multi_polygon_tag, multi_polygon_tag>
|
|
{
|
|
template <typename T, typename Strategy>
|
|
static void apply(Geometry const& mpoly, GeometryOut & mpoly_out,
|
|
T const& len, Strategy const& strategy)
|
|
{
|
|
std::size_t count = boost::size(mpoly);
|
|
range::resize(mpoly_out, count);
|
|
|
|
for (std::size_t i = 0 ; i < count ; ++i)
|
|
{
|
|
apply_poly(range::at(mpoly, i),
|
|
range::at(mpoly_out, i),
|
|
len, strategy);
|
|
}
|
|
}
|
|
|
|
template <typename Poly, typename PolyOut, typename T, typename Strategy>
|
|
static void apply_poly(Poly const& poly, PolyOut & poly_out,
|
|
T const& len, Strategy const& strategy)
|
|
{
|
|
densify<Poly, PolyOut, polygon_tag, polygon_tag>::
|
|
apply(poly, poly_out, len, strategy);
|
|
}
|
|
};
|
|
|
|
|
|
} // namespace dispatch
|
|
#endif // DOXYGEN_NO_DISPATCH
|
|
|
|
|
|
namespace resolve_strategy
|
|
{
|
|
|
|
template
|
|
<
|
|
typename Strategies,
|
|
bool IsUmbrella = strategies::detail::is_umbrella_strategy<Strategies>::value
|
|
>
|
|
struct densify
|
|
{
|
|
template <typename Geometry, typename Distance>
|
|
static inline void apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategies const& strategies)
|
|
{
|
|
dispatch::densify
|
|
<
|
|
Geometry, Geometry
|
|
>::apply(geometry, out, max_distance, strategies);
|
|
}
|
|
};
|
|
|
|
template <typename Strategy>
|
|
struct densify<Strategy, false>
|
|
{
|
|
template <typename Geometry, typename Distance>
|
|
static inline void apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategy const& strategy)
|
|
{
|
|
using strategies::densify::services::strategy_converter;
|
|
|
|
dispatch::densify
|
|
<
|
|
Geometry, Geometry
|
|
>::apply(geometry, out, max_distance,
|
|
strategy_converter<Strategy>::get(strategy));
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct densify<default_strategy, false>
|
|
{
|
|
template <typename Geometry, typename Distance>
|
|
static inline void apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
default_strategy const&)
|
|
{
|
|
typedef typename strategies::densify::services::default_strategy
|
|
<
|
|
Geometry
|
|
>::type strategies_type;
|
|
|
|
dispatch::densify
|
|
<
|
|
Geometry, Geometry
|
|
>::apply(geometry, out, max_distance, strategies_type());
|
|
}
|
|
};
|
|
|
|
} // namespace resolve_strategy
|
|
|
|
|
|
namespace resolve_dynamic {
|
|
|
|
template <typename Geometry, typename Tag = typename tag<Geometry>::type>
|
|
struct densify
|
|
{
|
|
template <typename Distance, typename Strategy>
|
|
static inline void apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategy const& strategy)
|
|
{
|
|
resolve_strategy::densify
|
|
<
|
|
Strategy
|
|
>::apply(geometry, out, max_distance, strategy);
|
|
}
|
|
};
|
|
|
|
template <typename Geometry>
|
|
struct densify<Geometry, dynamic_geometry_tag>
|
|
{
|
|
template <typename Distance, typename Strategy>
|
|
static inline void
|
|
apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategy const& strategy)
|
|
{
|
|
traits::visit<Geometry>::apply([&](auto const& g)
|
|
{
|
|
using geom_t = util::remove_cref_t<decltype(g)>;
|
|
geom_t o;
|
|
densify<geom_t>::apply(g, o, max_distance, strategy);
|
|
out = std::move(o);
|
|
}, geometry);
|
|
}
|
|
};
|
|
|
|
template <typename Geometry>
|
|
struct densify<Geometry, geometry_collection_tag>
|
|
{
|
|
template <typename Distance, typename Strategy>
|
|
static inline void
|
|
apply(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategy const& strategy)
|
|
{
|
|
detail::visit_breadth_first([&](auto const& g)
|
|
{
|
|
using geom_t = util::remove_cref_t<decltype(g)>;
|
|
geom_t o;
|
|
densify<geom_t>::apply(g, o, max_distance, strategy);
|
|
traits::emplace_back<Geometry>::apply(out, std::move(o));
|
|
return true;
|
|
}, geometry);
|
|
}
|
|
};
|
|
|
|
} // namespace resolve_dynamic
|
|
|
|
|
|
/*!
|
|
\brief Densify a geometry using a specified strategy
|
|
\ingroup densify
|
|
\tparam Geometry \tparam_geometry
|
|
\tparam Distance A numerical distance measure
|
|
\tparam Strategy A type fulfilling a DensifyStrategy concept
|
|
\param geometry Input geometry, to be densified
|
|
\param out Output geometry, densified version of the input geometry
|
|
\param max_distance Distance threshold (in units depending on strategy)
|
|
\param strategy Densify strategy to be used for densification
|
|
|
|
\qbk{distinguish,with strategy}
|
|
\qbk{[include reference/algorithms/densify.qbk]}
|
|
|
|
\qbk{
|
|
[heading Available Strategies]
|
|
\* [link geometry.reference.strategies.strategy_densify_cartesian Cartesian]
|
|
\* [link geometry.reference.strategies.strategy_densify_spherical Spherical]
|
|
\* [link geometry.reference.strategies.strategy_densify_geographic Geographic]
|
|
|
|
[heading Example]
|
|
[densify_strategy]
|
|
[densify_strategy_output]
|
|
|
|
[heading See also]
|
|
\* [link geometry.reference.algorithms.line_interpolate line_interpolate]
|
|
}
|
|
*/
|
|
template <typename Geometry, typename Distance, typename Strategy>
|
|
inline void densify(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance,
|
|
Strategy const& strategy)
|
|
{
|
|
concepts::check<Geometry>();
|
|
|
|
if (max_distance <= Distance(0))
|
|
{
|
|
BOOST_THROW_EXCEPTION(geometry::invalid_input_exception());
|
|
}
|
|
|
|
geometry::clear(out);
|
|
|
|
resolve_dynamic::densify
|
|
<
|
|
Geometry
|
|
>::apply(geometry, out, max_distance, strategy);
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Densify a geometry
|
|
\ingroup densify
|
|
\tparam Geometry \tparam_geometry
|
|
\tparam Distance A numerical distance measure
|
|
\param geometry Input geometry, to be densified
|
|
\param out Output geometry, densified version of the input geometry
|
|
\param max_distance Distance threshold (in units depending on coordinate system)
|
|
|
|
\qbk{[include reference/algorithms/densify.qbk]}
|
|
|
|
\qbk{
|
|
[heading Example]
|
|
[densify]
|
|
[densify_output]
|
|
|
|
[heading See also]
|
|
\* [link geometry.reference.algorithms.line_interpolate line_interpolate]
|
|
}
|
|
*/
|
|
template <typename Geometry, typename Distance>
|
|
inline void densify(Geometry const& geometry,
|
|
Geometry& out,
|
|
Distance const& max_distance)
|
|
{
|
|
densify(geometry, out, max_distance, default_strategy());
|
|
}
|
|
|
|
|
|
}} // namespace boost::geometry
|
|
|
|
#endif // BOOST_GEOMETRY_ALGORITHMS_DENSIFY_HPP
|