summaryrefslogtreecommitdiff
path: root/boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp')
-rw-r--r--boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp293
1 files changed, 185 insertions, 108 deletions
diff --git a/boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp b/boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp
index fe32f77236..14c5bd4eda 100644
--- a/boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp
+++ b/boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp
@@ -1,13 +1,14 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
-// Copyright (c) 2008-2014 Bruno Lalande, Paris, France.
-// Copyright (c) 2008-2014 Barend Gehrels, Amsterdam, the Netherlands.
-// Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
+// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
+// Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
+// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
-// This file was modified by Oracle on 2014.
-// Modifications copyright (c) 2014, Oracle and/or its affiliates.
+// This file was modified by Oracle on 2014, 2015.
+// Modifications copyright (c) 2014-2015, 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
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@@ -16,14 +17,23 @@
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP
+#include <boost/config.hpp>
+#include <boost/concept_check.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/type_traits/is_void.hpp>
#include <boost/geometry/core/access.hpp>
+#include <boost/geometry/core/assert.hpp>
+#include <boost/geometry/core/point_type.hpp>
+#include <boost/geometry/core/radian_access.hpp>
+#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/strategies/distance.hpp>
+#include <boost/geometry/strategies/concepts/distance_concept.hpp>
+#include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
#include <boost/geometry/util/math.hpp>
-#include <boost/geometry/util/calculation_type.hpp>
-
+#include <boost/geometry/algorithms/detail/assign_box_corners.hpp>
namespace boost { namespace geometry
@@ -32,131 +42,197 @@ namespace boost { namespace geometry
namespace strategy { namespace distance
{
+
+/*!
+\brief Strategy functor for distance point to box calculation
+\ingroup strategies
+\details Class which calculates the distance of a point to a box, for
+points and boxes on a sphere or globe
+\tparam CalculationType \tparam_calculation
+\tparam Strategy underlying point-segment distance strategy, defaults
+to cross track
+
+\qbk{
+[heading See also]
+[link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)]
+}
+
+*/
template
<
typename CalculationType = void,
- typename Strategy = haversine<double, CalculationType>
+ typename Strategy = cross_track<CalculationType>
>
class cross_track_point_box
{
public:
template <typename Point, typename Box>
struct return_type
- : promote_floating_point
- <
- typename select_calculation_type
- <
- Point,
- typename point_type<Box>::type,
- CalculationType
- >::type
- >
+ : services::return_type<Strategy, Point, typename point_type<Box>::type>
{};
+ typedef typename Strategy::radius_type radius_type;
+
inline cross_track_point_box()
{}
explicit inline cross_track_point_box(typename Strategy::radius_type const& r)
- : m_pp_strategy(r)
+ : m_ps_strategy(r)
{}
inline cross_track_point_box(Strategy const& s)
- : m_pp_strategy(s)
+ : m_ps_strategy(s)
{}
-
+
+
+ // It might be useful in the future
+ // to overload constructor with strategy info.
+ // crosstrack(...) {}
+
template <typename Point, typename Box>
inline typename return_type<Point, Box>::type
apply(Point const& point, Box const& box) const
{
-
#if !defined(BOOST_MSVC)
BOOST_CONCEPT_ASSERT
(
- (concept::PointDistanceStrategy
+ (concept::PointSegmentDistanceStrategy
<
- Strategy, Point,
- typename point_type<Box>::type
+ Strategy, Point, typename point_type<Box>::type
>)
);
#endif
+ // this method assumes that the coordinates of the point and
+ // the box are normalized
+
typedef typename return_type<Point, Box>::type return_type;
- typedef typename point_type<Box>::type box_point_t;
-
- // Create (counterclockwise) array of points, the fifth one closes it
- // If every point is on the LEFT side (=1) or ON the border (=0)
- // the distance should be equal to 0.
+ typedef typename point_type<Box>::type box_point_type;
// TODO: This strategy as well as other cross-track strategies
// and therefore e.g. spherical within(Point, Box) may not work
// properly for a Box degenerated to a Segment or Point
- boost::array<box_point_t, 5> bp;
- geometry::detail::assign_box_corners_oriented<true>(box, bp);
- bp[4] = bp[0];
+ box_point_type bottom_left, bottom_right, top_left, top_right;
+ geometry::detail::assign_box_corners(box,
+ bottom_left, bottom_right,
+ top_left, top_right);
+
+ return_type const plon = geometry::get_as_radian<0>(point);
+ return_type const plat = geometry::get_as_radian<1>(point);
+
+ return_type const lon_min = geometry::get_as_radian<0>(bottom_left);
+ return_type const lat_min = geometry::get_as_radian<1>(bottom_left);
+ return_type const lon_max = geometry::get_as_radian<0>(top_right);
+ return_type const lat_max = geometry::get_as_radian<1>(top_right);
+
+ return_type const pi = math::pi<return_type>();
+ return_type const two_pi = math::two_pi<return_type>();
+
+ // First check if the point is within the band defined by the
+ // minimum and maximum longitude of the box; if yes, determine
+ // if the point is above, below or inside the box and compute
+ // the distance (easy in this case)
+ //
+ // Notice that the point may not be inside the longitude range
+ // of the box, but the shifted point may be inside the
+ // longitude range of the box; in this case the point is still
+ // considered as inside the longitude range band of the box
+ if ((plon >= lon_min && plon <= lon_max) || plon + two_pi <= lon_max)
+ {
+ if (plat > lat_max)
+ {
+ return services::result_from_distance
+ <
+ Strategy, Point, box_point_type
+ >::apply(m_ps_strategy, radius() * (plat - lat_max));
+ }
+ else if (plat < lat_min)
+ {
+ return services::result_from_distance
+ <
+ Strategy, Point, box_point_type
+ >::apply(m_ps_strategy, radius() * (lat_min - plat));
+ }
+ else
+ {
+ BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max);
+ return return_type(0);
+ }
+ }
- for (int i = 1; i < 5; i++)
+ // Otherwise determine which αμονγ the two medirian segments of the
+ // box the point is closest to, and compute the distance of
+ // the point to this closest segment
+
+ // Below lon_midway is the longitude of the meridian that:
+ // (1) is midway between the meridians of the left and right
+ // meridians of the box, and
+ // (2) does not intersect the box
+ return_type const two = 2.0;
+ bool use_left_segment;
+ if (lon_max > pi)
{
- box_point_t const& p1 = bp[i - 1];
- box_point_t const& p2 = bp[i];
+ // the box crosses the antimeridian
- return_type const crs_AD = geometry::detail::course<return_type>(p1, point);
- return_type const crs_AB = geometry::detail::course<return_type>(p1, p2);
- return_type const d_crs1 = crs_AD - crs_AB;
- return_type const sin_d_crs1 = sin(d_crs1);
+ // midway longitude = lon_min - (lon_min + (lon_max - 2 * pi)) / 2;
+ return_type const lon_midway = (lon_min - lon_max) / two + pi;
+ BOOST_GEOMETRY_ASSERT(lon_midway >= -pi && lon_midway <= pi);
- // this constant sin() is here to be consistent with the side strategy
- return_type const sigXTD = asin(sin(0.001) * sin_d_crs1);
+ use_left_segment = plon > lon_midway;
+ }
+ else
+ {
+ // the box does not cross the antimeridian
- // If the point is on the right side of the edge
- if ( sigXTD > 0 )
+ return_type const lon_sum = lon_min + lon_max;
+ if (math::equals(lon_sum, return_type(0)))
{
- return_type const crs_BA = crs_AB - geometry::math::pi<return_type>();
- return_type const crs_BD = geometry::detail::course<return_type>(p2, point);
- return_type const d_crs2 = crs_BD - crs_BA;
+ // special case: the box is symmetric with respect to
+ // the prime meridian; the midway meridian is the antimeridian
- return_type const projection1 = cos( d_crs1 );
- return_type const projection2 = cos( d_crs2 );
+ use_left_segment = plon < lon_min;
+ }
+ else
+ {
+ // midway long. = lon_min - (2 * pi - (lon_max - lon_min)) / 2;
+ return_type lon_midway = (lon_min + lon_max) / two - pi;
- if(projection1 > 0.0 && projection2 > 0.0)
+ // normalize the midway longitude
+ if (lon_midway > pi)
{
- return_type const d1 = m_pp_strategy.apply(p1, point);
- return_type const
- XTD = radius()
- * geometry::math::abs(
- asin( sin( d1 / radius() ) * sin_d_crs1 )
- );
-
- return return_type(XTD);
+ lon_midway -= two_pi;
}
- else
+ else if (lon_midway < -pi)
{
- // OPTIMIZATION
- // Return d1 if projection1 <= 0 and d2 if projection2 <= 0
- // if both == 0 then return d1 or d2
- // both shouldn't be < 0
-
- return_type const d1 = m_pp_strategy.apply(p1, point);
- return_type const d2 = m_pp_strategy.apply(p2, point);
-
- return return_type((std::min)( d1 , d2 ));
+ lon_midway += two_pi;
}
+ BOOST_GEOMETRY_ASSERT(lon_midway >= -pi && lon_midway <= pi);
+
+ // if lon_sum is positive the midway meridian is left
+ // of the box, or right of the box otherwise
+ use_left_segment = lon_sum > 0
+ ? (plon < lon_min && plon >= lon_midway)
+ : (plon <= lon_max || plon > lon_midway);
}
}
- // Return 0 if the point isn't on the right side of any segment
- return return_type(0);
+ return use_left_segment
+ ? m_ps_strategy.apply(point, bottom_left, top_left)
+ : m_ps_strategy.apply(point, bottom_right, top_right);
}
inline typename Strategy::radius_type radius() const
- { return m_pp_strategy.radius(); }
-
-private :
+ {
+ return m_ps_strategy.radius();
+ }
- Strategy m_pp_strategy;
+private:
+ Strategy m_ps_strategy;
};
+
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
@@ -170,67 +246,68 @@ struct tag<cross_track_point_box<CalculationType, Strategy> >
template <typename CalculationType, typename Strategy, typename P, typename Box>
struct return_type<cross_track_point_box<CalculationType, Strategy>, P, Box>
- : cross_track_point_box<CalculationType, Strategy>::template return_type<P, Box>
+ : cross_track_point_box
+ <
+ CalculationType, Strategy
+ >::template return_type<P, Box>
{};
template <typename CalculationType, typename Strategy>
struct comparable_type<cross_track_point_box<CalculationType, Strategy> >
{
- // There is no shortcut, so the strategy itself is its comparable type
- typedef cross_track_point_box<CalculationType, Strategy> type;
+ typedef cross_track_point_box
+ <
+ CalculationType, typename comparable_type<Strategy>::type
+ > type;
};
-template
-<
- typename CalculationType,
- typename Strategy
->
+template <typename CalculationType, typename Strategy>
struct get_comparable<cross_track_point_box<CalculationType, Strategy> >
{
- typedef typename comparable_type
- <
- cross_track_point_box<CalculationType, Strategy>
- >::type comparable_type;
-public :
- static inline comparable_type apply(
- cross_track_point_box<CalculationType, Strategy> const& strategy)
+ typedef cross_track_point_box<CalculationType, Strategy> this_strategy;
+ typedef typename comparable_type<this_strategy>::type comparable_type;
+
+public:
+ static inline comparable_type apply(this_strategy const& strategy)
{
- return cross_track_point_box<CalculationType, Strategy>(strategy.radius());
+ return comparable_type(strategy.radius());
}
};
-template
-<
- typename CalculationType,
- typename Strategy,
- typename P, typename Box
->
+template <typename CalculationType, typename Strategy, typename P, typename Box>
struct result_from_distance
<
- cross_track_point_box<CalculationType, Strategy>,
- P,
- Box
+ cross_track_point_box<CalculationType, Strategy>, P, Box
>
{
-private :
- typedef typename cross_track_point_box
+private:
+ typedef cross_track_point_box<CalculationType, Strategy> this_strategy;
+
+ typedef typename this_strategy::template return_type
<
- CalculationType, Strategy
- >::template return_type<P, Box> return_type;
-public :
+ P, Box
+ >::type return_type;
+
+public:
template <typename T>
- static inline return_type apply(
- cross_track_point_box<CalculationType, Strategy> const& ,
- T const& distance)
+ static inline return_type apply(this_strategy const& strategy,
+ T const& distance)
{
- return distance;
+ Strategy s(strategy.radius());
+
+ return result_from_distance
+ <
+ Strategy, P, typename point_type<Box>::type
+ >::apply(s, distance);
}
};
+// define cross_track_point_box<default_point_segment_strategy> as
+// default point-box strategy for the spherical equatorial coordinate system
template <typename Point, typename Box, typename Strategy>
struct default_strategy
<
@@ -247,7 +324,7 @@ struct default_strategy
boost::is_void<Strategy>,
typename default_strategy
<
- point_tag, point_tag,
+ point_tag, segment_tag,
Point, typename point_type<Box>::type,
spherical_equatorial_tag, spherical_equatorial_tag
>::type,