[envelope] Add separate strategies for linestrings and rings.

This commit is contained in:
Adam Wulkiewicz
2021-11-02 18:12:22 +01:00
parent d57423fdc4
commit b0a2b52ef3
8 changed files with 246 additions and 34 deletions

View File

@@ -5,8 +5,8 @@
// Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2013-2020.
// Modifications copyright (c) 2013-2020, Oracle and/or its affiliates.
// This file was modified by Oracle on 2013-2021.
// Modifications copyright (c) 2013-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
@@ -33,7 +33,6 @@
#include <boost/geometry/algorithms/detail/envelope/segment.hpp>
#include <boost/geometry/algorithms/detail/normalize.hpp>
#include <boost/geometry/algorithms/dispatch/disjoint.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/formulas/vertex_longitude.hpp>

View File

@@ -39,6 +39,11 @@ struct envelope_polygon
if (geometry::is_empty(ext_ring))
{
// TODO: In spherical and geographic this is ambiguous. A hole technically is a ring
// defining the outside so the envelope would cover more than half of the globe.
// So depending on what we want we could consider reversing holes before passing them
// below.
// use dummy multi polygon to get the strategy because there is no multi ring concept
using strategy_t = decltype(strategy.envelope(detail::dummy_multi_polygon(),
detail::dummy_box()));

View File

@@ -76,16 +76,25 @@ public:
>(base_t::m_spheroid);
}
template <typename Geometry, typename Box>
auto envelope(Geometry const&, Box const&,
util::enable_if_linestring_t<Geometry> * = nullptr) const
{
return strategy::envelope::geographic_linestring
<
FormulaPolicy, Spheroid, CalculationType
>(base_t::m_spheroid);
}
template <typename Geometry, typename Box>
auto envelope(Geometry const&, Box const&,
std::enable_if_t
<
util::is_linestring<Geometry>::value
|| util::is_ring<Geometry>::value
util::is_ring<Geometry>::value
|| util::is_polygon<Geometry>::value
> * = nullptr) const
{
return strategy::envelope::geographic_range
return strategy::envelope::geographic_ring
<
FormulaPolicy, Spheroid, CalculationType
>(base_t::m_spheroid);

View File

@@ -74,16 +74,22 @@ struct spherical
return strategy::envelope::spherical_segment<CalculationType>();
}
template <typename Geometry, typename Box>
static auto envelope(Geometry const&, Box const&,
util::enable_if_linestring_t<Geometry> * = nullptr)
{
return strategy::envelope::spherical_linestring<CalculationType>();
}
template <typename Geometry, typename Box>
static auto envelope(Geometry const&, Box const&,
std::enable_if_t
<
util::is_linestring<Geometry>::value
|| util::is_ring<Geometry>::value
util::is_ring<Geometry>::value
|| util::is_polygon<Geometry>::value
> * = nullptr)
{
return strategy::envelope::spherical_range<CalculationType>();
return strategy::envelope::spherical_ring<CalculationType>();
}
template <typename Geometry, typename Box>

View File

@@ -14,6 +14,7 @@
#include <type_traits>
#include <boost/geometry/algorithms/detail/distance/segment_to_box.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/normalize.hpp>

View File

@@ -14,44 +14,92 @@
#include <boost/geometry/strategy/geographic/expand_segment.hpp>
#include <boost/geometry/strategy/spherical/envelope_range.hpp>
// TEMP - get rid of this dependency
#include <boost/geometry/strategies/geographic/point_in_poly_winding.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace envelope
{
// TODO: divide into geographic_linestring and geographic_ring
template
<
typename FormulaPolicy = strategy::andoyer,
typename Spheroid = geometry::srs::spheroid<double>,
typename CalculationType = void
>
class geographic_range
class geographic_linestring
{
public:
using model_type = Spheroid;
geographic_range()
geographic_linestring()
: m_spheroid()
{}
explicit geographic_range(Spheroid const& spheroid)
explicit geographic_linestring(Spheroid const& spheroid)
: m_spheroid(spheroid)
{}
template <typename Range, typename Box>
void apply(Range const& range, Box& mbr) const
{
detail::spheroidal_range(range, mbr,
envelope::geographic_segment
detail::spheroidal_linestring(range, mbr,
envelope::geographic_segment
<
FormulaPolicy, Spheroid, CalculationType
>(m_spheroid),
expand::geographic_segment
<
FormulaPolicy, Spheroid, CalculationType
>(m_spheroid));
}
Spheroid model() const
{
return m_spheroid;
}
private:
Spheroid m_spheroid;
};
template
<
typename FormulaPolicy = strategy::andoyer,
typename Spheroid = geometry::srs::spheroid<double>,
typename CalculationType = void
>
class geographic_ring
{
public:
using model_type = Spheroid;
geographic_ring()
: m_spheroid()
{}
explicit geographic_ring(Spheroid const& spheroid)
: m_spheroid(spheroid)
{}
template <typename Range, typename Box>
void apply(Range const& range, Box& mbr) const
{
detail::spheroidal_ring(range, mbr,
envelope::geographic_segment
<
FormulaPolicy, Spheroid, CalculationType
>(m_spheroid),
expand::geographic_segment
expand::geographic_segment
<
FormulaPolicy, Spheroid, CalculationType
>(m_spheroid),
within::geographic_winding
<
void, void,
FormulaPolicy, Spheroid, CalculationType
>(m_spheroid));
}

View File

@@ -10,6 +10,7 @@
#ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_RANGE_HPP
#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_RANGE_HPP
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/detail/envelope/initialize.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/strategy/spherical/envelope_point.hpp>
@@ -17,6 +18,11 @@
#include <boost/geometry/strategy/spherical/expand_segment.hpp>
#include <boost/geometry/views/closeable_view.hpp>
// TEMP - get rid of these dependencies
#include <boost/geometry/algorithms/detail/within/point_in_geometry.hpp>
#include <boost/geometry/strategies/spherical/point_in_poly_winding.hpp>
namespace boost { namespace geometry
{
@@ -28,14 +34,12 @@ namespace detail
{
template <typename Range, typename Box, typename EnvelopeStrategy, typename ExpandStrategy>
inline void spheroidal_range(Range const& range, Box& mbr,
EnvelopeStrategy const& envelope_strategy,
ExpandStrategy const& expand_strategy)
inline void spheroidal_linestring(Range const& range, Box& mbr,
EnvelopeStrategy const& envelope_strategy,
ExpandStrategy const& expand_strategy)
{
geometry::detail::closed_view<Range const> closed_range(range);
auto it = boost::begin(closed_range);
auto const end = boost::end(closed_range);
auto it = boost::begin(range);
auto const end = boost::end(range);
if (it == end)
{
// initialize box (assign inverse)
@@ -68,21 +72,125 @@ inline void spheroidal_range(Range const& range, Box& mbr,
}
}
template <typename Ring, typename WithinStrategy>
inline bool pole_within(bool north_pole, Ring const& ring, WithinStrategy const& within_strategy)
{
if (boost::size(ring) < core_detail::closure::minimum_ring_size
<
geometry::closure<Ring>::value
>::value)
{
return false;
}
using point_t = typename geometry::point_type<Ring>::type;
using coord_t = typename geometry::coordinate_type<point_t>::type;
using units_t = typename geometry::detail::cs_angular_units<point_t>::type;
using constants_t = math::detail::constants_on_spheroid<coord_t, units_t>;
point_t point;
geometry::assign_zero(point);
if (north_pole)
{
geometry::set<1>(point, constants_t::max_latitude());
}
else
{
geometry::set<1>(point, constants_t::min_latitude());
}
geometry::detail::closed_clockwise_view<Ring const> view(ring);
return geometry::detail::within::point_in_range(point, view, within_strategy) > 0;
}
template <typename Range, typename Box, typename EnvelopeStrategy, typename ExpandStrategy, typename WithinStrategy>
inline void spheroidal_ring(Range const& range, Box& mbr,
EnvelopeStrategy const& envelope_strategy,
ExpandStrategy const& expand_strategy,
WithinStrategy const& within_strategy)
{
geometry::detail::closed_view<Range const> closed_range(range);
spheroidal_linestring(closed_range, mbr, envelope_strategy, expand_strategy);
using coord_t = typename geometry::coordinate_type<Box>::type;
using point_t = typename geometry::point_type<Box>::type;
using units_t = typename geometry::detail::cs_angular_units<point_t>::type;
using constants_t = math::detail::constants_on_spheroid<coord_t, units_t>;
coord_t const two_pi = constants_t::period();
coord_t const lon_min = geometry::get<0, 0>(mbr);
coord_t const lon_max = geometry::get<1, 0>(mbr);
// If box covers the whole longitude range it is possible that the ring contains
// one of the poles.
// TODO: Technically it is possible that a reversed ring may cover more than
// half of the globe and mbr of it's linear ring may be small and not cover the
// longitude range.
if (lon_max - lon_min >= two_pi)
{
coord_t const lat_n_pole = constants_t::max_latitude();
coord_t const lat_s_pole = constants_t::min_latitude();
coord_t lat_min = geometry::get<0, 1>(mbr);
coord_t lat_max = geometry::get<1, 1>(mbr);
// Normalize box latitudes, just in case
if (math::equals(lat_min, lat_s_pole))
{
lat_min = lat_s_pole;
}
if (math::equals(lat_max, lat_n_pole))
{
lat_max = lat_n_pole;
}
// TODO - implement something simpler than within strategy because here
// we know that neither min nor max is a pole so there is no segment which
// contains a pole, no endpoint, no vertex at pole, there are no antipodal
// points. So many special cases can be ignored.
if (lat_max < lat_n_pole)
{
if (pole_within(true, range, within_strategy))
{
lat_max = lat_n_pole;
}
}
if (lat_min > lat_s_pole)
{
if (pole_within(false, range, within_strategy))
{
lat_min = lat_s_pole;
}
}
geometry::set<0, 1>(mbr, lat_min);
geometry::set<1, 1>(mbr, lat_max);
}
}
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
// TODO: divide into spherical_linestring and spherical_ring
template <typename CalculationType = void>
class spherical_range
class spherical_linestring
{
public:
template <typename Range, typename Box>
static inline void apply(Range const& range, Box& mbr)
{
detail::spheroidal_range(range, mbr,
envelope::spherical_segment<CalculationType>(),
expand::spherical_segment<CalculationType>());
detail::spheroidal_linestring(range, mbr,
envelope::spherical_segment<CalculationType>(),
expand::spherical_segment<CalculationType>());
}
};
template <typename CalculationType = void>
class spherical_ring
{
public:
template <typename Range, typename Box>
static inline void apply(Range const& range, Box& mbr)
{
detail::spheroidal_ring(range, mbr,
envelope::spherical_segment<CalculationType>(),
expand::spherical_segment<CalculationType>(),
within::spherical_winding<void, void, CalculationType>());
}
};