blob: fee1e2b7e1aa566a3221fc7eb4f2f77fd953ec95 [file] [log] [blame]
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2011 Barend Gehrels, 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_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/strategies/compare.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace compare
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename Units>
struct shift
{
};
template <>
struct shift<degree>
{
static inline double full() { return 360.0; }
static inline double half() { return 180.0; }
};
template <>
struct shift<radian>
{
static inline double full() { return 2.0 * boost::math::constants::pi<double>(); }
static inline double half() { return boost::math::constants::pi<double>(); }
};
} // namespace detail
#endif
/*!
\brief Compare (in one direction) strategy for spherical coordinates
\ingroup strategies
\tparam Point point-type
\tparam Dimension dimension
*/
template <typename CoordinateType, typename Units, typename Compare>
struct circular_comparator
{
static inline CoordinateType put_in_range(CoordinateType const& c,
double min_border, double max_border)
{
CoordinateType value = c;
while (value < min_border)
{
value += detail::shift<Units>::full();
}
while (value > max_border)
{
value -= detail::shift<Units>::full();
}
return value;
}
inline bool operator()(CoordinateType const& c1, CoordinateType const& c2) const
{
Compare compare;
// Check situation that one of them is e.g. std::numeric_limits.
static const double full = detail::shift<Units>::full();
double mx = 10.0 * full;
if (c1 < -mx || c1 > mx || c2 < -mx || c2 > mx)
{
// do normal comparison, using circular is not useful
return compare(c1, c2);
}
static const double half = full / 2.0;
CoordinateType v1 = put_in_range(c1, -half, half);
CoordinateType v2 = put_in_range(c2, -half, half);
// Two coordinates on a circle are
// at max <= half a circle away from each other.
// So if it is more, shift origin.
CoordinateType diff = geometry::math::abs(v1 - v2);
if (diff > half)
{
v1 = put_in_range(v1, 0, full);
v2 = put_in_range(v2, 0, full);
}
return compare(v1, v2);
}
};
}} // namespace strategy::compare
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
// Specialize for the longitude (dim 0)
template
<
typename Point,
template<typename> class CoordinateSystem,
typename Units
>
struct strategy_compare<spherical_polar_tag, 1, Point, CoordinateSystem<Units>, 0>
{
typedef typename coordinate_type<Point>::type coordinate_type;
typedef strategy::compare::circular_comparator
<
coordinate_type,
Units,
std::less<coordinate_type>
> type;
};
template
<
typename Point,
template<typename> class CoordinateSystem,
typename Units
>
struct strategy_compare<spherical_polar_tag, -1, Point, CoordinateSystem<Units>, 0>
{
typedef typename coordinate_type<Point>::type coordinate_type;
typedef strategy::compare::circular_comparator
<
coordinate_type,
Units,
std::greater<coordinate_type>
> type;
};
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP