[algorithms][distance] re-factor point-to-geometry distance computations;

include in point_to_geometry.hpp implementation of point-to-multigeometry
distance computations and dispatches; compute, whenever applicable, the closest
feature of the goemetry to the point, and then compute the distance as the
distance of this closest feature to the point; optimize the performance of
point-to-ring and point-to-polygon by not computing both containment and distance
to the boundary: compute the distance to the boundary only if the containment
test fails;
This commit is contained in:
Menelaos Karavelas
2014-10-13 15:14:23 +03:00
parent e206352d12
commit e74accfab9

View File

@@ -20,33 +20,32 @@
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP
#include <utility>
#include <iterator>
#include <boost/core/ignore_unused.hpp>
#include <boost/range.hpp>
#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/exterior_ring.hpp>
#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/core/interior_type.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/tags.hpp>
#include <boost/geometry/strategies/distance_comparable_to_regular.hpp>
#include <boost/geometry/views/closeable_view.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/algorithms/intersects.hpp>
#include <boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp>
#include <boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp>
#include <boost/geometry/algorithms/detail/distance/iterator_selector.hpp>
#include <boost/geometry/algorithms/dispatch/distance.hpp>
#include <boost/geometry/algorithms/detail/distance/default_strategies.hpp>
namespace boost { namespace geometry
{
@@ -63,7 +62,7 @@ struct point_to_point
typename strategy::distance::services::return_type<Strategy, P1, P2>::type
apply(P1 const& p1, P2 const& p2, Strategy const& strategy)
{
boost::ignore_unused_variable_warning(strategy);
boost::ignore_unused(strategy);
return strategy.apply(p1, p2);
}
};
@@ -84,12 +83,10 @@ private:
Strategy
>::type comparable_strategy;
typedef typename strategy::distance::services::return_type
typedef detail::closest_feature::point_to_point_range
<
comparable_strategy,
Point,
typename boost::range_value<Range>::type
>::type comparable_return_type;
Point, Range, Closure, comparable_strategy
> closest_feature_type;
public:
typedef typename strategy::distance::services::return_type
@@ -102,68 +99,24 @@ public:
static inline return_type apply(Point const& point, Range const& range,
Strategy const& strategy)
{
comparable_strategy c_strategy =
strategy::distance::services::get_comparable
<
Strategy
>::apply(strategy);
return_type const zero = return_type(0);
comparable_return_type const zero = comparable_return_type(0);
if (boost::size(range) == 0)
if ( boost::size(range) == 0 )
{
return zero;
}
typedef typename closeable_view<Range const, Closure>::type view_type;
namespace sds = strategy::distance::services;
view_type view(range);
typename closest_feature_type::return_type cf
= closest_feature_type::apply(point,
range,
sds::get_comparable
<
Strategy
>::apply(strategy));
// line of one point: return point distance
typedef typename boost::range_iterator<view_type const>::type iterator_type;
iterator_type it = boost::begin(view);
iterator_type prev = it++;
if (it == boost::end(view))
{
return strategy::distance::services::comparable_to_regular
<
comparable_strategy, Strategy, Point,
typename boost::range_value<Range>::type
>::apply( c_strategy.apply(point,
*boost::begin(view),
*boost::begin(view)) );
}
// start with first segment distance
comparable_return_type cd = c_strategy.apply(point, *prev, *it);
// check if other segments are closer
for (++prev, ++it; it != boost::end(view); ++prev, ++it)
{
comparable_return_type cds = c_strategy.apply(point, *prev, *it);
if (geometry::math::equals(cds, zero))
{
return strategy::distance::services::comparable_to_regular
<
comparable_strategy,
Strategy,
Point,
typename boost::range_value<Range>::type
>::apply(zero);
}
else if (cds < cd)
{
cd = cds;
}
}
return strategy::distance::services::comparable_to_regular
<
comparable_strategy,
Strategy,
Point,
typename boost::range_value<Range>::type
>::apply(cd);
return strategy.apply(point, *cf.first, *cf.second);
}
};
@@ -177,35 +130,28 @@ template
>
struct point_to_ring
{
typedef std::pair
typedef typename strategy::distance::services::return_type
<
typename strategy::distance::services::return_type
<
Strategy, Point, typename point_type<Ring>::type
>::type,
bool
> distance_containment;
Strategy, Point, typename point_type<Ring>::type
>::type return_type;
static inline distance_containment apply(Point const& point,
Ring const& ring,
Strategy const& strategy)
static inline return_type apply(Point const& point,
Ring const& ring,
Strategy const& strategy)
{
return distance_containment
(
point_to_range
<
Point,
Ring,
Closure,
Strategy
>::apply(point, ring, strategy),
geometry::within(point, ring)
);
if ( geometry::within(point, ring) )
{
return return_type(0);
}
return detail::distance::point_to_range
<
Point, Ring, closure<Ring>::value, Strategy
>::apply(point, ring, strategy);
}
};
template
<
typename Point,
@@ -215,79 +161,150 @@ template
>
class point_to_polygon
{
public:
typedef typename strategy::distance::services::return_type
<
Strategy, Point, typename point_type<Polygon>::type
>::type return_type;
private:
typedef point_to_range
<
Point, typename ring_type<Polygon>::type, Closure, Strategy
> per_ring;
struct distance_to_interior_rings
{
template <typename InteriorRingIterator>
static inline return_type apply(Point const& point,
InteriorRingIterator first,
InteriorRingIterator beyond,
Strategy const& strategy)
{
for (InteriorRingIterator it = first; it != beyond; ++it)
{
if ( geometry::within(point, *it) )
{
// the point is inside a polygon hole, so its distance
// to the polygon its distance to the polygon's
// hole boundary
return per_ring::apply(point, *it, strategy);
}
}
return 0;
}
template <typename InteriorRings>
static inline return_type apply(Point const& point,
InteriorRings const& interior_rings,
Strategy const& strategy)
{
return apply(point,
boost::begin(interior_rings),
boost::end(interior_rings),
strategy);
}
};
public:
static inline return_type apply(Point const& point,
Polygon const& polygon,
Strategy const& strategy)
{
if ( !geometry::within(point, exterior_ring(polygon)) )
{
// the point is outside the exterior ring, so its distance
// to the polygon is its distance to the polygon's exterior ring
return per_ring::apply(point, exterior_ring(polygon), strategy);
}
// Check interior rings
return distance_to_interior_rings::apply(point,
interior_rings(polygon),
strategy);
}
};
template
<
typename Point,
typename MultiGeometry,
typename Strategy,
bool CheckWithin = false
>
class point_to_multigeometry
{
private:
typedef typename strategy::distance::services::comparable_type
<
Strategy
>::type comparable_strategy;
typedef typename strategy::distance::services::return_type
<
comparable_strategy, Point, typename point_type<Polygon>::type
>::type comparable_return_type;
typedef std::pair
<
comparable_return_type, bool
> comparable_distance_containment;
public:
typedef typename strategy::distance::services::return_type
<
Strategy, Point, typename point_type<Polygon>::type
Strategy,
Point,
typename point_type<MultiGeometry>::type
>::type return_type;
typedef std::pair<return_type, bool> distance_containment;
static inline distance_containment apply(Point const& point,
Polygon const& polygon,
Strategy const& strategy)
static inline return_type apply(Point const& point,
MultiGeometry const& multigeometry,
Strategy const& strategy)
{
comparable_strategy c_strategy =
strategy::distance::services::get_comparable
<
Strategy
>::apply(strategy);
typedef iterator_selector<MultiGeometry const> selector_type;
// Check distance to all rings
typedef point_to_ring
typedef detail::closest_feature::geometry_to_range point_to_range;
namespace sds = strategy::distance::services;
typename selector_type::iterator_type it_min
= point_to_range::apply(point,
selector_type::begin(multigeometry),
selector_type::end(multigeometry),
sds::get_comparable
<
Strategy
>::apply(strategy));
return dispatch::distance
<
Point,
typename ring_type<Polygon>::type,
Closure,
comparable_strategy
> per_ring;
typename std::iterator_traits
<
typename selector_type::iterator_type
>::value_type,
Strategy
>::apply(point, *it_min, strategy);
}
};
comparable_distance_containment dc =
per_ring::apply(point, exterior_ring(polygon), c_strategy);
typename interior_return_type<Polygon const>::type rings
= interior_rings(polygon);
for (typename boost::range_iterator
<
typename interior_type<Polygon const>::type const
>::type it = boost::begin(rings);
it != boost::end(rings); ++it)
template <typename Point, typename MultiGeometry, typename Strategy>
struct point_to_multigeometry<Point, MultiGeometry, Strategy, true>
{
public:
typedef typename strategy::distance::services::return_type
<
Strategy,
Point,
typename point_type<MultiGeometry>::type
>::type return_type;
static inline return_type apply(Point const& point,
MultiGeometry const& multigeometry,
Strategy const& strategy)
{
if ( geometry::within(point, multigeometry) )
{
comparable_distance_containment dcr =
per_ring::apply(point, *it, c_strategy);
if (dcr.first < dc.first)
{
dc.first = dcr.first;
}
// If it was inside, and also inside inner ring,
// turn off the inside-flag, it is outside the polygon
if (dc.second && dcr.second)
{
dc.second = false;
}
return 0;
}
return_type rd = strategy::distance::services::comparable_to_regular
return point_to_multigeometry
<
comparable_strategy, Strategy, Point, Polygon
>::apply(dc.first);
return std::make_pair(rd, dc.second);
Point, MultiGeometry, Strategy, false
>::apply(point, multigeometry, strategy);
}
};
@@ -307,111 +324,73 @@ namespace dispatch
template <typename P1, typename P2, typename Strategy>
struct distance
<
P1, P2, Strategy,
point_tag, point_tag, strategy_tag_distance_point_point,
false
>
: detail::distance::point_to_point<P1, P2, Strategy>
P1, P2, Strategy, point_tag, point_tag,
strategy_tag_distance_point_point, false
> : detail::distance::point_to_point<P1, P2, Strategy>
{};
// Point-line version 2, where point-segment strategy is specified
template <typename Point, typename Linestring, typename Strategy>
struct distance
<
Point, Linestring, Strategy,
point_tag, linestring_tag, strategy_tag_distance_point_segment,
false
> : detail::distance::point_to_range<Point, Linestring, closed, Strategy>
<
Point, Linestring, Strategy, point_tag, linestring_tag,
strategy_tag_distance_point_segment, false
> : detail::distance::point_to_range<Point, Linestring, closed, Strategy>
{};
// Point-ring , where point-segment strategy is specified
template <typename Point, typename Ring, typename Strategy>
struct distance
<
Point, Ring, Strategy,
point_tag, ring_tag, strategy_tag_distance_point_segment,
false
>
{
typedef typename strategy::distance::services::return_type
<
Point, Ring, Strategy, point_tag, ring_tag,
strategy_tag_distance_point_segment, false
> : detail::distance::point_to_ring
<
Strategy, Point, typename point_type<Ring>::type
>::type return_type;
static inline return_type apply(Point const& point,
Ring const& ring,
Strategy const& strategy)
{
std::pair<return_type, bool>
dc = detail::distance::point_to_ring
<
Point, Ring,
geometry::closure<Ring>::value,
Strategy
>::apply(point, ring, strategy);
return dc.second ? return_type(0) : dc.first;
}
};
Point, Ring, closure<Ring>::value, Strategy
>
{};
// Point-polygon , where point-segment strategy is specified
template <typename Point, typename Polygon, typename Strategy>
struct distance
<
Point, Polygon, Strategy, point_tag, polygon_tag,
strategy_tag_distance_point_segment, false
>
{
typedef typename strategy::distance::services::return_type
<
Point, Polygon, Strategy, point_tag, polygon_tag,
strategy_tag_distance_point_segment, false
> : detail::distance::point_to_polygon
<
Strategy, Point, typename point_type<Polygon>::type
>::type return_type;
static inline return_type apply(Point const& point,
Polygon const& polygon,
Strategy const& strategy)
{
std::pair<return_type, bool>
dc = detail::distance::point_to_polygon
<
Point, Polygon,
geometry::closure<Polygon>::value,
Strategy
>::apply(point, polygon, strategy);
return dc.second ? return_type(0) : dc.first;
}
};
Point, Polygon, closure<Polygon>::value, Strategy
>
{};
// Point-segment version 2, with point-segment strategy
template <typename Point, typename Segment, typename Strategy>
struct distance
<
Point, Segment, Strategy,
point_tag, segment_tag, strategy_tag_distance_point_segment,
false
>
<
Point, Segment, Strategy, point_tag, segment_tag,
strategy_tag_distance_point_segment, false
>
{
static inline typename return_type<Strategy, Point, typename point_type<Segment>::type>::type
apply(Point const& point,
Segment const& segment,
Strategy const& strategy)
static inline typename strategy::distance::services::return_type
<
Strategy, Point, typename point_type<Segment>::type
>::type apply(Point const& point,
Segment const& segment,
Strategy const& strategy)
{
typename point_type<Segment>::type p[2];
geometry::detail::assign_point_from_index<0>(segment, p[0]);
geometry::detail::assign_point_from_index<1>(segment, p[1]);
boost::ignore_unused_variable_warning(strategy);
boost::ignore_unused(strategy);
return strategy.apply(point, p[0], p[1]);
}
};
template <typename Point, typename Box, typename Strategy>
struct distance
<
@@ -425,12 +404,75 @@ struct distance
>::type
apply(Point const& point, Box const& box, Strategy const& strategy)
{
boost::ignore_unused_variable_warning(strategy);
boost::ignore_unused(strategy);
return strategy.apply(point, box);
}
};
template<typename Point, typename MultiPoint, typename Strategy>
struct distance
<
Point, MultiPoint, Strategy, point_tag, multi_point_tag,
strategy_tag_distance_point_point, false
> : detail::distance::point_to_multigeometry
<
Point, MultiPoint, Strategy
>
{};
template<typename Point, typename MultiLinestring, typename Strategy>
struct distance
<
Point, MultiLinestring, Strategy, point_tag, multi_linestring_tag,
strategy_tag_distance_point_segment, false
> : detail::distance::point_to_multigeometry
<
Point, MultiLinestring, Strategy
>
{};
template<typename Point, typename MultiPolygon, typename Strategy>
struct distance
<
Point, MultiPolygon, Strategy, point_tag, multi_polygon_tag,
strategy_tag_distance_point_segment, false
> : detail::distance::point_to_multigeometry
<
Point, MultiPolygon, Strategy, true
>
{};
template <typename Point, typename Linear, typename Strategy>
struct distance
<
Point, Linear, Strategy, point_tag, linear_tag,
strategy_tag_distance_point_segment, false
> : distance
<
Point, Linear, Strategy,
point_tag, typename tag<Linear>::type,
strategy_tag_distance_point_segment, false
>
{};
template <typename Point, typename Areal, typename Strategy>
struct distance
<
Point, Areal, Strategy, point_tag, areal_tag,
strategy_tag_distance_point_segment, false
> : distance
<
Point, Areal, Strategy,
point_tag, typename tag<Areal>::type,
strategy_tag_distance_point_segment, false
>
{};
} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH