Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_AREA_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/area.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HPP
+122
View File
@@ -0,0 +1,122 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2016-2021 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AZIMUTH_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AZIMUTH_HPP
#include <type_traits>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/strategies/azimuth.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace azimuth
{
template <typename CalculationType = void>
class spherical
{
public:
template <typename T1, typename T2>
struct result_type
: geometry::select_most_precise
<
T1, T2, CalculationType
>
{};
template <typename T1, typename T2, typename Result>
static inline void apply(T1 const& lon1_rad, T1 const& lat1_rad,
T2 const& lon2_rad, T2 const& lat2_rad,
Result& a1, Result& a2)
{
compute<true, true>(lon1_rad, lat1_rad,
lon2_rad, lat2_rad,
a1, a2);
}
template <typename T1, typename T2, typename Result>
static inline void apply(T1 const& lon1_rad, T1 const& lat1_rad,
T2 const& lon2_rad, T2 const& lat2_rad,
Result& a1)
{
compute<true, false>(lon1_rad, lat1_rad,
lon2_rad, lat2_rad,
a1, a1);
}
template <typename T1, typename T2, typename Result>
static inline void apply_reverse(T1 const& lon1_rad, T1 const& lat1_rad,
T2 const& lon2_rad, T2 const& lat2_rad,
Result& a2)
{
compute<false, true>(lon1_rad, lat1_rad,
lon2_rad, lat2_rad,
a2, a2);
}
private:
template
<
bool EnableAzimuth,
bool EnableReverseAzimuth,
typename T1, typename T2, typename Result
>
static inline void compute(T1 const& lon1_rad, T1 const& lat1_rad,
T2 const& lon2_rad, T2 const& lat2_rad,
Result& a1, Result& a2)
{
typedef typename result_type<T1, T2>::type calc_t;
geometry::formula::result_spherical<calc_t>
result = geometry::formula::spherical_azimuth
<
calc_t,
EnableReverseAzimuth
>(calc_t(lon1_rad), calc_t(lat1_rad),
calc_t(lon2_rad), calc_t(lat2_rad));
if (EnableAzimuth)
{
a1 = result.azimuth;
}
if (EnableReverseAzimuth)
{
a2 = result.reverse_azimuth;
}
}
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <>
struct default_strategy<spherical_equatorial_tag>
{
typedef strategy::azimuth::spherical<> type;
};
}
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::azimuth
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AZIMUTH_HPP
@@ -0,0 +1,200 @@
// Boost.Geometry
// Copyright (c) 2021, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP
#include <algorithm>
#include <type_traits>
#include <boost/config.hpp>
#include <boost/concept_check.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/concepts/distance_concept.hpp>
#include <boost/geometry/strategies/spherical/distance_haversine.hpp>
#include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/spherical/intersection.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
# include <boost/geometry/io/dsv/write.hpp>
#endif
namespace boost { namespace geometry
{
namespace strategy { namespace closest_points
{
template
<
typename CalculationType = void,
typename Strategy = distance::comparable::haversine<double, CalculationType>
>
class cross_track
{
public:
template <typename Point, typename PointOfSegment>
struct calculation_type
: promote_floating_point
<
typename select_calculation_type
<
Point,
PointOfSegment,
CalculationType
>::type
>
{};
using radius_type = typename Strategy::radius_type;
cross_track() = default;
explicit inline cross_track(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline cross_track(Strategy const& s)
: m_strategy(s)
{}
template <typename Point, typename PointOfSegment>
inline auto apply(Point const& p,
PointOfSegment const& sp1,
PointOfSegment const& sp2) const
{
using CT = typename calculation_type<Point, PointOfSegment>::type;
// http://williams.best.vwh.net/avform.htm#XTE
CT d3 = m_strategy.apply(sp1, sp2);
if (geometry::math::equals(d3, 0.0))
{
// "Degenerate" segment, return either d1 or d2
return sp1;
}
CT d1 = m_strategy.apply(sp1, p);
CT d2 = m_strategy.apply(sp2, p);
auto d_crs_pair = distance::detail::compute_cross_track_pair<CT>::apply(
p, sp1, sp2);
// d1, d2, d3 are in principle not needed, only the sign matters
CT projection1 = cos(d_crs_pair.first) * d1 / d3;
CT projection2 = cos(d_crs_pair.second) * d2 / d3;
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " "
<< crs_AD * geometry::math::r2d<CT>() << std::endl;
std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " "
<< crs_AB * geometry::math::r2d<CT>() << std::endl;
std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " "
<< crs_BA * geometry::math::r2d<CT>() << std::endl;
std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " "
<< crs_BD * geometry::math::r2d<CT>() << std::endl;
std::cout << "Projection AD-AB " << projection1 << " : "
<< d_crs1 * geometry::math::r2d<CT>() << std::endl;
std::cout << "Projection BD-BA " << projection2 << " : "
<< d_crs2 * geometry::math::r2d<CT>() << std::endl;
std::cout << " d1: " << (d1 )
<< " d2: " << (d2 )
<< std::endl;
#endif
if (projection1 > 0.0 && projection2 > 0.0)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
CT XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) ));
std::cout << "Projection ON the segment" << std::endl;
std::cout << "XTD: " << XTD
<< " d1: " << (d1 * radius())
<< " d2: " << (d2 * radius())
<< std::endl;
#endif
auto distance = distance::detail::compute_cross_track_distance::apply(
d_crs_pair.first, d1);
CT lon1 = geometry::get_as_radian<0>(sp1);
CT lat1 = geometry::get_as_radian<1>(sp1);
CT lon2 = geometry::get_as_radian<0>(sp2);
CT lat2 = geometry::get_as_radian<1>(sp2);
CT dist = CT(2) * asin(math::sqrt(distance)) * m_strategy.radius();
CT dist_d1 = CT(2) * asin(math::sqrt(d1)) * m_strategy.radius();
// Note: this is similar to spherical computation in geographic
// point_segment_distance formula
CT earth_radius = m_strategy.radius();
CT cos_frac = cos(dist_d1 / earth_radius) / cos(dist / earth_radius);
CT s14_sph = cos_frac >= 1
? CT(0) : cos_frac <= -1 ? math::pi<CT>() * earth_radius
: acos(cos_frac) * earth_radius;
CT a12 = geometry::formula::spherical_azimuth<>(lon1, lat1, lon2, lat2);
auto res_direct = geometry::formula::spherical_direct
<
true,
false
>(lon1, lat1, s14_sph, a12, srs::sphere<CT>(earth_radius));
model::point
<
CT,
dimension<PointOfSegment>::value,
typename coordinate_system<PointOfSegment>::type
> cp;
geometry::set_from_radian<0>(cp, res_direct.lon2);
geometry::set_from_radian<1>(cp, res_direct.lat2);
return cp;
}
else
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
std::cout << "Projection OUTSIDE the segment" << std::endl;
#endif
return d1 < d2 ? sp1 : sp2;
}
}
template <typename T1, typename T2>
inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
{
return m_strategy.radius() * (lat1 - lat2);
}
inline typename Strategy::radius_type radius() const
{ return m_strategy.radius(); }
private :
Strategy m_strategy;
};
}} // namespace strategy::closest_points
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP
+340
View File
@@ -0,0 +1,340 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2017-2023.
// Modifications copyright (c) 2017-2023, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP
#include <type_traits>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/strategies/compare.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace compare
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <std::size_t I, typename P>
static inline typename geometry::coordinate_type<P>::type
get(P const& p, std::true_type /*same units*/)
{
return geometry::get<I>(p);
}
template <std::size_t I, typename P>
static inline typename geometry::coordinate_type<P>::type
get(P const& p, std::false_type /*different units*/)
{
return geometry::get_as_radian<I>(p);
}
template
<
typename ComparePolicy,
typename EqualsPolicy,
typename Point1,
typename Point2,
std::size_t DimensionCount
>
struct spherical_latitude
{
typedef typename geometry::coordinate_type<Point1>::type coordinate1_type;
typedef typename geometry::detail::cs_angular_units<Point1>::type units1_type;
typedef typename geometry::coordinate_type<Point2>::type coordinate2_type;
typedef typename geometry::detail::cs_angular_units<Point2>::type units2_type;
typedef std::is_same<units1_type, units2_type> same_units_type;
template <typename T1, typename T2>
static inline bool apply(Point1 const& left, Point2 const& right,
T1 const& l1, T2 const& r1)
{
// latitudes equal
if (EqualsPolicy::apply(l1, r1))
{
return compare::detail::compare_loop
<
ComparePolicy, EqualsPolicy, 2, DimensionCount
>::apply(left, right);
}
else
{
return ComparePolicy::apply(l1, r1);
}
}
static inline bool apply(Point1 const& left, Point2 const& right)
{
coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type());
coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type());
return apply(left, right, l1, r1);
}
};
template
<
typename ComparePolicy,
typename EqualsPolicy,
typename Point1,
typename Point2
>
struct spherical_latitude<ComparePolicy, EqualsPolicy, Point1, Point2, 1>
{
template <typename T1, typename T2>
static inline bool apply(Point1 const& left, Point2 const& right,
T1 const& , T2 const& )
{
return apply(left, right);
}
static inline bool apply(Point1 const& left, Point2 const& right)
{
return compare::detail::compare_loop
<
ComparePolicy, EqualsPolicy, 1, 1
>::apply(left, right);
}
};
template
<
typename ComparePolicy,
typename EqualsPolicy,
typename Point1,
typename Point2,
std::size_t DimensionCount
>
struct spherical_longitude
{
typedef typename geometry::coordinate_type<Point1>::type coordinate1_type;
typedef typename geometry::detail::cs_angular_units<Point1>::type units1_type;
typedef typename geometry::coordinate_type<Point2>::type coordinate2_type;
typedef typename geometry::detail::cs_angular_units<Point2>::type units2_type;
typedef std::is_same<units1_type, units2_type> same_units_type;
typedef std::conditional_t<same_units_type::value, units1_type, geometry::radian> units_type;
static const bool is_equatorial = ! std::is_same
<
typename geometry::cs_tag<Point1>::type,
geometry::spherical_polar_tag
>::value;
static inline bool are_both_at_antimeridian(coordinate1_type const& l0,
coordinate2_type const& r0,
bool & is_left_at,
bool & is_right_at)
{
is_left_at = math::is_longitude_antimeridian<units_type>(l0);
is_right_at = math::is_longitude_antimeridian<units_type>(r0);
return is_left_at && is_right_at;
}
static inline bool apply(Point1 const& left, Point2 const& right)
{
// if units are different the coordinates are in radians
coordinate1_type const& l0 = compare::detail::get<0>(left, same_units_type());
coordinate2_type const& r0 = compare::detail::get<0>(right, same_units_type());
coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type());
coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type());
bool is_left_at_antimeridian = false;
bool is_right_at_antimeridian = false;
// longitudes equal
if (EqualsPolicy::apply(l0, r0)
// both at antimeridian
|| are_both_at_antimeridian(l0, r0, is_left_at_antimeridian, is_right_at_antimeridian)
// both at pole
|| (EqualsPolicy::apply(l1, r1)
&& math::is_latitude_pole<units_type, is_equatorial>(l1)))
{
return spherical_latitude
<
ComparePolicy, EqualsPolicy, Point1, Point2, DimensionCount
>::apply(left, right, l1, r1);
}
// if left is at antimeridian and right is not at antimeridian
// then left is greater than right
else if (is_left_at_antimeridian)
{
// less/equal_to -> false, greater -> true
return ComparePolicy::apply(1, 0);
}
// if right is at antimeridian and left is not at antimeridian
// then left is lesser than right
else if (is_right_at_antimeridian)
{
// less -> true, equal_to/greater -> false
return ComparePolicy::apply(0, 1);
}
else
{
return ComparePolicy::apply(l0, r0);
}
}
};
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
/*!
\brief Compare strategy for spherical coordinates
\ingroup strategies
\tparam Point point-type
\tparam Dimension dimension
*/
template
<
typename ComparePolicy,
typename EqualsPolicy,
int Dimension = -1
>
struct spherical
: cartesian<ComparePolicy, EqualsPolicy, Dimension>
{};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
// all dimensions starting from longitude
template <typename ComparePolicy, typename EqualsPolicy>
struct spherical<ComparePolicy, EqualsPolicy, -1>
{
template <typename Point1, typename Point2>
static inline bool apply(Point1 const& left, Point2 const& right)
{
return compare::detail::spherical_longitude
<
ComparePolicy,
EqualsPolicy,
Point1,
Point2,
std::conditional_t
<
(dimension<Point1>::value < dimension<Point2>::value),
std::integral_constant<std::size_t, dimension<Point1>::value>,
std::integral_constant<std::size_t, dimension<Point2>::value>
>::value
>::apply(left, right);
}
};
// only longitudes (and latitudes to check poles)
template <typename ComparePolicy, typename EqualsPolicy>
struct spherical<ComparePolicy, EqualsPolicy, 0>
{
template <typename Point1, typename Point2>
static inline bool apply(Point1 const& left, Point2 const& right)
{
return compare::detail::spherical_longitude
<
ComparePolicy, EqualsPolicy, Point1, Point2, 1
>::apply(left, right);
}
};
// only latitudes
template <typename ComparePolicy, typename EqualsPolicy>
struct spherical<ComparePolicy, EqualsPolicy, 1>
{
template <typename Point1, typename Point2>
static inline bool apply(Point1 const& left, Point2 const& right)
{
return compare::detail::spherical_latitude
<
ComparePolicy, EqualsPolicy, Point1, Point2, 2
>::apply(left, right);
}
};
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename ComparePolicy, typename EqualsPolicy, typename Point1, typename Point2, int Dimension>
struct default_strategy
<
ComparePolicy, EqualsPolicy,
Point1, Point2, Dimension,
spherical_tag, spherical_tag
>
{
typedef compare::spherical<ComparePolicy, EqualsPolicy, Dimension> type;
};
template <typename ComparePolicy, typename EqualsPolicy, typename Point1, typename Point2, int Dimension>
struct default_strategy
<
ComparePolicy, EqualsPolicy,
Point1, Point2, Dimension,
spherical_polar_tag, spherical_polar_tag
>
{
typedef compare::spherical<ComparePolicy, EqualsPolicy, Dimension> type;
};
template <typename ComparePolicy, typename EqualsPolicy, typename Point1, typename Point2, int Dimension>
struct default_strategy
<
ComparePolicy, EqualsPolicy,
Point1, Point2, Dimension,
spherical_equatorial_tag, spherical_equatorial_tag
>
{
typedef compare::spherical<ComparePolicy, EqualsPolicy, Dimension> type;
};
template <typename ComparePolicy, typename EqualsPolicy, typename Point1, typename Point2, int Dimension>
struct default_strategy
<
ComparePolicy, EqualsPolicy,
Point1, Point2, Dimension,
geographic_tag, geographic_tag
>
{
typedef compare::spherical<ComparePolicy, EqualsPolicy, Dimension> type;
};
} // namespace services
}} // namespace strategy::compare
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP
+151
View File
@@ -0,0 +1,151 @@
// Boost.Geometry
// Copyright (c) 2017-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// 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_STRATEGIES_SPHERICAL_DENSIFY_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DENSIFY_HPP
#include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
#include <boost/geometry/algorithms/detail/signed_size_type.hpp>
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/arithmetic/cross_product.hpp>
#include <boost/geometry/arithmetic/dot_product.hpp>
#include <boost/geometry/arithmetic/normalize.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/formulas/interpolate_point_spherical.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/srs/sphere.hpp>
#include <boost/geometry/strategies/densify.hpp>
#include <boost/geometry/strategies/spherical/get_radius.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace densify
{
/*!
\brief Densification of spherical segment.
\ingroup strategies
\tparam RadiusTypeOrSphere \tparam_radius_or_sphere
\tparam CalculationType \tparam_calculation
\qbk{
[heading See also]
[link geometry.reference.algorithms.densify.densify_4_with_strategy densify (with strategy)]
}
*/
template
<
typename RadiusTypeOrSphere = double,
typename CalculationType = void
>
class spherical
{
public:
typedef typename strategy_detail::get_radius
<
RadiusTypeOrSphere
>::type radius_type;
// For consistency with area strategy the radius is set to 1
inline spherical()
: m_radius(1.0)
{}
template <typename RadiusOrSphere>
explicit inline spherical(RadiusOrSphere const& radius_or_sphere)
: m_radius(strategy_detail::get_radius
<
RadiusOrSphere
>::apply(radius_or_sphere))
{}
template <typename Point, typename AssignPolicy, typename T>
inline void apply(Point const& p0, Point const& p1, AssignPolicy & policy, T const& length_threshold) const
{
typedef typename AssignPolicy::point_type out_point_t;
typedef typename select_most_precise
<
typename coordinate_type<Point>::type,
typename coordinate_type<out_point_t>::type,
CalculationType
>::type calc_t;
calc_t angle01;
formula::interpolate_point_spherical<calc_t> formula;
formula.compute_angle(p0, p1, angle01);
BOOST_GEOMETRY_ASSERT(length_threshold > T(0));
signed_size_type n = signed_size_type(angle01 * m_radius / length_threshold);
if (n <= 0)
return;
formula.compute_axis(p0, angle01);
calc_t step = angle01 / (n + 1);
calc_t a = step;
for (signed_size_type i = 0 ; i < n ; ++i, a += step)
{
out_point_t p;
formula.compute_point(a, p);
geometry::detail::conversion::point_to_point
<
Point, out_point_t,
2, dimension<out_point_t>::value
>::apply(p0, p);
policy.apply(p);
}
}
inline radius_type radius() const
{
return m_radius;
}
private:
radius_type m_radius;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <>
struct default_strategy<spherical_equatorial_tag>
{
typedef strategy::densify::spherical<> type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::densify
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DENSIFY_HPP
@@ -0,0 +1,134 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
// Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2013-2018.
// Modifications copyright (c) 2013-2018, 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
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_DISJOINT_BOX_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISJOINT_BOX_BOX_HPP
#include <cstddef>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/strategies/cartesian/disjoint_box_box.hpp>
#include <boost/geometry/strategies/disjoint.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry { namespace strategy { namespace disjoint
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
struct box_box_on_spheroid
{
template <typename Box1, typename Box2>
static inline bool apply(Box1 const& box1, Box2 const& box2)
{
typedef typename geometry::select_most_precise
<
typename coordinate_type<Box1>::type,
typename coordinate_type<Box2>::type
>::type calc_t;
typedef typename geometry::detail::cs_angular_units<Box1>::type units_t;
typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
calc_t const b1_min = get<min_corner, 0>(box1);
calc_t const b1_max = get<max_corner, 0>(box1);
calc_t const b2_min = get<min_corner, 0>(box2);
calc_t const b2_max = get<max_corner, 0>(box2);
// min <= max <=> diff >= 0
calc_t const diff1 = b1_max - b1_min;
calc_t const diff2 = b2_max - b2_min;
// check the intersection if neither box cover the whole globe
if (diff1 < constants::period() && diff2 < constants::period())
{
// calculate positive longitude translation with b1_min as origin
calc_t const diff_min = math::longitude_distance_unsigned<units_t>(b1_min, b2_min);
calc_t const b2_min_transl = b1_min + diff_min; // always right of b1_min
calc_t b2_max_transl = b2_min_transl - constants::period() + diff2;
// if the translation is too close then use the original point
// note that math::abs(b2_max_transl - b2_max) takes values very
// close to k*2*constants::period() for k=0,1,2,...
if (math::abs(b2_max_transl - b2_max) < constants::period() / 2)
{
b2_max_transl = b2_max;
}
if (b2_min_transl > b1_max // b2_min right of b1_max
&& b2_max_transl < b1_min) // b2_max left of b1_min
{
return true;
}
}
return box_box
<
Box1, Box2, 1
>::apply(box1, box2);
}
};
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
struct spherical_box_box
{
template <typename Box1, typename Box2>
static inline bool apply(Box1 const& box1, Box2 const& box2)
{
return detail::box_box_on_spheroid::apply(box1, box2);
}
};
namespace services
{
template <typename Box1, typename Box2, int TopDim1, int TopDim2>
struct default_strategy<Box1, Box2, box_tag, box_tag, TopDim1, TopDim2, spherical_equatorial_tag, spherical_equatorial_tag>
{
typedef disjoint::spherical_box_box type;
};
template <typename Box1, typename Box2, int TopDim1, int TopDim2>
struct default_strategy<Box1, Box2, box_tag, box_tag, TopDim1, TopDim2, spherical_polar_tag, spherical_polar_tag>
{
typedef disjoint::spherical_box_box type;
};
template <typename Box1, typename Box2, int TopDim1, int TopDim2>
struct default_strategy<Box1, Box2, box_tag, box_tag, TopDim1, TopDim2, geographic_tag, geographic_tag>
{
typedef disjoint::spherical_box_box type;
};
} // namespace services
}}}} // namespace boost::geometry::strategy::disjoint
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISJOINT_BOX_BOX_HPP
@@ -0,0 +1,105 @@
// Boost.Geometry
// Copyright (c) 2017-2019 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISJOINT_SEGMENT_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISJOINT_SEGMENT_BOX_HPP
#include <cstddef>
#include <utility>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/calculation_type.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp>
#include <boost/geometry/algorithms/detail/disjoint/segment_box.hpp>
// TODO: spherical_point_box currently defined in the same file as cartesian
#include <boost/geometry/strategies/cartesian/point_in_box.hpp>
#include <boost/geometry/strategies/disjoint.hpp>
#include <boost/geometry/strategies/normalize.hpp>
#include <boost/geometry/strategies/spherical/azimuth.hpp>
#include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
namespace boost { namespace geometry { namespace strategy { namespace disjoint
{
// NOTE: This may be temporary place for this or corresponding strategy
// It seems to be more appropriate to implement the opposite of it
// e.g. intersection::segment_box because in disjoint() algorithm
// other strategies that are used are intersection and covered_by strategies.
struct segment_box_spherical
{
typedef covered_by::spherical_point_box disjoint_point_box_strategy_type;
static inline disjoint_point_box_strategy_type get_disjoint_point_box_strategy()
{
return disjoint_point_box_strategy_type();
}
template <typename Segment, typename Box>
static inline bool apply(Segment const& segment, Box const& box)
{
typedef typename point_type<Segment>::type segment_point_type;
typedef typename coordinate_type<segment_point_type>::type CT;
geometry::strategy::azimuth::spherical<CT> azimuth_strategy;
return geometry::detail::disjoint::disjoint_segment_box_sphere_or_spheroid
<
spherical_equatorial_tag
>::apply(segment, box,
azimuth_strategy,
strategy::normalize::spherical_point(),
strategy::covered_by::spherical_point_box(),
strategy::disjoint::spherical_box_box());
}
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename Linear, typename Box, typename LinearTag>
struct default_strategy<Linear, Box, LinearTag, box_tag, 1, 2,
spherical_equatorial_tag, spherical_equatorial_tag>
{
typedef segment_box_spherical type;
};
template <typename Box, typename Linear, typename LinearTag>
struct default_strategy<Box, Linear, box_tag, LinearTag, 2, 1,
spherical_equatorial_tag, spherical_equatorial_tag>
{
typedef segment_box_spherical type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}}}} // namespace boost::geometry::strategy::disjoint
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISJOINT_SEGMENT_BOX_HPP
@@ -0,0 +1,848 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2014-2021.
// Modifications copyright (c) 2014-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
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
#include <algorithm>
#include <type_traits>
#include <boost/config.hpp>
#include <boost/concept_check.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/concepts/distance_concept.hpp>
#include <boost/geometry/strategies/spherical/distance_haversine.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/spherical/intersection.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
# include <boost/geometry/io/dsv/write.hpp>
#endif
namespace boost { namespace geometry
{
namespace strategy { namespace distance
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename CalculationType>
struct compute_cross_track_pair
{
template <typename Point, typename PointOfSegment>
static inline auto apply(Point const& p,
PointOfSegment const& sp1,
PointOfSegment const& sp2)
{
CalculationType lon1 = geometry::get_as_radian<0>(sp1);
CalculationType lat1 = geometry::get_as_radian<1>(sp1);
CalculationType lon2 = geometry::get_as_radian<0>(sp2);
CalculationType lat2 = geometry::get_as_radian<1>(sp2);
CalculationType lon = geometry::get_as_radian<0>(p);
CalculationType lat = geometry::get_as_radian<1>(p);
CalculationType const crs_AD = geometry::formula::spherical_azimuth
<
CalculationType,
false
>(lon1, lat1, lon, lat).azimuth;
auto result = geometry::formula::spherical_azimuth
<
CalculationType,
true
>(lon1, lat1, lon2, lat2);
CalculationType crs_AB = result.azimuth;
CalculationType crs_BA = result.reverse_azimuth -
geometry::math::pi<CalculationType>();
CalculationType crs_BD = geometry::formula::spherical_azimuth
<
CalculationType,
false
>(lon2, lat2, lon, lat).azimuth;
CalculationType d_crs1 = crs_AD - crs_AB;
CalculationType d_crs2 = crs_BD - crs_BA;
return std::pair<CalculationType, CalculationType>(d_crs1, d_crs2);
}
};
struct compute_cross_track_distance
{
template <typename CalculationType>
static inline auto apply(CalculationType const& d_crs1,
CalculationType const& d1)
{
CalculationType const half(0.5);
CalculationType const quarter(0.25);
CalculationType sin_d_crs1 = sin(d_crs1);
/*
This is the straightforward obvious way to continue:
return_type discriminant
= 1.0 - 4.0 * (d1 - d1 * d1) * sin_d_crs1 * sin_d_crs1;
return 0.5 - 0.5 * math::sqrt(discriminant);
Below we optimize the number of arithmetic operations
and account for numerical robustness:
*/
CalculationType d1_x_sin = d1 * sin_d_crs1;
CalculationType d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
return d / (half + math::sqrt(quarter - d));
}
};
}
#endif // DOXYGEN_NO_DETAIL
namespace comparable
{
/*
Given a spherical segment AB and a point D, we are interested in
computing the distance of D from AB. This is usually known as the
cross track distance.
If the projection (along great circles) of the point D lies inside
the segment AB, then the distance (cross track error) XTD is given
by the formula (see http://williams.best.vwh.net/avform.htm#XTE):
XTD = asin( sin(dist_AD) * sin(crs_AD-crs_AB) )
where dist_AD is the great circle distance between the points A and
B, and crs_AD, crs_AB is the course (bearing) between the points A,
D and A, B, respectively.
If the point D does not project inside the arc AB, then the distance
of D from AB is the minimum of the two distances dist_AD and dist_BD.
Our reference implementation for this procedure is listed below
(this was the old Boost.Geometry implementation of the cross track distance),
where:
* The member variable m_strategy is the underlying haversine strategy.
* p stands for the point D.
* sp1 stands for the segment endpoint A.
* sp2 stands for the segment endpoint B.
================= reference implementation -- start =================
return_type d1 = m_strategy.apply(sp1, p);
return_type d3 = m_strategy.apply(sp1, sp2);
if (geometry::math::equals(d3, 0.0))
{
// "Degenerate" segment, return either d1 or d2
return d1;
}
return_type d2 = m_strategy.apply(sp2, p);
return_type crs_AD = geometry::detail::course<return_type>(sp1, p);
return_type crs_AB = geometry::detail::course<return_type>(sp1, sp2);
return_type crs_BA = crs_AB - geometry::math::pi<return_type>();
return_type crs_BD = geometry::detail::course<return_type>(sp2, p);
return_type d_crs1 = crs_AD - crs_AB;
return_type d_crs2 = crs_BD - crs_BA;
// d1, d2, d3 are in principle not needed, only the sign matters
return_type projection1 = cos( d_crs1 ) * d1 / d3;
return_type projection2 = cos( d_crs2 ) * d2 / d3;
if (projection1 > 0.0 && projection2 > 0.0)
{
return_type XTD
= radius() * math::abs( asin( sin( d1 / radius() ) * sin( d_crs1 ) ));
// Return shortest distance, projected point on segment sp1-sp2
return return_type(XTD);
}
else
{
// Return shortest distance, project either on point sp1 or sp2
return return_type( (std::min)( d1 , d2 ) );
}
================= reference implementation -- end =================
Motivation
----------
In what follows we develop a comparable version of the cross track
distance strategy, that meets the following goals:
* It is more efficient than the original cross track strategy (less
operations and less calls to mathematical functions).
* Distances using the comparable cross track strategy can not only
be compared with other distances using the same strategy, but also with
distances computed with the comparable version of the haversine strategy.
* It can serve as the basis for the computation of the cross track distance,
as it is more efficient to compute its comparable version and
transform that to the actual cross track distance, rather than
follow/use the reference implementation listed above.
Major idea
----------
The idea here is to use the comparable haversine strategy to compute
the distances d1, d2 and d3 in the above listing. Once we have done
that we need also to make sure that instead of returning XTD (as
computed above) that we return a distance CXTD that is compatible
with the comparable haversine distance. To achieve this CXTD must satisfy
the relation:
XTD = 2 * R * asin( sqrt(XTD) )
where R is the sphere's radius.
Below we perform the mathematical analysis that show how to compute CXTD.
Mathematical analysis
---------------------
Below we use the following trigonometric identities:
sin(2 * x) = 2 * sin(x) * cos(x)
cos(asin(x)) = sqrt(1 - x^2)
Observation:
The distance d1 needed when the projection of the point D is within the
segment must be the true distance. However, comparable::haversine<>
returns a comparable distance instead of the one needed.
To remedy this, we implicitly compute what is needed.
More precisely, we need to compute sin(true_d1):
sin(true_d1) = sin(2 * asin(sqrt(d1)))
= 2 * sin(asin(sqrt(d1)) * cos(asin(sqrt(d1)))
= 2 * sqrt(d1) * sqrt(1-(sqrt(d1))^2)
= 2 * sqrt(d1 - d1 * d1)
This relation is used below.
As we mentioned above the goal is to find CXTD (named "a" below for
brevity) such that ("b" below stands for "d1", and "c" for "d_crs1"):
2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
Analysis:
2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
<=> 2 * asin(sqrt(a)) == asin(sqrt(b-b^2) * sin(c))
<=> sin(2 * asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
<=> 2 * sin(asin(sqrt(a))) * cos(asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
<=> 2 * sqrt(a) * sqrt(1-a) == 2 * sqrt(b-b^2) * sin(c)
<=> sqrt(a) * sqrt(1-a) == sqrt(b-b^2) * sin(c)
<=> sqrt(a-a^2) == sqrt(b-b^2) * sin(c)
<=> a-a^2 == (b-b^2) * (sin(c))^2
Consider the quadratic equation: x^2-x+p^2 == 0,
where p = sqrt(b-b^2) * sin(c); its discriminant is:
d = 1 - 4 * p^2 = 1 - 4 * (b-b^2) * (sin(c))^2
The two solutions are:
a_1 = (1 - sqrt(d)) / 2
a_2 = (1 + sqrt(d)) / 2
Which one to choose?
"a" refers to the distance (on the unit sphere) of D from the
supporting great circle Circ(A,B) of the segment AB.
The two different values for "a" correspond to the lengths of the two
arcs delimited D and the points of intersection of Circ(A,B) and the
great circle perperdicular to Circ(A,B) passing through D.
Clearly, the value we want is the smallest among these two distances,
hence the root we must choose is the smallest root among the two.
So the answer is:
CXTD = ( 1 - sqrt(1 - 4 * (b-b^2) * (sin(c))^2) ) / 2
Therefore, in order to implement the comparable version of the cross
track strategy we need to:
(1) Use the comparable version of the haversine strategy instead of
the non-comparable one.
(2) Instead of return XTD when D projects inside the segment AB, we
need to return CXTD, given by the following formula:
CXTD = ( 1 - sqrt(1 - 4 * (d1-d1^2) * (sin(d_crs1))^2) ) / 2;
Complexity Analysis
-------------------
In the analysis that follows we refer to the actual implementation below.
In particular, instead of computing CXTD as above, we use the more
efficient (operation-wise) computation of CXTD shown here:
return_type sin_d_crs1 = sin(d_crs1);
return_type d1_x_sin = d1 * sin_d_crs1;
return_type d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
return d / (0.5 + math::sqrt(0.25 - d));
Notice that instead of computing:
0.5 - 0.5 * sqrt(1 - 4 * d) = 0.5 - sqrt(0.25 - d)
we use the following formula instead:
d / (0.5 + sqrt(0.25 - d)).
This is done for numerical robustness. The expression 0.5 - sqrt(0.25 - x)
has large numerical errors for values of x close to 0 (if using doubles
the error start to become large even when d is as large as 0.001).
To remedy that, we re-write 0.5 - sqrt(0.25 - x) as:
0.5 - sqrt(0.25 - d)
= (0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d)) / (0.5 + sqrt(0.25 - d)).
The numerator is the difference of two squares:
(0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d))
= 0.5^2 - (sqrt(0.25 - d))^ = 0.25 - (0.25 - d) = d,
which gives the expression we use.
For the complexity analysis, we distinguish between two cases:
(A) The distance is realized between the point D and an
endpoint of the segment AB
Gains:
Since we are using comparable::haversine<> which is called
3 times, we gain:
-> 3 calls to sqrt
-> 3 calls to asin
-> 6 multiplications
Loses: None
So the net gain is:
-> 6 function calls (sqrt/asin)
-> 6 arithmetic operations
If we use comparable::cross_track<> to compute
cross_track<> we need to account for a call to sqrt, a call
to asin and 2 multiplications. In this case the net gain is:
-> 4 function calls (sqrt/asin)
-> 4 arithmetic operations
(B) The distance is realized between the point D and an
interior point of the segment AB
Gains:
Since we are using comparable::haversine<> which is called
3 times, we gain:
-> 3 calls to sqrt
-> 3 calls to asin
-> 6 multiplications
Also we gain the operations used to compute XTD:
-> 2 calls to sin
-> 1 call to asin
-> 1 call to abs
-> 2 multiplications
-> 1 division
So the total gains are:
-> 9 calls to sqrt/sin/asin
-> 1 call to abs
-> 8 multiplications
-> 1 division
Loses:
To compute a distance compatible with comparable::haversine<>
we need to perform a few more operations, namely:
-> 1 call to sin
-> 1 call to sqrt
-> 2 multiplications
-> 1 division
-> 1 addition
-> 2 subtractions
So roughly speaking the net gain is:
-> 8 fewer function calls and 3 fewer arithmetic operations
If we were to implement cross_track directly from the
comparable version (much like what haversine<> does using
comparable::haversine<>) we need additionally
-> 2 function calls (asin/sqrt)
-> 2 multiplications
So it pays off to re-implement cross_track<> to use
comparable::cross_track<>; in this case the net gain would be:
-> 6 function calls
-> 1 arithmetic operation
Summary/Conclusion
------------------
Following the mathematical and complexity analysis above, the
comparable cross track strategy (as implemented below) satisfies
all the goal mentioned in the beginning:
* It is more efficient than its non-comparable counter-part.
* Comparable distances using this new strategy can also be compared
with comparable distances computed with the comparable haversine
strategy.
* It turns out to be more efficient to compute the actual cross
track distance XTD by first computing CXTD, and then computing
XTD by means of the formula:
XTD = 2 * R * asin( sqrt(CXTD) )
*/
template
<
typename CalculationType = void,
typename Strategy = comparable::haversine<double, CalculationType>
>
class cross_track
{
public:
template <typename Point, typename PointOfSegment>
struct return_type
: promote_floating_point
<
typename select_calculation_type
<
Point,
PointOfSegment,
CalculationType
>::type
>
{};
using radius_type = typename Strategy::radius_type;
cross_track() = default;
explicit inline cross_track(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline cross_track(Strategy const& s)
: m_strategy(s)
{}
// It might be useful in the future
// to overload constructor with strategy info.
// crosstrack(...) {}
template <typename Point, typename PointOfSegment>
inline typename return_type<Point, PointOfSegment>::type
apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
{
#if !defined(BOOST_MSVC)
BOOST_CONCEPT_ASSERT
(
(concepts::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
);
#endif
using return_type = typename return_type<Point, PointOfSegment>::type;
// http://williams.best.vwh.net/avform.htm#XTE
return_type d1 = m_strategy.apply(sp1, p);
return_type d3 = m_strategy.apply(sp1, sp2);
if (geometry::math::equals(d3, 0.0))
{
// "Degenerate" segment, return either d1 or d2
return d1;
}
return_type d2 = m_strategy.apply(sp2, p);
auto d_crs_pair = detail::compute_cross_track_pair<return_type>::apply(
p, sp1, sp2);
// d1, d2, d3 are in principle not needed, only the sign matters
return_type projection1 = cos(d_crs_pair.first) * d1 / d3;
return_type projection2 = cos(d_crs_pair.second) * d2 / d3;
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " "
<< crs_AD * geometry::math::r2d<return_type>() << std::endl;
std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " "
<< crs_AB * geometry::math::r2d<return_type>() << std::endl;
std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " "
<< crs_BA * geometry::math::r2d<return_type>() << std::endl;
std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " "
<< crs_BD * geometry::math::r2d<return_type>() << std::endl;
std::cout << "Projection AD-AB " << projection1 << " : "
<< d_crs1 * geometry::math::r2d<return_type>() << std::endl;
std::cout << "Projection BD-BA " << projection2 << " : "
<< d_crs2 * geometry::math::r2d<return_type>() << std::endl;
std::cout << " d1: " << (d1 )
<< " d2: " << (d2 )
<< std::endl;
#endif
if (projection1 > 0.0 && projection2 > 0.0)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
return_type XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) ));
std::cout << "Projection ON the segment" << std::endl;
std::cout << "XTD: " << XTD
<< " d1: " << (d1 * radius())
<< " d2: " << (d2 * radius())
<< std::endl;
#endif
return detail::compute_cross_track_distance::apply(
d_crs_pair.first, d1);
}
else
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
std::cout << "Projection OUTSIDE the segment" << std::endl;
#endif
// Return shortest distance, project either on point sp1 or sp2
return return_type( (std::min)( d1 , d2 ) );
}
}
template <typename T1, typename T2>
inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
{
return m_strategy.radius() * (lat1 - lat2);
}
inline typename Strategy::radius_type radius() const
{ return m_strategy.radius(); }
private :
Strategy m_strategy;
};
} // namespace comparable
/*!
\brief Strategy functor for distance point to segment calculation
\ingroup strategies
\details Class which calculates the distance of a point to a segment, for points on a sphere or globe
\see http://williams.best.vwh.net/avform.htm
\tparam CalculationType \tparam_calculation
\tparam Strategy underlying point-point distance strategy, defaults to haversine
\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>
>
class cross_track
{
public :
template <typename Point, typename PointOfSegment>
struct return_type
: promote_floating_point
<
typename select_calculation_type
<
Point,
PointOfSegment,
CalculationType
>::type
>
{};
using radius_type = typename Strategy::radius_type;
inline cross_track()
{}
explicit inline cross_track(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline cross_track(Strategy const& s)
: m_strategy(s)
{}
// It might be useful in the future
// to overload constructor with strategy info.
// crosstrack(...) {}
template <typename Point, typename PointOfSegment>
inline auto apply(Point const& p,
PointOfSegment const& sp1,
PointOfSegment const& sp2) const
{
#if !defined(BOOST_MSVC)
BOOST_CONCEPT_ASSERT
(
(concepts::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
);
#endif
using return_type = typename return_type<Point, PointOfSegment>::type;
using this_type = cross_track<CalculationType, Strategy>;
using comparable_type = typename services::comparable_type
<
this_type
>::type;
comparable_type cstrategy
= services::get_comparable<this_type>::apply(m_strategy);
return_type const a = cstrategy.apply(p, sp1, sp2);
return_type const c = return_type(2.0) * asin(math::sqrt(a));
return c * radius();
}
template <typename T1, typename T2>
inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const
{
return m_strategy.radius() * (lat1 - lat2);
}
inline typename Strategy::radius_type radius() const
{ return m_strategy.radius(); }
private :
Strategy m_strategy;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename CalculationType, typename Strategy>
struct tag<cross_track<CalculationType, Strategy> >
{
using type = strategy_tag_distance_point_segment;
};
template <typename CalculationType, typename Strategy, typename P, typename PS>
struct return_type<cross_track<CalculationType, Strategy>, P, PS>
: cross_track<CalculationType, Strategy>::template return_type<P, PS>
{};
template <typename CalculationType, typename Strategy>
struct comparable_type<cross_track<CalculationType, Strategy> >
{
using type = comparable::cross_track
<
CalculationType, typename comparable_type<Strategy>::type
> ;
};
template
<
typename CalculationType,
typename Strategy
>
struct get_comparable<cross_track<CalculationType, Strategy> >
{
using comparable_type = typename comparable_type
<
cross_track<CalculationType, Strategy>
>::type;
public :
static inline comparable_type
apply(cross_track<CalculationType, Strategy> const& strategy)
{
return comparable_type(strategy.radius());
}
};
template
<
typename CalculationType,
typename Strategy,
typename P,
typename PS
>
struct result_from_distance<cross_track<CalculationType, Strategy>, P, PS>
{
private :
using return_type = typename cross_track
<
CalculationType, Strategy
>::template return_type<P, PS>::type;
public :
template <typename T>
static inline return_type
apply(cross_track<CalculationType, Strategy> const& , T const& distance)
{
return distance;
}
};
// Specializations for comparable::cross_track
template <typename RadiusType, typename CalculationType>
struct tag<comparable::cross_track<RadiusType, CalculationType> >
{
using type = strategy_tag_distance_point_segment;
};
template
<
typename RadiusType,
typename CalculationType,
typename P,
typename PS
>
struct return_type<comparable::cross_track<RadiusType, CalculationType>, P, PS>
: comparable::cross_track
<
RadiusType, CalculationType
>::template return_type<P, PS>
{};
template <typename RadiusType, typename CalculationType>
struct comparable_type<comparable::cross_track<RadiusType, CalculationType> >
{
using type = comparable::cross_track<RadiusType, CalculationType>;
};
template <typename RadiusType, typename CalculationType>
struct get_comparable<comparable::cross_track<RadiusType, CalculationType> >
{
private :
using this_type = comparable::cross_track<RadiusType, CalculationType>;
public :
static inline this_type apply(this_type const& input)
{
return input;
}
};
template
<
typename RadiusType,
typename CalculationType,
typename P,
typename PS
>
struct result_from_distance
<
comparable::cross_track<RadiusType, CalculationType>, P, PS
>
{
private :
using strategy_type = comparable::cross_track<RadiusType, CalculationType>;
using return_type = typename return_type<strategy_type, P, PS>::type;
public :
template <typename T>
static inline return_type apply(strategy_type const& strategy,
T const& distance)
{
return_type const s
= sin( (distance / strategy.radius()) / return_type(2.0) );
return s * s;
}
};
/*
TODO: spherical polar coordinate system requires "get_as_radian_equatorial<>"
template <typename Point, typename PointOfSegment, typename Strategy>
struct default_strategy
<
segment_tag, Point, PointOfSegment,
spherical_polar_tag, spherical_polar_tag,
Strategy
>
{
typedef cross_track
<
void,
std::conditional_t
<
std::is_void<Strategy>::value,
typename default_strategy
<
point_tag, Point, PointOfSegment,
spherical_polar_tag, spherical_polar_tag
>::type,
Strategy
>
> type;
};
*/
template <typename Point, typename PointOfSegment, typename Strategy>
struct default_strategy
<
point_tag, segment_tag, Point, PointOfSegment,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>
{
using type = cross_track
<
void,
std::conditional_t
<
std::is_void<Strategy>::value,
typename default_strategy
<
point_tag, point_tag, Point, PointOfSegment,
spherical_equatorial_tag, spherical_equatorial_tag
>::type,
Strategy
>
>;
};
template <typename PointOfSegment, typename Point, typename Strategy>
struct default_strategy
<
segment_tag, point_tag, PointOfSegment, Point,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>
{
using type = typename default_strategy
<
point_tag, segment_tag, Point, PointOfSegment,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>::type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::distance
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
@@ -0,0 +1,475 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2016-2020 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP
#include <type_traits>
#include <boost/config.hpp>
#include <boost/concept_check.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/algorithms/detail/assign_box_corners.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace distance
{
namespace details
{
template <typename ReturnType>
class cross_track_box_box_generic
{
public :
template <typename Point, typename PPStrategy, typename PSStrategy>
ReturnType static inline diagonal_case(Point topA,
Point topB,
Point bottomA,
Point bottomB,
bool north_shortest,
bool non_overlap,
PPStrategy pp_strategy,
PSStrategy ps_strategy)
{
if (north_shortest && non_overlap)
{
return pp_strategy.apply(topA, bottomB);
}
if (north_shortest && !non_overlap)
{
return ps_strategy.apply(topA, topB, bottomB);
}
if (!north_shortest && non_overlap)
{
return pp_strategy.apply(bottomA, topB);
}
return ps_strategy.apply(bottomA, topB, bottomB);
}
template
<
typename Box1,
typename Box2,
typename PPStrategy,
typename PSStrategy
>
ReturnType static inline apply (Box1 const& box1,
Box2 const& box2,
PPStrategy pp_strategy,
PSStrategy ps_strategy)
{
// this method assumes that the coordinates of the point and
// the box are normalized
typedef typename point_type<Box1>::type box_point_type1;
typedef typename point_type<Box2>::type box_point_type2;
box_point_type1 bottom_left1, bottom_right1, top_left1, top_right1;
geometry::detail::assign_box_corners(box1,
bottom_left1, bottom_right1,
top_left1, top_right1);
box_point_type2 bottom_left2, bottom_right2, top_left2, top_right2;
geometry::detail::assign_box_corners(box2,
bottom_left2, bottom_right2,
top_left2, top_right2);
ReturnType lon_min1 = geometry::get_as_radian<0>(bottom_left1);
ReturnType const lat_min1 = geometry::get_as_radian<1>(bottom_left1);
ReturnType lon_max1 = geometry::get_as_radian<0>(top_right1);
ReturnType const lat_max1 = geometry::get_as_radian<1>(top_right1);
ReturnType lon_min2 = geometry::get_as_radian<0>(bottom_left2);
ReturnType const lat_min2 = geometry::get_as_radian<1>(bottom_left2);
ReturnType lon_max2 = geometry::get_as_radian<0>(top_right2);
ReturnType const lat_max2 = geometry::get_as_radian<1>(top_right2);
ReturnType const two_pi = math::two_pi<ReturnType>();
// Test which sides of the boxes are closer and if boxes cross
// antimeridian
bool right_wrap;
if (lon_min2 > 0 && lon_max2 < 0) // box2 crosses antimeridian
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(box2 crosses antimeridian)";
#endif
right_wrap = lon_min2 - lon_max1 < lon_min1 - lon_max2;
lon_max2 += two_pi;
if (lon_min1 > 0 && lon_max1 < 0) // both boxes crosses antimeridian
{
lon_max1 += two_pi;
}
}
else if (lon_min1 > 0 && lon_max1 < 0) // only box1 crosses antimeridian
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(box1 crosses antimeridian)";
#endif
return apply(box2, box1, pp_strategy, ps_strategy);
}
else
{
right_wrap = lon_max1 <= lon_min2
? lon_min2 - lon_max1 < two_pi - (lon_max2 - lon_min1)
: lon_min1 - lon_max2 > two_pi - (lon_max1 - lon_min2);
}
// Check1: if box2 crosses the band defined by the
// minimum and maximum longitude of box1; if yes, determine
// if the box2 is above, below or intersects/is inside box1 and compute
// the distance (easy in this case)
bool lon_min12 = lon_min1 <= lon_min2;
bool right = lon_max1 <= lon_min2;
bool left = lon_min1 >= lon_max2;
bool lon_max12 = lon_max1 <= lon_max2;
if ((lon_min12 && !right)
|| (!left && !lon_max12)
|| (!lon_min12 && lon_max12))
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(up-down)\n";
#endif
if (lat_min1 > lat_max2)
{
return geometry::strategy::distance::services::result_from_distance
<
PSStrategy, box_point_type1, box_point_type2
>::apply(ps_strategy, ps_strategy
.vertical_or_meridian(lat_min1, lat_max2));
}
else if (lat_max1 < lat_min2)
{
return geometry::strategy::distance::services::result_from_distance
<
PSStrategy, box_point_type1, box_point_type2
>::apply(ps_strategy, ps_strategy
.vertical_or_meridian(lat_min2, lat_max1));
}
else
{
//BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max);
return ReturnType(0);
}
}
// Check2: if box2 is right/left of box1
// the max lat of box2 should be less than the max lat of box1
bool bottom_max;
ReturnType top_common = (std::min)(lat_max1, lat_max2);
ReturnType bottom_common = (std::max)(lat_min1, lat_min2);
// true if the closest points are on northern hemisphere
bool north_shortest = top_common + bottom_common > 0;
// true if box bands do not overlap
bool non_overlap = top_common < bottom_common;
if (north_shortest)
{
bottom_max = lat_max1 >= lat_max2;
}
else
{
bottom_max = lat_min1 <= lat_min2;
}
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(diagonal)";
#endif
if (bottom_max && !right_wrap)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(bottom left)";
#endif
return diagonal_case(top_right2, top_left1,
bottom_right2, bottom_left1,
north_shortest, non_overlap,
pp_strategy, ps_strategy);
}
if (bottom_max && right_wrap)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(bottom right)";
#endif
return diagonal_case(top_left2, top_right1,
bottom_left2, bottom_right1,
north_shortest, non_overlap,
pp_strategy, ps_strategy);
}
if (!bottom_max && !right_wrap)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(top left)";
#endif
return diagonal_case(top_left1, top_right2,
bottom_left1, bottom_right2,
north_shortest, non_overlap,
pp_strategy, ps_strategy);
}
if (!bottom_max && right_wrap)
{
#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX
std::cout << "(top right)";
#endif
return diagonal_case(top_right1, top_left2,
bottom_right1, bottom_left2,
north_shortest, non_overlap,
pp_strategy, ps_strategy);
}
return ReturnType(0);
}
};
} //namespace details
/*!
\brief Strategy functor for distance box to box calculation
\ingroup strategies
\details Class which calculates the distance of a box to a box, for
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>
>
class cross_track_box_box
{
public:
template <typename Box1, typename Box2>
struct return_type
: services::return_type<Strategy,
typename point_type<Box1>::type,
typename point_type<Box2>::type>
{};
typedef typename Strategy::radius_type radius_type;
// strategy getters
// point-segment strategy getters
struct distance_ps_strategy
{
typedef cross_track<CalculationType, Strategy> type;
};
typedef typename strategy::distance::services::comparable_type
<
Strategy
>::type pp_comparable_strategy;
typedef std::conditional_t
<
std::is_same
<
pp_comparable_strategy,
Strategy
>::value,
typename strategy::distance::services::comparable_type
<
typename distance_ps_strategy::type
>::type,
typename distance_ps_strategy::type
> ps_strategy_type;
// constructors
inline cross_track_box_box()
{}
explicit inline cross_track_box_box(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline cross_track_box_box(Strategy const& s)
: m_strategy(s)
{}
// It might be useful in the future
// to overload constructor with strategy info.
// crosstrack(...) {}
template <typename Box1, typename Box2>
inline typename return_type<Box1, Box2>::type
apply(Box1 const& box1, Box2 const& box2) const
{
#if !defined(BOOST_MSVC)
BOOST_CONCEPT_ASSERT
(
(concepts::PointDistanceStrategy
<
Strategy,
typename point_type<Box1>::type,
typename point_type<Box2>::type
>)
);
#endif
typedef typename return_type<Box1, Box2>::type return_type;
return details::cross_track_box_box_generic
<return_type>::apply(box1, box2,
m_strategy,
ps_strategy_type(m_strategy));
}
inline typename Strategy::radius_type radius() const
{
return m_strategy.radius();
}
private:
Strategy m_strategy;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename CalculationType, typename Strategy>
struct tag<cross_track_box_box<CalculationType, Strategy> >
{
typedef strategy_tag_distance_box_box type;
};
template <typename CalculationType, typename Strategy, typename Box1, typename Box2>
struct return_type<cross_track_box_box<CalculationType, Strategy>, Box1, Box2>
: cross_track_box_box
<
CalculationType, Strategy
>::template return_type<Box1, Box2>
{};
template <typename CalculationType, typename Strategy>
struct comparable_type<cross_track_box_box<CalculationType, Strategy> >
{
typedef cross_track_box_box
<
CalculationType, typename comparable_type<Strategy>::type
> type;
};
template <typename CalculationType, typename Strategy>
struct get_comparable<cross_track_box_box<CalculationType, Strategy> >
{
typedef cross_track_box_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 comparable_type(strategy.radius());
}
};
template <typename CalculationType, typename Strategy, typename Box1, typename Box2>
struct result_from_distance
<
cross_track_box_box<CalculationType, Strategy>, Box1, Box2
>
{
private:
typedef cross_track_box_box<CalculationType, Strategy> this_strategy;
typedef typename this_strategy::template return_type
<
Box1, Box2
>::type return_type;
public:
template <typename T>
static inline return_type apply(this_strategy const& strategy,
T const& distance)
{
Strategy s(strategy.radius());
return result_from_distance
<
Strategy,
typename point_type<Box1>::type,
typename point_type<Box2>::type
>::apply(s, distance);
}
};
// define cross_track_box_box<default_point_segment_strategy> as
// default box-box strategy for the spherical equatorial coordinate system
template <typename Box1, typename Box2, typename Strategy>
struct default_strategy
<
box_tag, box_tag, Box1, Box2,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>
{
typedef cross_track_box_box
<
void,
std::conditional_t
<
std::is_void<Strategy>::value,
typename default_strategy
<
point_tag, point_tag,
typename point_type<Box1>::type, typename point_type<Box2>::type,
spherical_equatorial_tag, spherical_equatorial_tag
>::type,
Strategy
>
> type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::distance
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP
@@ -0,0 +1,418 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// 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-2020.
// Modifications copyright (c) 2014-2020, 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
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP
#include <type_traits>
#include <boost/config.hpp>
#include <boost/concept_check.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/algorithms/detail/assign_box_corners.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace distance
{
namespace details
{
template <typename ReturnType>
class cross_track_point_box_generic
{
public :
template
<
typename Point,
typename Box,
typename Strategy
>
ReturnType static inline apply (Point const& point,
Box const& box,
Strategy ps_strategy)
{
// this method assumes that the coordinates of the point and
// the box are normalized
typedef typename point_type<Box>::type box_point_type;
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);
ReturnType const plon = geometry::get_as_radian<0>(point);
ReturnType const plat = geometry::get_as_radian<1>(point);
ReturnType const lon_min = geometry::get_as_radian<0>(bottom_left);
ReturnType const lat_min = geometry::get_as_radian<1>(bottom_left);
ReturnType const lon_max = geometry::get_as_radian<0>(top_right);
ReturnType const lat_max = geometry::get_as_radian<1>(top_right);
ReturnType const pi = math::pi<ReturnType>();
ReturnType const two_pi = math::two_pi<ReturnType>();
typedef typename point_type<Box>::type box_point_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 geometry::strategy::distance::services::result_from_distance
<
Strategy, Point, box_point_type
>::apply(ps_strategy, ps_strategy
.vertical_or_meridian(plat, lat_max));
}
else if (plat < lat_min)
{
return geometry::strategy::distance::services::result_from_distance
<
Strategy, Point, box_point_type
>::apply(ps_strategy, ps_strategy
.vertical_or_meridian(lat_min, plat));
}
else
{
BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max);
return ReturnType(0);
}
}
// Otherwise determine which among 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
ReturnType const two = 2.0;
bool use_left_segment;
if (lon_max > pi)
{
// the box crosses the antimeridian
// midway longitude = lon_min - (lon_min + (lon_max - 2 * pi)) / 2;
ReturnType const lon_midway = (lon_min - lon_max) / two + pi;
BOOST_GEOMETRY_ASSERT(lon_midway >= -pi && lon_midway <= pi);
use_left_segment = plon > lon_midway;
}
else
{
// the box does not cross the antimeridian
ReturnType const lon_sum = lon_min + lon_max;
if (math::equals(lon_sum, ReturnType(0)))
{
// special case: the box is symmetric with respect to
// the prime meridian; the midway meridian is the antimeridian
use_left_segment = plon < lon_min;
}
else
{
// midway long. = lon_min - (2 * pi - (lon_max - lon_min)) / 2;
ReturnType lon_midway = (lon_min + lon_max) / two - pi;
// normalize the midway longitude
if (lon_midway > pi)
{
lon_midway -= two_pi;
}
else if (lon_midway < -pi)
{
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 use_left_segment
? ps_strategy.apply(point, bottom_left, top_left)
: ps_strategy.apply(point, bottom_right, top_right);
}
};
} //namespace details
/*!
\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-point distance strategy
\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>
>
class cross_track_point_box
{
public:
template <typename Point, typename Box>
struct return_type
: services::return_type<Strategy, Point, typename point_type<Box>::type>
{};
typedef typename Strategy::radius_type radius_type;
// strategy getters
// point-segment strategy getters
struct distance_ps_strategy
{
typedef cross_track<CalculationType, Strategy> type;
};
typedef typename strategy::distance::services::comparable_type
<
Strategy
>::type pp_comparable_strategy;
typedef std::conditional_t
<
std::is_same
<
pp_comparable_strategy,
Strategy
>::value,
typename strategy::distance::services::comparable_type
<
typename distance_ps_strategy::type
>::type,
typename distance_ps_strategy::type
> ps_strategy_type;
// constructors
inline cross_track_point_box()
{}
explicit inline cross_track_point_box(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline cross_track_point_box(Strategy const& s)
: m_strategy(s)
{}
// methods
// 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
(
(concepts::PointDistanceStrategy
<
Strategy, Point, typename point_type<Box>::type
>)
);
#endif
typedef typename return_type<Point, Box>::type return_type;
return details::cross_track_point_box_generic
<return_type>::apply(point, box,
ps_strategy_type(m_strategy));
}
inline typename Strategy::radius_type radius() const
{
return m_strategy.radius();
}
private:
Strategy m_strategy;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename CalculationType, typename Strategy>
struct tag<cross_track_point_box<CalculationType, Strategy> >
{
typedef strategy_tag_distance_point_box type;
};
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>
{};
template <typename CalculationType, typename Strategy>
struct comparable_type<cross_track_point_box<CalculationType, Strategy> >
{
typedef cross_track_point_box
<
CalculationType, typename comparable_type<Strategy>::type
> type;
};
template <typename CalculationType, typename Strategy>
struct get_comparable<cross_track_point_box<CalculationType, 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 comparable_type(strategy.radius());
}
};
template <typename CalculationType, typename Strategy, typename P, typename Box>
struct result_from_distance
<
cross_track_point_box<CalculationType, Strategy>, P, Box
>
{
private:
typedef cross_track_point_box<CalculationType, Strategy> this_strategy;
typedef typename this_strategy::template return_type
<
P, Box
>::type return_type;
public:
template <typename T>
static inline return_type apply(this_strategy const& strategy,
T const& 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
<
point_tag, box_tag, Point, Box,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>
{
typedef cross_track_point_box
<
void,
std::conditional_t
<
std::is_void<Strategy>::value,
typename default_strategy
<
point_tag, point_tag,
Point, typename point_type<Box>::type,
spherical_equatorial_tag, spherical_equatorial_tag
>::type,
Strategy
>
> type;
};
template <typename Box, typename Point, typename Strategy>
struct default_strategy
<
box_tag, point_tag, Box, Point,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>
{
typedef typename default_strategy
<
point_tag, box_tag, Point, Box,
spherical_equatorial_tag, spherical_equatorial_tag,
Strategy
>::type type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::distance
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP
@@ -0,0 +1,335 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2017, 2018.
// Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_HAVERSINE_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_HAVERSINE_HPP
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/srs/sphere.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/strategies/spherical/get_radius.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace distance
{
namespace comparable
{
// Comparable haversine.
// To compare distances, we can avoid:
// - multiplication with radius and 2.0
// - applying sqrt
// - applying asin (which is strictly (monotone) increasing)
template
<
typename RadiusTypeOrSphere = double,
typename CalculationType = void
>
class haversine
{
public :
template <typename Point1, typename Point2>
struct calculation_type
: promote_floating_point
<
typename select_calculation_type
<
Point1,
Point2,
CalculationType
>::type
>
{};
typedef typename strategy_detail::get_radius
<
RadiusTypeOrSphere
>::type radius_type;
inline haversine()
: m_radius(1.0)
{}
template <typename RadiusOrSphere>
explicit inline haversine(RadiusOrSphere const& radius_or_sphere)
: m_radius(strategy_detail::get_radius
<
RadiusOrSphere
>::apply(radius_or_sphere))
{}
template <typename Point1, typename Point2>
static inline typename calculation_type<Point1, Point2>::type
apply(Point1 const& p1, Point2 const& p2)
{
return calculate<typename calculation_type<Point1, Point2>::type>(
get_as_radian<0>(p1), get_as_radian<1>(p1),
get_as_radian<0>(p2), get_as_radian<1>(p2)
);
}
inline radius_type radius() const
{
return m_radius;
}
private :
template <typename R, typename T1, typename T2>
static inline R calculate(T1 const& lon1, T1 const& lat1,
T2 const& lon2, T2 const& lat2)
{
return math::hav(lat2 - lat1)
+ cos(lat1) * cos(lat2) * math::hav(lon2 - lon1);
}
radius_type m_radius;
};
} // namespace comparable
/*!
\brief Distance calculation for spherical coordinates
on a perfect sphere using haversine
\ingroup strategies
\tparam RadiusTypeOrSphere \tparam_radius_or_sphere
\tparam CalculationType \tparam_calculation
\author Adapted from: http://williams.best.vwh.net/avform.htm
\see http://en.wikipedia.org/wiki/Great-circle_distance
\note (from Wiki:) The great circle distance d between two
points with coordinates {lat1,lon1} and {lat2,lon2} is given by:
d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
A mathematically equivalent formula, which is less subject
to rounding error for short distances is:
d=2*asin(sqrt((sin((lat1-lat2) / 2))^2
+ cos(lat1)*cos(lat2)*(sin((lon1-lon2) / 2))^2))
\qbk{
[heading See also]
[link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)]
}
*/
template
<
typename RadiusTypeOrSphere = double,
typename CalculationType = void
>
class haversine
{
typedef comparable::haversine<RadiusTypeOrSphere, CalculationType> comparable_type;
public :
template <typename Point1, typename Point2>
struct calculation_type
: services::return_type<comparable_type, Point1, Point2>
{};
typedef typename strategy_detail::get_radius
<
RadiusTypeOrSphere
>::type radius_type;
/*!
\brief Default constructor, radius set to 1.0 for the unit sphere
*/
inline haversine()
: m_radius(1.0)
{}
/*!
\brief Constructor
\param radius_or_sphere radius of the sphere or sphere model
*/
template <typename RadiusOrSphere>
explicit inline haversine(RadiusOrSphere const& radius_or_sphere)
: m_radius(strategy_detail::get_radius
<
RadiusOrSphere
>::apply(radius_or_sphere))
{}
/*!
\brief applies the distance calculation
\return the calculated distance (including multiplying with radius)
\param p1 first point
\param p2 second point
*/
template <typename Point1, typename Point2>
inline typename calculation_type<Point1, Point2>::type
apply(Point1 const& p1, Point2 const& p2) const
{
typedef typename calculation_type<Point1, Point2>::type calculation_type;
calculation_type const a = comparable_type::apply(p1, p2);
calculation_type const c = calculation_type(2.0) * asin(math::sqrt(a));
return calculation_type(m_radius) * c;
}
/*!
\brief access to radius value
\return the radius
*/
inline radius_type radius() const
{
return m_radius;
}
private :
radius_type m_radius;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename RadiusType, typename CalculationType>
struct tag<haversine<RadiusType, CalculationType> >
{
typedef strategy_tag_distance_point_point type;
};
template <typename RadiusType, typename CalculationType, typename P1, typename P2>
struct return_type<haversine<RadiusType, CalculationType>, P1, P2>
: haversine<RadiusType, CalculationType>::template calculation_type<P1, P2>
{};
template <typename RadiusType, typename CalculationType>
struct comparable_type<haversine<RadiusType, CalculationType> >
{
typedef comparable::haversine<RadiusType, CalculationType> type;
};
template <typename RadiusType, typename CalculationType>
struct get_comparable<haversine<RadiusType, CalculationType> >
{
private :
typedef haversine<RadiusType, CalculationType> this_type;
typedef comparable::haversine<RadiusType, CalculationType> comparable_type;
public :
static inline comparable_type apply(this_type const& input)
{
return comparable_type(input.radius());
}
};
template <typename RadiusType, typename CalculationType, typename P1, typename P2>
struct result_from_distance<haversine<RadiusType, CalculationType>, P1, P2>
{
private :
typedef haversine<RadiusType, CalculationType> this_type;
typedef typename return_type<this_type, P1, P2>::type return_type;
public :
template <typename T>
static inline return_type apply(this_type const& , T const& value)
{
return return_type(value);
}
};
// Specializations for comparable::haversine
template <typename RadiusType, typename CalculationType>
struct tag<comparable::haversine<RadiusType, CalculationType> >
{
typedef strategy_tag_distance_point_point type;
};
template <typename RadiusType, typename CalculationType, typename P1, typename P2>
struct return_type<comparable::haversine<RadiusType, CalculationType>, P1, P2>
: comparable::haversine<RadiusType, CalculationType>::template calculation_type<P1, P2>
{};
template <typename RadiusType, typename CalculationType>
struct comparable_type<comparable::haversine<RadiusType, CalculationType> >
{
typedef comparable::haversine<RadiusType, CalculationType> type;
};
template <typename RadiusType, typename CalculationType>
struct get_comparable<comparable::haversine<RadiusType, CalculationType> >
{
private :
typedef comparable::haversine<RadiusType, CalculationType> this_type;
public :
static inline this_type apply(this_type const& input)
{
return input;
}
};
template <typename RadiusType, typename CalculationType, typename P1, typename P2>
struct result_from_distance<comparable::haversine<RadiusType, CalculationType>, P1, P2>
{
private :
typedef comparable::haversine<RadiusType, CalculationType> strategy_type;
typedef typename return_type<strategy_type, P1, P2>::type return_type;
public :
template <typename T>
static inline return_type apply(strategy_type const& strategy, T const& distance)
{
return_type const s = sin((distance / strategy.radius()) / return_type(2));
return s * s;
}
};
// Register it as the default for point-types
// in a spherical equatorial coordinate system
template <typename Point1, typename Point2>
struct default_strategy
<
point_tag, point_tag, Point1, Point2,
spherical_equatorial_tag, spherical_equatorial_tag
>
{
typedef strategy::distance::haversine<typename select_coordinate_type<Point1, Point2>::type> type;
};
// Note: spherical polar coordinate system requires "get_as_radian_equatorial"
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::distance
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_HAVERSINE_HPP
@@ -0,0 +1,374 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2018-2021 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_SEGMENT_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_SEGMENT_BOX_HPP
#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>
#include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
#include <boost/geometry/strategies/spherical/distance_cross_track.hpp>
#include <boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/cartesian/point_in_box.hpp> // spherical
#include <boost/geometry/strategies/spherical/ssf.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace distance
{
struct generic_segment_box
{
template
<
typename LessEqual,
typename ReturnType,
typename SegmentPoint,
typename BoxPoint,
typename Strategies
>
static inline ReturnType segment_below_of_box(
SegmentPoint const& p0,
SegmentPoint const& p1,
BoxPoint const&,
BoxPoint const& top_right,
BoxPoint const& bottom_left,
BoxPoint const& bottom_right,
Strategies const& strategies)
{
ReturnType result;
typename LessEqual::other less_equal;
typedef geometry::model::segment<SegmentPoint> segment_type;
// if cs_tag is spherical_tag check segment's cs_tag with spherical_equatorial_tag as default
typedef std::conditional_t
<
std::is_same<typename Strategies::cs_tag, spherical_tag>::value,
std::conditional_t
<
std::is_same
<
typename geometry::cs_tag<segment_type>::type,
spherical_polar_tag
>::value,
spherical_polar_tag, spherical_equatorial_tag
>,
typename Strategies::cs_tag
> cs_tag;
typedef geometry::detail::disjoint::
disjoint_segment_box_sphere_or_spheroid<cs_tag>
disjoint_sb;
typedef typename disjoint_sb::disjoint_info disjoint_info_type;
segment_type seg(p0, p1);
geometry::model::box<BoxPoint> input_box;
geometry::set_from_radian<geometry::min_corner, 0>
(input_box, geometry::get_as_radian<0>(bottom_left));
geometry::set_from_radian<geometry::min_corner, 1>
(input_box, geometry::get_as_radian<1>(bottom_left));
geometry::set_from_radian<geometry::max_corner, 0>
(input_box, geometry::get_as_radian<0>(top_right));
geometry::set_from_radian<geometry::max_corner, 1>
(input_box, geometry::get_as_radian<1>(top_right));
SegmentPoint p_max;
// TODO: Think about rewriting this and simply passing strategies
// The problem is that this algorithm is called by disjoint(S/B) strategies.
disjoint_info_type disjoint_result = disjoint_sb::
apply(seg, input_box, p_max,
strategies.azimuth(),
strategies.normalize(p0),
strategies.covered_by(p0, input_box), // disjoint
strategies.disjoint(input_box, input_box));
if (disjoint_result == disjoint_info_type::intersect) //intersect
{
return 0;
}
// disjoint but vertex not computed
if (disjoint_result == disjoint_info_type::disjoint_no_vertex)
{
typedef typename coordinate_type<SegmentPoint>::type CT;
geometry::model::box<SegmentPoint> mbr;
geometry::envelope(seg, mbr, strategies);
CT lon1 = geometry::get_as_radian<0>(p0);
CT lat1 = geometry::get_as_radian<1>(p0);
CT lon2 = geometry::get_as_radian<0>(p1);
CT lat2 = geometry::get_as_radian<1>(p1);
if (lon1 > lon2)
{
std::swap(lon1, lon2);
std::swap(lat1, lat2);
}
CT vertex_lat;
CT lat_sum = lat1 + lat2;
if (lat_sum > CT(0))
{
vertex_lat = geometry::get_as_radian<geometry::max_corner, 1>(mbr);
} else {
vertex_lat = geometry::get_as_radian<geometry::min_corner, 1>(mbr);
}
CT alp1;
strategies.azimuth().apply(lon1, lat1, lon2, lat2, alp1);
// TODO: formula should not call strategy!
CT vertex_lon = geometry::formula::vertex_longitude
<
CT,
cs_tag
>::apply(lon1, lat1, lon2, lat2,
vertex_lat, alp1, strategies.azimuth());
geometry::set_from_radian<0>(p_max, vertex_lon);
geometry::set_from_radian<1>(p_max, vertex_lat);
}
//otherwise disjoint and vertex computed inside disjoint
if (less_equal(geometry::get_as_radian<0>(bottom_left),
geometry::get_as_radian<0>(p_max)))
{
result = boost::numeric_cast<ReturnType>(
strategies.distance(bottom_left, seg).apply(bottom_left, p0, p1));
}
else
{
// TODO: The strategy should not call the algorithm like that
result = geometry::detail::distance::segment_to_box_2D
<
ReturnType,
SegmentPoint,
BoxPoint,
Strategies
>::template call_above_of_box
<
typename LessEqual::other
>(p1, p0, p_max, bottom_right, strategies);
}
return result;
}
template <typename SPoint, typename BPoint>
static void mirror(SPoint& p0,
SPoint& p1,
BPoint& bottom_left,
BPoint& bottom_right,
BPoint& top_left,
BPoint& top_right)
{
//if segment's vertex is the southest point then mirror geometries
if (geometry::get<1>(p0) + geometry::get<1>(p1) < 0)
{
BPoint bl = bottom_left;
BPoint br = bottom_right;
geometry::set<1>(p0, geometry::get<1>(p0) * -1);
geometry::set<1>(p1, geometry::get<1>(p1) * -1);
geometry::set<1>(bottom_left, geometry::get<1>(top_left) * -1);
geometry::set<1>(top_left, geometry::get<1>(bl) * -1);
geometry::set<1>(bottom_right, geometry::get<1>(top_right) * -1);
geometry::set<1>(top_right, geometry::get<1>(br) * -1);
}
}
};
//===========================================================================
template
<
typename CalculationType = void,
typename Strategy = haversine<double, CalculationType>
>
struct spherical_segment_box
{
template <typename PointOfSegment, typename PointOfBox>
struct calculation_type
: promote_floating_point
<
typename strategy::distance::services::return_type
<
Strategy,
PointOfSegment,
PointOfBox
>::type
>
{};
typedef spherical_tag cs_tag;
// constructors
inline spherical_segment_box()
{}
explicit inline spherical_segment_box(typename Strategy::radius_type const& r)
: m_strategy(r)
{}
inline spherical_segment_box(Strategy const& s)
: m_strategy(s)
{}
typename Strategy::radius_type radius() const
{
return m_strategy.radius();
}
// methods
template
<
typename LessEqual, typename ReturnType,
typename SegmentPoint, typename BoxPoint,
typename Strategies
>
inline ReturnType segment_below_of_box(SegmentPoint const& p0,
SegmentPoint const& p1,
BoxPoint const& top_left,
BoxPoint const& top_right,
BoxPoint const& bottom_left,
BoxPoint const& bottom_right,
Strategies const& strategies) const
{
return generic_segment_box::segment_below_of_box
<
LessEqual,
ReturnType
>(p0,p1,top_left,top_right,bottom_left,bottom_right,
strategies);
}
template <typename SPoint, typename BPoint>
static void mirror(SPoint& p0,
SPoint& p1,
BPoint& bottom_left,
BPoint& bottom_right,
BPoint& top_left,
BPoint& top_right)
{
generic_segment_box::mirror(p0, p1,
bottom_left, bottom_right,
top_left, top_right);
}
private:
Strategy m_strategy;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename CalculationType, typename Strategy>
struct tag<spherical_segment_box<CalculationType, Strategy> >
{
typedef strategy_tag_distance_segment_box type;
};
template <typename CalculationType, typename Strategy, typename PS, typename PB>
struct return_type<spherical_segment_box<CalculationType, Strategy>, PS, PB>
: spherical_segment_box<CalculationType, Strategy>::template calculation_type<PS, PB>
{};
template <typename CalculationType, typename Strategy>
struct comparable_type<spherical_segment_box<CalculationType, Strategy> >
{
// Define a cartesian_segment_box strategy with its underlying point-segment
// strategy being comparable
typedef spherical_segment_box
<
CalculationType,
typename comparable_type<Strategy>::type
> type;
};
template <typename CalculationType, typename Strategy>
struct get_comparable<spherical_segment_box<CalculationType, Strategy> >
{
typedef typename comparable_type
<
spherical_segment_box<CalculationType, Strategy>
>::type comparable_type;
public :
static inline comparable_type apply(spherical_segment_box<CalculationType, Strategy> const& )
{
return comparable_type();
}
};
template <typename CalculationType, typename Strategy, typename PS, typename PB>
struct result_from_distance<spherical_segment_box<CalculationType, Strategy>, PS, PB>
{
private :
typedef typename return_type<
spherical_segment_box
<
CalculationType,
Strategy
>,
PS,
PB
>::type return_type;
public :
template <typename T>
static inline return_type apply(spherical_segment_box<CalculationType,
Strategy> const& ,
T const& value)
{
Strategy s;
return result_from_distance<Strategy, PS, PB>::apply(s, value);
}
};
template <typename Segment, typename Box>
struct default_strategy
<
segment_tag, box_tag, Segment, Box,
spherical_equatorial_tag, spherical_equatorial_tag
>
{
typedef spherical_segment_box<> type;
};
template <typename Box, typename Segment>
struct default_strategy
<
box_tag, segment_tag, Box, Segment,
spherical_equatorial_tag, spherical_equatorial_tag
>
{
typedef typename default_strategy
<
segment_tag, box_tag, Segment, Box,
spherical_equatorial_tag, spherical_equatorial_tag
>::type type;
};
}
#endif
}} // namespace strategy::distance
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_SEGMENT_BOX_HPP
+21
View File
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_ENVELOPE_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/envelope.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_HPP
+21
View File
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_ENVELOPE_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_BOX_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/envelope_box.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_BOX_HPP
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/envelope_multipoint.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_MULTIPOINT_HPP
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_ENVELOPE_POINT_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_POINT_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/envelope_point.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_POINT_HPP
@@ -0,0 +1,22 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_ENVELOPE_SEGMENT_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_SEGMENT_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/envelope_segment.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_ENVELOPE_SEGMENT_HPP
+21
View File
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_EXPAND_BOX_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_BOX_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/expand_box.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_BOX_HPP
+21
View File
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_EXPAND_POINT_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_POINT_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/expand_point.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_POINT_HPP
@@ -0,0 +1,21 @@
// Boost.Geometry
// Copyright (c) 2020, 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_STRATEGIES_SPHERICAL_EXPAND_SEGMENT_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_SEGMENT_HPP
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.")
#include <boost/geometry/strategy/spherical/expand_segment.hpp>
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_EXPAND_SEGMENT_HPP
+81
View File
@@ -0,0 +1,81 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2016-2018 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_GET_RADIUS_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_GET_RADIUS_HPP
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace strategy_detail
{
template
<
typename RadiusTypeOrSphere,
typename Tag = typename tag<RadiusTypeOrSphere>::type
>
struct get_radius
{
typedef typename geometry::radius_type<RadiusTypeOrSphere>::type type;
static type apply(RadiusTypeOrSphere const& sphere)
{
return geometry::get_radius<0>(sphere);
}
};
template <typename RadiusTypeOrSphere>
struct get_radius<RadiusTypeOrSphere, void>
{
typedef RadiusTypeOrSphere type;
static type apply(RadiusTypeOrSphere const& radius)
{
return radius;
}
};
// For backward compatibility
template <typename Point>
struct get_radius<Point, point_tag>
{
typedef typename select_most_precise
<
typename coordinate_type<Point>::type,
double
>::type type;
template <typename RadiusOrSphere>
static typename get_radius<RadiusOrSphere>::type
apply(RadiusOrSphere const& radius_or_sphere)
{
return get_radius<RadiusOrSphere>::apply(radius_or_sphere);
}
};
} // namespace strategy_detail
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_GET_RADIUS_HPP
+969
View File
@@ -0,0 +1,969 @@
// Boost.Geometry
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2016-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_INTERSECTION_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_INTERSECTION_HPP
#include <algorithm>
#include <type_traits>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/algorithms/detail/assign_values.hpp>
#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp>
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
#include <boost/geometry/algorithms/detail/recalculate.hpp>
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/arithmetic/cross_product.hpp>
#include <boost/geometry/arithmetic/dot_product.hpp>
#include <boost/geometry/arithmetic/normalize.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/geometries/concepts/point_concept.hpp>
#include <boost/geometry/geometries/concepts/segment_concept.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/policies/robustness/segment_ratio.hpp>
#include <boost/geometry/strategy/spherical/area.hpp>
#include <boost/geometry/strategy/spherical/envelope.hpp>
#include <boost/geometry/strategy/spherical/expand_box.hpp>
#include <boost/geometry/strategy/spherical/expand_segment.hpp>
#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/intersection.hpp>
#include <boost/geometry/strategies/intersection_result.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/side_info.hpp>
#include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
#include <boost/geometry/strategies/spherical/disjoint_segment_box.hpp>
#include <boost/geometry/strategies/spherical/distance_haversine.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/spherical/point_in_poly_winding.hpp>
#include <boost/geometry/strategies/spherical/ssf.hpp>
#include <boost/geometry/strategies/within.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace intersection
{
// NOTE:
// The coordinates of crossing IP may be calculated with small precision in some cases.
// For double, near the equator noticed error ~1e-9 so far greater than
// machine epsilon which is ~1e-16. This error is ~0.04m.
// E.g. consider two cases, one near the origin and the second one rotated by 90 deg around Z or SN axis.
// After the conversion from spherical degrees to cartesian 3d the following coordinates
// are calculated:
// for sph (-1 -1, 1 1) deg cart3d ys are -0.017449748351250485 and 0.017449748351250485
// for sph (89 -1, 91 1) deg cart3d xs are 0.017449748351250571 and -0.017449748351250450
// During the conversion degrees must first be converted to radians and then radians
// are passed into trigonometric functions. The error may have several causes:
// 1. Radians cannot represent exactly the same angles as degrees.
// 2. Different longitudes are passed into sin() for x, corresponding to cos() for y,
// and for different angle the error of the result may be different.
// 3. These non-corresponding cartesian coordinates are used in calculation,
// e.g. multiplied several times in cross and dot products.
// If it was a problem this strategy could e.g. "normalize" longitudes before the conversion using the source units
// by rotating the globe around Z axis, so moving longitudes always the same way towards the origin,
// assuming this could help which is not clear.
// For now, intersection points near the endpoints are checked explicitly if needed (if the IP is near the endpoint)
// to generate precise result for them. Only the crossing (i) case may suffer from lower precision.
template
<
typename CalcPolicy,
typename CalculationType = void
>
struct ecef_segments
{
typedef spherical_tag cs_tag;
enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 };
// segment_intersection_info cannot outlive relate_ecef_segments
template <typename CoordinateType, typename SegmentRatio, typename Vector3d>
struct segment_intersection_info
{
segment_intersection_info(CalcPolicy const& calc)
: calc_policy(calc)
{}
template <typename Point, typename Segment1, typename Segment2>
void calculate(Point& point, Segment1 const& a, Segment2 const& b) const
{
if (ip_flag == ipi_inters)
{
// TODO: assign the rest of coordinates
point = calc_policy.template from_cart3d<Point>(intersection_point);
}
else if (ip_flag == ipi_at_a1)
{
detail::assign_point_from_index<0>(a, point);
}
else if (ip_flag == ipi_at_a2)
{
detail::assign_point_from_index<1>(a, point);
}
else if (ip_flag == ipi_at_b1)
{
detail::assign_point_from_index<0>(b, point);
}
else // ip_flag == ipi_at_b2
{
detail::assign_point_from_index<1>(b, point);
}
}
Vector3d intersection_point;
SegmentRatio robust_ra;
SegmentRatio robust_rb;
intersection_point_flag ip_flag;
CalcPolicy const& calc_policy;
};
// Relate segments a and b
template
<
typename UniqueSubRange1,
typename UniqueSubRange2,
typename Policy
>
static inline typename Policy::return_type
apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q,
Policy const&)
{
// For now create it using default constructor. In the future it could
// be stored in strategy. However then apply() wouldn't be static and
// all relops and setops would have to take the strategy or model.
// Initialize explicitly to prevent compiler errors in case of PoD type
CalcPolicy const calc_policy = CalcPolicy();
typedef typename UniqueSubRange1::point_type point1_type;
typedef typename UniqueSubRange2::point_type point2_type;
BOOST_CONCEPT_ASSERT( (concepts::ConstPoint<point1_type>) );
BOOST_CONCEPT_ASSERT( (concepts::ConstPoint<point2_type>) );
point1_type const& a1 = range_p.at(0);
point1_type const& a2 = range_p.at(1);
point2_type const& b1 = range_q.at(0);
point2_type const& b2 = range_q.at(1);
typedef model::referring_segment<point1_type const> segment1_type;
typedef model::referring_segment<point2_type const> segment2_type;
segment1_type const a(a1, a2);
segment2_type const b(b1, b2);
// TODO: check only 2 first coordinates here?
bool a_is_point = equals_point_point(a1, a2);
bool b_is_point = equals_point_point(b1, b2);
if(a_is_point && b_is_point)
{
return equals_point_point(a1, b2)
? Policy::degenerate(a, true)
: Policy::disjoint()
;
}
typedef typename select_calculation_type
<segment1_type, segment2_type, CalculationType>::type calc_t;
calc_t const c0 = 0;
calc_t const c1 = 1;
typedef model::point<calc_t, 3, cs::cartesian> vec3d_t;
vec3d_t const a1v = calc_policy.template to_cart3d<vec3d_t>(a1);
vec3d_t const a2v = calc_policy.template to_cart3d<vec3d_t>(a2);
vec3d_t const b1v = calc_policy.template to_cart3d<vec3d_t>(b1);
vec3d_t const b2v = calc_policy.template to_cart3d<vec3d_t>(b2);
bool degen_neq_coords = false;
side_info sides;
typename CalcPolicy::template plane<vec3d_t>
plane2 = calc_policy.get_plane(b1v, b2v);
calc_t dist_b1_b2 = 0;
if (! b_is_point)
{
calculate_dist(b1v, b2v, plane2, dist_b1_b2);
if (math::equals(dist_b1_b2, c0))
{
degen_neq_coords = true;
b_is_point = true;
dist_b1_b2 = 0;
}
else
{
// not normalized normals, the same as in side strategy
sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v));
if (sides.same<0>())
{
// Both points are at same side of other segment, we can leave
return Policy::disjoint();
}
}
}
typename CalcPolicy::template plane<vec3d_t>
plane1 = calc_policy.get_plane(a1v, a2v);
calc_t dist_a1_a2 = 0;
if (! a_is_point)
{
calculate_dist(a1v, a2v, plane1, dist_a1_a2);
if (math::equals(dist_a1_a2, c0))
{
degen_neq_coords = true;
a_is_point = true;
dist_a1_a2 = 0;
}
else
{
// not normalized normals, the same as in side strategy
sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v));
if (sides.same<1>())
{
// Both points are at same side of other segment, we can leave
return Policy::disjoint();
}
}
}
// NOTE: at this point the segments may still be disjoint
calc_t len1 = 0;
// point or opposite sides of a sphere/spheroid, assume point
if (! a_is_point && ! detail::vec_normalize(plane1.normal, len1))
{
a_is_point = true;
if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0)
{
sides.set<0>(0, 0);
}
}
calc_t len2 = 0;
if (! b_is_point && ! detail::vec_normalize(plane2.normal, len2))
{
b_is_point = true;
if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0)
{
sides.set<1>(0, 0);
}
}
// check both degenerated once more
if (a_is_point && b_is_point)
{
return equals_point_point(a1, b2)
? Policy::degenerate(a, true)
: Policy::disjoint()
;
}
// NOTE: at this point the segments may still be disjoint
// NOTE: at this point one of the segments may be degenerated
bool collinear = sides.collinear();
if (! collinear)
{
// NOTE: for some approximations it's possible that both points may lie
// on the same geodesic but still some of the sides may be != 0.
// This is e.g. true for long segments represented as elliptic arcs
// with origin different than the center of the coordinate system.
// So make the sides consistent
// WARNING: the side strategy doesn't have the info about the other
// segment so it may return results inconsistent with this intersection
// strategy, as it checks both segments for consistency
if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0)
{
collinear = true;
sides.set<1>(0, 0);
}
else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0)
{
collinear = true;
sides.set<0>(0, 0);
}
}
calc_t dot_n1n2 = dot_product(plane1.normal, plane2.normal);
// NOTE: this is technically not needed since theoretically above sides
// are calculated, but just in case check the normals.
// Have in mind that SSF side strategy doesn't check this.
// collinear if normals are equal or opposite: cos(a) in {-1, 1}
if (! collinear && math::equals(math::abs(dot_n1n2), c1))
{
collinear = true;
sides.set<0>(0, 0);
sides.set<1>(0, 0);
}
if (collinear)
{
if (a_is_point)
{
return collinear_one_degenerated<Policy, calc_t>(a, true, b1, b2, a1, a2, b1v, b2v,
plane2, a1v, a2v, dist_b1_b2, degen_neq_coords);
}
else if (b_is_point)
{
// b2 used to be consistent with (degenerated) checks above (is it needed?)
return collinear_one_degenerated<Policy, calc_t>(b, false, a1, a2, b1, b2, a1v, a2v,
plane1, b1v, b2v, dist_a1_a2, degen_neq_coords);
}
else
{
calc_t dist_a1_b1, dist_a1_b2;
calc_t dist_b1_a1, dist_b1_a2;
calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, b2v, dist_a1_a2, dist_a1_b1);
calculate_collinear_data(a1, a2, b2, b1, a1v, a2v, plane1, b2v, b1v, dist_a1_a2, dist_a1_b2);
calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, a2v, dist_b1_b2, dist_b1_a1);
calculate_collinear_data(b1, b2, a2, a1, b1v, b2v, plane2, a2v, a1v, dist_b1_b2, dist_b1_a2);
// NOTE: The following optimization causes problems with consitency
// It may either be caused by numerical issues or the way how distance is coded:
// as cosine of angle scaled and translated, see: calculate_dist()
/*dist_b1_b2 = dist_a1_b2 - dist_a1_b1;
dist_b1_a1 = -dist_a1_b1;
dist_b1_a2 = dist_a1_a2 - dist_a1_b1;
dist_a1_a2 = dist_b1_a2 - dist_b1_a1;
dist_a1_b1 = -dist_b1_a1;
dist_a1_b2 = dist_b1_b2 - dist_b1_a1;*/
segment_ratio<calc_t> ra_from(dist_b1_a1, dist_b1_b2);
segment_ratio<calc_t> ra_to(dist_b1_a2, dist_b1_b2);
segment_ratio<calc_t> rb_from(dist_a1_b1, dist_a1_a2);
segment_ratio<calc_t> rb_to(dist_a1_b2, dist_a1_a2);
// NOTE: this is probably not needed
int const a1_wrt_b = position_value(c0, dist_a1_b1, dist_a1_b2);
int const a2_wrt_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2);
int const b1_wrt_a = position_value(c0, dist_b1_a1, dist_b1_a2);
int const b2_wrt_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2);
if (a1_wrt_b == 1)
{
ra_from.assign(0, dist_b1_b2);
rb_from.assign(0, dist_a1_a2);
}
else if (a1_wrt_b == 3)
{
ra_from.assign(dist_b1_b2, dist_b1_b2);
rb_to.assign(0, dist_a1_a2);
}
if (a2_wrt_b == 1)
{
ra_to.assign(0, dist_b1_b2);
rb_from.assign(dist_a1_a2, dist_a1_a2);
}
else if (a2_wrt_b == 3)
{
ra_to.assign(dist_b1_b2, dist_b1_b2);
rb_to.assign(dist_a1_a2, dist_a1_a2);
}
if ((a1_wrt_b < 1 && a2_wrt_b < 1) || (a1_wrt_b > 3 && a2_wrt_b > 3))
{
return Policy::disjoint();
}
bool const opposite = dot_n1n2 < c0;
return Policy::segments_collinear(a, b, opposite,
a1_wrt_b, a2_wrt_b, b1_wrt_a, b2_wrt_a,
ra_from, ra_to, rb_from, rb_to);
}
}
else // crossing
{
if (a_is_point || b_is_point)
{
return Policy::disjoint();
}
vec3d_t i1;
intersection_point_flag ip_flag;
calc_t dist_a1_i1, dist_b1_i1;
if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v,
plane1, plane2, calc_policy,
sides, dist_a1_a2, dist_b1_b2,
i1, dist_a1_i1, dist_b1_i1, ip_flag))
{
// intersects
segment_intersection_info
<
calc_t,
segment_ratio<calc_t>,
vec3d_t
> sinfo(calc_policy);
sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2);
sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2);
sinfo.intersection_point = i1;
sinfo.ip_flag = ip_flag;
return Policy::segments_crosses(sides, sinfo, a, b);
}
else
{
return Policy::disjoint();
}
}
}
private:
template <typename Policy, typename CalcT, typename Segment, typename Point1, typename Point2, typename Vec3d, typename Plane>
static inline typename Policy::return_type
collinear_one_degenerated(Segment const& segment, bool degenerated_a,
Point1 const& a1, Point1 const& a2,
Point2 const& b1, Point2 const& b2,
Vec3d const& a1v, Vec3d const& a2v,
Plane const& plane,
Vec3d const& b1v, Vec3d const& b2v,
CalcT const& dist_1_2,
bool degen_neq_coords)
{
CalcT dist_1_o;
return ! calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane, b1v, b2v, dist_1_2, dist_1_o, degen_neq_coords)
? Policy::disjoint()
: Policy::one_degenerate(segment, segment_ratio<CalcT>(dist_1_o, dist_1_2), degenerated_a);
}
template <typename Point1, typename Point2, typename Vec3d, typename Plane, typename CalcT>
static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in
Point2 const& b1, Point2 const& /*b2*/, // in
Vec3d const& a1v, // in
Vec3d const& a2v, // in
Plane const& plane1, // in
Vec3d const& b1v, // in
Vec3d const& b2v, // in
CalcT const& dist_a1_a2, // in
CalcT& dist_a1_b1, // out
bool degen_neq_coords = false) // in
{
// calculate dist_a1_b1
calculate_dist(a1v, a2v, plane1, b1v, dist_a1_b1);
// if b1 is equal to a1
if (is_endpoint_equal(dist_a1_b1, a1, b1))
{
dist_a1_b1 = 0;
return true;
}
// or b1 is equal to a2
else if (is_endpoint_equal(dist_a1_a2 - dist_a1_b1, a2, b1))
{
dist_a1_b1 = dist_a1_a2;
return true;
}
// check the other endpoint of degenerated segment near a pole
if (degen_neq_coords)
{
static CalcT const c0 = 0;
CalcT dist_a1_b2 = 0;
calculate_dist(a1v, a2v, plane1, b2v, dist_a1_b2);
if (math::equals(dist_a1_b2, c0))
{
dist_a1_b1 = 0;
return true;
}
else if (math::equals(dist_a1_a2 - dist_a1_b2, c0))
{
dist_a1_b1 = dist_a1_a2;
return true;
}
}
// or i1 is on b
return segment_ratio<CalcT>(dist_a1_b1, dist_a1_a2).on_segment();
}
template <typename Point1, typename Point2, typename Vec3d, typename Plane, typename CalcT>
static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in
Point2 const& b1, Point2 const& b2, // in
Vec3d const& a1v, Vec3d const& a2v, // in
Vec3d const& b1v, Vec3d const& b2v, // in
Plane const& plane1, // in
Plane const& plane2, // in
CalcPolicy const& calc_policy, // in
side_info const& sides, // in
CalcT const& dist_a1_a2, // in
CalcT const& dist_b1_b2, // in
Vec3d & ip, // out
CalcT& dist_a1_ip, // out
CalcT& dist_b1_ip, // out
intersection_point_flag& ip_flag) // out
{
Vec3d ip1, ip2;
calc_policy.intersection_points(plane1, plane2, ip1, ip2);
calculate_dist(a1v, a2v, plane1, ip1, dist_a1_ip);
ip = ip1;
// choose the opposite side of the globe if the distance is shorter
{
CalcT const d = abs_distance(dist_a1_a2, dist_a1_ip);
if (d > CalcT(0))
{
// TODO: this should be ok not only for sphere
// but requires more investigation
CalcT const dist_a1_i2 = dist_of_i2(dist_a1_ip);
CalcT const d2 = abs_distance(dist_a1_a2, dist_a1_i2);
if (d2 < d)
{
dist_a1_ip = dist_a1_i2;
ip = ip2;
}
}
}
bool is_on_a = false, is_near_a1 = false, is_near_a2 = false;
if (! is_potentially_crossing(dist_a1_a2, dist_a1_ip, is_on_a, is_near_a1, is_near_a2))
{
return false;
}
calculate_dist(b1v, b2v, plane2, ip, dist_b1_ip);
bool is_on_b = false, is_near_b1 = false, is_near_b2 = false;
if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2))
{
return false;
}
// reassign the IP if some endpoints overlap
if (is_near_a1)
{
if (is_near_b1 && equals_point_point(a1, b1))
{
dist_a1_ip = 0;
dist_b1_ip = 0;
//i1 = a1v;
ip_flag = ipi_at_a1;
return true;
}
if (is_near_b2 && equals_point_point(a1, b2))
{
dist_a1_ip = 0;
dist_b1_ip = dist_b1_b2;
//i1 = a1v;
ip_flag = ipi_at_a1;
return true;
}
}
if (is_near_a2)
{
if (is_near_b1 && equals_point_point(a2, b1))
{
dist_a1_ip = dist_a1_a2;
dist_b1_ip = 0;
//i1 = a2v;
ip_flag = ipi_at_a2;
return true;
}
if (is_near_b2 && equals_point_point(a2, b2))
{
dist_a1_ip = dist_a1_a2;
dist_b1_ip = dist_b1_b2;
//i1 = a2v;
ip_flag = ipi_at_a2;
return true;
}
}
// at this point we know that the endpoints doesn't overlap
// reassign IP and distance if the IP is on a segment and one of
// the endpoints of the other segment lies on the former segment
if (is_on_a)
{
if (is_near_b1 && sides.template get<1, 0>() == 0) // b1 wrt a
{
calculate_dist(a1v, a2v, plane1, b1v, dist_a1_ip); // for consistency
dist_b1_ip = 0;
//i1 = b1v;
ip_flag = ipi_at_b1;
return true;
}
if (is_near_b2 && sides.template get<1, 1>() == 0) // b2 wrt a
{
calculate_dist(a1v, a2v, plane1, b2v, dist_a1_ip); // for consistency
dist_b1_ip = dist_b1_b2;
//i1 = b2v;
ip_flag = ipi_at_b2;
return true;
}
}
if (is_on_b)
{
if (is_near_a1 && sides.template get<0, 0>() == 0) // a1 wrt b
{
dist_a1_ip = 0;
calculate_dist(b1v, b2v, plane2, a1v, dist_b1_ip); // for consistency
//i1 = a1v;
ip_flag = ipi_at_a1;
return true;
}
if (is_near_a2 && sides.template get<0, 1>() == 0) // a2 wrt b
{
dist_a1_ip = dist_a1_a2;
calculate_dist(b1v, b2v, plane2, a2v, dist_b1_ip); // for consistency
//i1 = a2v;
ip_flag = ipi_at_a2;
return true;
}
}
ip_flag = ipi_inters;
return is_on_a && is_on_b;
}
template <typename Vec3d, typename Plane, typename CalcT>
static inline void calculate_dist(Vec3d const& a1v, // in
Vec3d const& a2v, // in
Plane const& plane1, // in
CalcT& dist_a1_a2) // out
{
static CalcT const c1 = 1;
CalcT const cos_a1_a2 = plane1.cos_angle_between(a1v, a2v);
dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi]
}
template <typename Vec3d, typename Plane, typename CalcT>
static inline void calculate_dist(Vec3d const& a1v, // in
Vec3d const& /*a2v*/, // in
Plane const& plane1, // in
Vec3d const& i1, // in
CalcT& dist_a1_i1) // out
{
static CalcT const c1 = 1;
static CalcT const c2 = 2;
static CalcT const c4 = 4;
bool is_forward = true;
CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward);
dist_a1_i1 = -cos_a1_i1 + c1; // [0, 2] representing [0, pi]
if (! is_forward) // left or right of a1 on a
{
dist_a1_i1 = -dist_a1_i1; // [0, 2] -> [0, -2] representing [0, -pi]
}
if (dist_a1_i1 <= -c2) // <= -pi
{
dist_a1_i1 += c4; // += 2pi
}
}
/*
template <typename Vec3d, typename Plane, typename CalcT>
static inline void calculate_dists(Vec3d const& a1v, // in
Vec3d const& a2v, // in
Plane const& plane1, // in
Vec3d const& i1, // in
CalcT& dist_a1_a2, // out
CalcT& dist_a1_i1) // out
{
calculate_dist(a1v, a2v, plane1, dist_a1_a2);
calculate_dist(a1v, a2v, plane1, i1, dist_a1_i1);
}
*/
// the dist of the ip on the other side of the sphere
template <typename CalcT>
static inline CalcT dist_of_i2(CalcT const& dist_a1_i1)
{
CalcT const c2 = 2;
CalcT const c4 = 4;
CalcT dist_a1_i2 = dist_a1_i1 - c2; // dist_a1_i2 = dist_a1_i1 - pi;
if (dist_a1_i2 <= -c2) // <= -pi
{
dist_a1_i2 += c4; // += 2pi;
}
return dist_a1_i2;
}
template <typename CalcT>
static inline CalcT abs_distance(CalcT const& dist_a1_a2, CalcT const& dist_a1_i1)
{
if (dist_a1_i1 < CalcT(0))
return -dist_a1_i1;
else if (dist_a1_i1 > dist_a1_a2)
return dist_a1_i1 - dist_a1_a2;
else
return CalcT(0);
}
template <typename CalcT>
static inline bool is_potentially_crossing(CalcT const& dist_a1_a2, CalcT const& dist_a1_i1, // in
bool& is_on_a, bool& is_near_a1, bool& is_near_a2) // out
{
is_on_a = segment_ratio<CalcT>(dist_a1_i1, dist_a1_a2).on_segment();
is_near_a1 = is_near(dist_a1_i1);
is_near_a2 = is_near(dist_a1_a2 - dist_a1_i1);
return is_on_a || is_near_a1 || is_near_a2;
}
template <typename CalcT, typename P1, typename P2>
static inline bool is_endpoint_equal(CalcT const& dist,
P1 const& ai, P2 const& b1)
{
static CalcT const c0 = 0;
return is_near(dist) && (math::equals(dist, c0) || equals_point_point(ai, b1));
}
template <typename CalcT>
static inline bool is_near(CalcT const& dist)
{
CalcT const small_number = CalcT(std::is_same<CalcT, float>::value ? 0.0001 : 0.00000001);
return math::abs(dist) <= small_number;
}
template <typename ProjCoord1, typename ProjCoord2>
static inline int position_value(ProjCoord1 const& ca1,
ProjCoord2 const& cb1,
ProjCoord2 const& cb2)
{
// S1x 0 1 2 3 4
// S2 |---------->
return math::equals(ca1, cb1) ? 1
: math::equals(ca1, cb2) ? 3
: cb1 < cb2 ?
( ca1 < cb1 ? 0
: ca1 > cb2 ? 4
: 2 )
: ( ca1 > cb1 ? 0
: ca1 < cb2 ? 4
: 2 );
}
template <typename Point1, typename Point2>
static inline bool equals_point_point(Point1 const& point1, Point2 const& point2)
{
return strategy::within::spherical_point_point::apply(point1, point2);
}
};
struct spherical_segments_calc_policy
{
template <typename Point, typename Point3d>
static Point from_cart3d(Point3d const& point_3d)
{
return formula::cart3d_to_sph<Point>(point_3d);
}
template <typename Point3d, typename Point>
static Point3d to_cart3d(Point const& point)
{
return formula::sph_to_cart3d<Point3d>(point);
}
template <typename Point3d>
struct plane
{
typedef typename coordinate_type<Point3d>::type coord_t;
// not normalized
plane(Point3d const& p1, Point3d const& p2)
: normal(cross_product(p1, p2))
{}
int side_value(Point3d const& pt) const
{
return formula::sph_side_value(normal, pt);
}
static coord_t cos_angle_between(Point3d const& p1, Point3d const& p2)
{
return dot_product(p1, p2);
}
coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const
{
coord_t const c0 = 0;
is_forward = dot_product(normal, cross_product(p1, p2)) >= c0;
return dot_product(p1, p2);
}
Point3d normal;
};
template <typename Point3d>
static plane<Point3d> get_plane(Point3d const& p1, Point3d const& p2)
{
return plane<Point3d>(p1, p2);
}
template <typename Point3d>
static bool intersection_points(plane<Point3d> const& plane1,
plane<Point3d> const& plane2,
Point3d & ip1, Point3d & ip2)
{
typedef typename coordinate_type<Point3d>::type coord_t;
ip1 = cross_product(plane1.normal, plane2.normal);
// NOTE: the length should be greater than 0 at this point
// if the normals were not normalized and their dot product
// not checked before this function is called the length
// should be checked here (math::equals(len, c0))
coord_t const len = math::sqrt(dot_product(ip1, ip1));
geometry::detail::for_each_dimension<Point3d>([&](auto index)
{
coord_t const coord = get<index>(ip1) / len; // normalize
set<index>(ip1, coord);
set<index>(ip2, -coord);
});
return true;
}
};
template
<
typename CalculationType = void
>
struct spherical_segments
: ecef_segments
<
spherical_segments_calc_policy,
CalculationType
>
{};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
/*template <typename CalculationType>
struct default_strategy<spherical_polar_tag, CalculationType>
{
typedef spherical_segments<CalculationType> type;
};*/
template <typename CalculationType>
struct default_strategy<spherical_equatorial_tag, CalculationType>
{
typedef spherical_segments<CalculationType> type;
};
template <typename CalculationType>
struct default_strategy<geographic_tag, CalculationType>
{
// NOTE: Spherical strategy returns the same result as the geographic one
// representing segments as great elliptic arcs. If the elliptic arcs are
// not great elliptic arcs (the origin not in the center of the coordinate
// system) then there may be problems with consistency of the side and
// intersection strategies.
typedef spherical_segments<CalculationType> type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::intersection
namespace strategy
{
namespace within { namespace services
{
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
}} // within::services
namespace covered_by { namespace services
{
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef strategy::intersection::spherical_segments<> type;
};
}} // within::services
} // strategy
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_INTERSECTION_HPP
@@ -0,0 +1,118 @@
// Boost.Geometry
// Copyright (c) 2018-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// 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_STRATEGIES_SPHERICAL_LINE_INTERPOLATE_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_LINE_INTERPOLATE_HPP
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/formulas/interpolate_point_spherical.hpp>
#include <boost/geometry/srs/spheroid.hpp>
#include <boost/geometry/strategies/line_interpolate.hpp>
#include <boost/geometry/strategies/spherical/distance_haversine.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace line_interpolate
{
/*!
\brief Interpolate point on a spherical segment.
\ingroup strategies
\tparam CalculationType \tparam_calculation
\tparam DistanceStrategy The underlying point-point distance strategy
\qbk{
[heading See also]
\* [link geometry.reference.algorithms.line_interpolate.line_interpolate_4_with_strategy line_interpolate (with strategy)]
}
*/
template
<
typename CalculationType = void,
typename DistanceStrategy = distance::haversine<double, CalculationType>
>
class spherical
{
public:
typedef typename DistanceStrategy::radius_type radius_type;
spherical() = default;
explicit inline spherical(typename DistanceStrategy::radius_type const& r)
: m_strategy(r)
{}
inline spherical(DistanceStrategy const& s)
: m_strategy(s)
{}
template <typename Point, typename Fraction, typename Distance>
inline void apply(Point const& p0,
Point const& p1,
Fraction const& fraction,
Point & p,
Distance const&) const
{
typedef typename select_calculation_type_alt
<
CalculationType,
Point
>::type calc_t;
formula::interpolate_point_spherical<calc_t> formula;
calc_t angle01;
formula.compute_angle(p0, p1, angle01);
formula.compute_axis(p0, angle01);
calc_t a = angle01 * fraction;
formula.compute_point(a, p);
}
inline radius_type radius() const
{
return m_strategy.radius();
}
private :
DistanceStrategy m_strategy;
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <>
struct default_strategy<spherical_equatorial_tag>
{
typedef strategy::line_interpolate::spherical<> type;
};
} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
}} // namespace strategy::line_interpolate
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_LINE_INTERPOLATE_HPP
+179
View File
@@ -0,0 +1,179 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
// Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland
// This file was modified by Oracle on 2013-2020.
// Modifications copyright (c) 2013-2020, 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
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_STRATEGY_SPHERICAL_POINT_IN_POINT_HPP
#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POINT_HPP
#include <cstddef>
#include <type_traits>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/algorithms/detail/normalize.hpp>
#include <boost/geometry/algorithms/dispatch/disjoint.hpp>
#include <boost/geometry/algorithms/transform.hpp>
#include <boost/geometry/geometries/helper_geometry.hpp>
#include <boost/geometry/strategies/cartesian/point_in_point.hpp>
#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/strategy_transform.hpp>
#include <boost/geometry/strategies/within.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace within
{
class point_point_on_spheroid
{
public:
typedef spherical_tag cs_tag;
private:
template <typename Point1, typename Point2, bool SameUnits>
struct are_same_points
{
static inline bool apply(Point1 const& point1, Point2 const& point2)
{
typedef typename helper_geometry<Point1>::type helper_point_type1;
typedef typename helper_geometry<Point2>::type helper_point_type2;
helper_point_type1 point1_normalized;
strategy::normalize::spherical_point::apply(point1, point1_normalized);
helper_point_type2 point2_normalized;
strategy::normalize::spherical_point::apply(point2, point2_normalized);
return point_point_generic
<
0, dimension<Point1>::value
>::apply(point1_normalized, point2_normalized);
}
};
template <typename Point1, typename Point2>
struct are_same_points<Point1, Point2, false> // points have different units
{
static inline bool apply(Point1 const& point1, Point2 const& point2)
{
typedef typename geometry::select_most_precise
<
typename fp_coordinate_type<Point1>::type,
typename fp_coordinate_type<Point2>::type
>::type calculation_type;
typename helper_geometry
<
Point1, calculation_type, radian
>::type helper_point1, helper_point2;
Point1 point1_normalized;
strategy::normalize::spherical_point::apply(point1, point1_normalized);
Point2 point2_normalized;
strategy::normalize::spherical_point::apply(point2, point2_normalized);
geometry::transform(point1_normalized, helper_point1);
geometry::transform(point2_normalized, helper_point2);
return point_point_generic
<
0, dimension<Point1>::value
>::apply(helper_point1, helper_point2);
}
};
public:
template <typename Point1, typename Point2>
static inline bool apply(Point1 const& point1, Point2 const& point2)
{
return are_same_points
<
Point1,
Point2,
std::is_same
<
typename detail::cs_angular_units<Point1>::type,
typename detail::cs_angular_units<Point2>::type
>::value
>::apply(point1, point2);
}
};
}} // namespace detail::within
#endif // DOXYGEN_NO_DETAIL
namespace strategy { namespace within
{
struct spherical_point_point
: geometry::detail::within::point_point_on_spheroid
{};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename PointLike1, typename PointLike2, typename Tag1, typename Tag2>
struct default_strategy<PointLike1, PointLike2, Tag1, Tag2, pointlike_tag, pointlike_tag, spherical_tag, spherical_tag>
{
typedef strategy::within::spherical_point_point type;
};
} // namespace services
#endif
}} // namespace strategy::within
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace strategy { namespace covered_by { namespace services
{
template <typename PointLike1, typename PointLike2, typename Tag1, typename Tag2>
struct default_strategy<PointLike1, PointLike2, Tag1, Tag2, pointlike_tag, pointlike_tag, spherical_tag, spherical_tag>
{
typedef strategy::within::spherical_point_point type;
};
}}} // namespace strategy::covered_by::services
#endif
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POINT_HPP
@@ -0,0 +1,593 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2013-2017 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2013-2023.
// Modifications copyright (c) 2013-2023 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/strategy/spherical/expand_point.hpp>
#include <boost/geometry/strategies/cartesian/point_in_box.hpp>
#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
#include <boost/geometry/strategies/spherical/ssf.hpp>
#include <boost/geometry/strategies/within.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace within
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename SideStrategy, typename CalculationType>
class spherical_winding_base
{
template <typename Point, typename PointOfSegment>
struct calculation_type
: select_calculation_type
<
Point,
PointOfSegment,
CalculationType
>
{};
/*! subclass to keep state */
class counter
{
int m_count;
//int m_count_n;
int m_count_s;
int m_raw_count;
int m_raw_count_anti;
bool m_touches;
inline int code() const
{
if (m_touches)
{
return 0;
}
if (m_raw_count != 0 && m_raw_count_anti != 0)
{
if (m_raw_count > 0) // right, wrap around south pole
{
return (m_count + m_count_s) == 0 ? -1 : 1;
}
else // left, wrap around north pole
{
//return (m_count + m_count_n) == 0 ? -1 : 1;
// m_count_n is 0
return m_count == 0 ? -1 : 1;
}
}
return m_count == 0 ? -1 : 1;
}
public :
friend class spherical_winding_base;
inline counter()
: m_count(0)
//, m_count_n(0)
, m_count_s(0)
, m_raw_count(0)
, m_raw_count_anti(0)
, m_touches(false)
{}
};
struct count_info
{
explicit count_info(int c = 0, bool ia = false)
: count(c)
, is_anti(ia)
{}
int count;
bool is_anti;
};
public:
typedef typename SideStrategy::cs_tag cs_tag;
spherical_winding_base() = default;
template <typename Model>
explicit spherical_winding_base(Model const& model)
: m_side_strategy(model)
{}
// Typedefs and static methods to fulfill the concept
typedef counter state_type;
template <typename Point, typename PointOfSegment>
inline bool apply(Point const& point,
PointOfSegment const& s1, PointOfSegment const& s2,
counter& state) const
{
typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
bool eq1 = false;
bool eq2 = false;
bool s_antipodal = false;
count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal);
if (ci.count != 0)
{
if (! ci.is_anti)
{
int side = 0;
if (ci.count == 1 || ci.count == -1)
{
side = side_equal(point, eq1 ? s1 : s2, ci);
}
else // count == 2 || count == -2
{
if (! s_antipodal)
{
// 1 left, -1 right
side = m_side_strategy.apply(s1, s2, point);
}
else
{
calc_t const pi = constants::half_period();
calc_t const s1_lat = get<1>(s1);
calc_t const s2_lat = get<1>(s2);
side = math::sign(ci.count)
* (pi - s1_lat - s2_lat <= pi // segment goes through north pole
? -1 // going right all points will be on right side
: 1); // going right all points will be on left side
}
}
if (side == 0)
{
// Point is lying on segment
state.m_touches = true;
state.m_count = 0;
return false;
}
// Side is NEG for right, POS for left.
// The count is -2 for left, 2 for right (or -1/1)
// Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE
// See accompagnying figure (TODO)
if (side * ci.count > 0)
{
state.m_count += ci.count;
}
state.m_raw_count += ci.count;
}
else
{
// Count negated because the segment is on the other side of the globe
// so it is reversed to match this side of the globe
// Assuming geometry wraps around north pole, for segments on the other side of the globe
// the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0
//state.m_count_n -= 0;
// Assuming geometry wraps around south pole, for segments on the other side of the globe
// the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0
state.m_count_s -= ci.count;
state.m_raw_count_anti -= ci.count;
}
}
return ! state.m_touches;
}
static inline int result(counter const& state)
{
return state.code();
}
protected:
template <typename Point, typename PointOfSegment>
static inline count_info check_segment(Point const& point,
PointOfSegment const& seg1,
PointOfSegment const& seg2,
counter& state,
bool& eq1, bool& eq2, bool& s_antipodal)
{
if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal))
{
return count_info(0, false);
}
return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal);
}
template <typename Point, typename PointOfSegment>
static inline int check_touch(Point const& point,
PointOfSegment const& seg1,
PointOfSegment const& seg2,
counter& state,
bool& eq1,
bool& eq2,
bool& s_antipodal)
{
typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
calc_t const c0 = 0;
calc_t const c2 = 2;
calc_t const pi = constants::half_period();
calc_t const half_pi = pi / c2;
calc_t const p_lon = get<0>(point);
calc_t const s1_lon = get<0>(seg1);
calc_t const s2_lon = get<0>(seg2);
calc_t const p_lat = get<1>(point);
calc_t const s1_lat = get<1>(seg1);
calc_t const s2_lat = get<1>(seg2);
// NOTE: lat in {-90, 90} and arbitrary lon
// it doesn't matter what lon it is if it's a pole
// so e.g. if one of the segment endpoints is a pole
// then only the other lon matters
bool eq1_strict = longitudes_equal<units_t>(s1_lon, p_lon);
bool eq2_strict = longitudes_equal<units_t>(s2_lon, p_lon);
bool eq1_anti = false;
bool eq2_anti = false;
calc_t const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi);
eq1 = eq1_strict // lon strictly equal to s1
|| (eq1_anti = longitudes_equal<units_t>(s1_lon, anti_p_lon)); // anti-lon strictly equal to s1
eq2 = eq2_strict // lon strictly equal to s2
|| (eq2_anti = longitudes_equal<units_t>(s2_lon, anti_p_lon)); // anti-lon strictly equal to s2
// segment overlapping pole
calc_t const s_lon_diff = math::longitude_distance_signed<units_t>(s1_lon, s2_lon);
s_antipodal = math::equals(s_lon_diff, pi);
if (s_antipodal)
{
eq1 = eq2 = eq1 || eq2;
// segment overlapping pole and point is pole
if (math::equals(math::abs(p_lat), half_pi))
{
eq1 = eq2 = true;
}
}
// check whether point is on a segment with a pole endpoint
if (math::longitude_distance_signed<units_t>(s2_lon, p_lon) == c0)
{
bool const s1_north = math::equals(get<1>(seg1), half_pi);
bool const s1_south = math::equals(get<1>(seg1), -half_pi);
if (s1_north || s1_south)
{
state.m_touches = s1_south ? s2_lat > p_lat : s2_lat < p_lat;
return state.m_touches;
}
}
if (math::longitude_distance_signed<units_t>(s1_lon, p_lon) == c0)
{
bool const s2_north = math::equals(get<1>(seg2), half_pi);
bool const s2_south = math::equals(get<1>(seg2), -half_pi);
if (s2_north || s2_south)
{
state.m_touches = s2_south ? s1_lat > p_lat : s1_lat < p_lat;
return state.m_touches;
}
}
// Both equal p -> segment vertical
// The only thing which has to be done is check if point is ON segment
if (eq1 && eq2)
{
// segment endpoints on the same sides of the globe
if (! s_antipodal)
{
// p's lat between segment endpoints' lats
if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) )
{
if (!eq1_anti || !eq2_anti)
{
state.m_touches = true;
}
}
}
else
{
// going through north or south pole?
if (pi - s1_lat - s2_lat <= pi)
{
if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north
|| math::equals(p_lat, half_pi) ) // point on north pole
{
state.m_touches = true;
}
else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole
{
return false;
}
}
else // south pole
{
if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south
|| math::equals(p_lat, -half_pi) ) // point on south pole
{
state.m_touches = true;
}
else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole
{
return false;
}
}
}
return true;
}
return false;
}
// Called if point is not aligned with a vertical segment
template <typename Point, typename PointOfSegment>
static inline count_info calculate_count(Point const& point,
PointOfSegment const& seg1,
PointOfSegment const& seg2,
bool eq1, bool eq2, bool s_antipodal)
{
typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
// If both segment endpoints were poles below checks wouldn't be enough
// but this means that either both are the same or that they are N/S poles
// and therefore the segment is not valid.
// If needed (eq1 && eq2 ? 0) could be returned
calc_t const c0 = 0;
calc_t const c2 = 2;
calc_t const pi = constants::half_period();
calc_t const half_pi = pi / c2;
bool const s1_is_pole = math::equals(std::abs(get<1>(seg1)), half_pi);
bool const s2_is_pole = math::equals(std::abs(get<1>(seg2)), half_pi);
if (s1_is_pole && s2_is_pole)
{
return count_info(0, false);
}
calc_t const p = get<0>(point);
calc_t const s1 = get<0>(seg1);
calc_t const s2 = get<0>(seg2);
calc_t const s1_p = math::longitude_distance_signed<units_t>(s1, p);
if (s_antipodal)
{
return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E
}
calc_t const s1_s2 = math::longitude_distance_signed<units_t>(s1, s2);
if (eq1 || eq2) // Point on level s1 or s2
{
return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E
longitudes_equal<units_t>(p + pi, (eq1 ? s1 : s2)));
}
// Point between s1 and s2
if ( math::sign(s1_p) == math::sign(s1_s2)
&& math::abs(s1_p) < math::abs(s1_s2) )
{
return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E
}
calc_t const s1_p_anti = math::longitude_distance_signed<units_t>(s1, p + pi);
// Anti-Point between s1 and s2
if ( math::sign(s1_p_anti) == math::sign(s1_s2)
&& math::abs(s1_p_anti) < math::abs(s1_s2) )
{
return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E
}
return count_info(0, false);
}
// Fix for https://svn.boost.org/trac/boost/ticket/9628
// For floating point coordinates, the <D> coordinate of a point is compared
// with the segment's points using some EPS. If the coordinates are "equal"
// the sides are calculated. Therefore we can treat a segment as a long areal
// geometry having some width. There is a small ~triangular area somewhere
// between the segment's effective area and a segment's line used in sides
// calculation where the segment is on the one side of the line but on the
// other side of a segment (due to the width).
// Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP.
// For the s1 of a segment going NE the real side is RIGHT but the point may
// be detected as LEFT, like this:
// RIGHT
// ___----->
// ^ O Pt __ __
// EPS __ __
// v__ __ BUT DETECTED AS LEFT OF THIS LINE
// _____7
// _____/
// _____/
// In the code below actually D = 0, so segments are nearly-vertical
// Called when the point is on the same level as one of the segment's points
// but the point is not aligned with a vertical segment
template <typename Point, typename PointOfSegment>
inline int side_equal(Point const& point,
PointOfSegment const& se,
count_info const& ci) const
{
typedef typename coordinate_type<PointOfSegment>::type scoord_t;
typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
if (math::equals(get<1>(point), get<1>(se)))
{
return 0;
}
// Create a horizontal segment intersecting the original segment's endpoint
// equal to the point, with the derived direction (E/W).
PointOfSegment ss1, ss2;
set<1>(ss1, get<1>(se));
set<0>(ss1, get<0>(se));
set<1>(ss2, get<1>(se));
scoord_t ss20 = get<0>(se);
if (ci.count > 0)
{
ss20 += small_angle<scoord_t, units_t>();
}
else
{
ss20 -= small_angle<scoord_t, units_t>();
}
math::normalize_longitude<units_t>(ss20);
set<0>(ss2, ss20);
// Check the side using this vertical segment
return m_side_strategy.apply(ss1, ss2, point);
}
// 1 deg or pi/180 rad
template <typename CalcT, typename Units>
static inline CalcT small_angle()
{
typedef math::detail::constants_on_spheroid<CalcT, Units> constants;
return constants::half_period() / CalcT(180);
}
template <typename Units, typename CalcT>
static inline bool longitudes_equal(CalcT const& lon1, CalcT const& lon2)
{
return math::equals(
math::longitude_distance_signed<Units>(lon1, lon2),
CalcT(0));
}
SideStrategy m_side_strategy;
};
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
/*!
\brief Within detection using winding rule in spherical coordinate system.
\ingroup strategies
\tparam Point \tparam_point
\tparam PointOfSegment \tparam_segment_point
\tparam CalculationType \tparam_calculation
\qbk{
[heading See also]
[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)]
}
*/
template
<
typename Point = void, // for backward compatibility
typename PointOfSegment = Point, // for backward compatibility
typename CalculationType = void
>
class spherical_winding
: public within::detail::spherical_winding_base
<
side::spherical_side_formula<CalculationType>,
CalculationType
>
{};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef within::spherical_winding<> type;
};
template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef within::spherical_winding<> type;
};
} // namespace services
#endif
}} // namespace strategy::within
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace strategy { namespace covered_by { namespace services
{
template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
{
typedef within::spherical_winding<> type;
};
template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
{
typedef within::spherical_winding<> type;
};
}}} // namespace strategy::covered_by::services
#endif
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
+146
View File
@@ -0,0 +1,146 @@
// Boost.Geometry
// Copyright (c) 2019-2020, 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_STRATEGIES_SPHERICAL_POINT_ORDER_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP
//#include <type_traits>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/strategy/spherical/area.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/point_order.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace point_order
{
//template <typename CalculationType = void>
//struct spherical
//{
// typedef azimuth_tag version_tag;
//
// template <typename Geometry>
// struct result_type
// {
// typedef typename geometry::select_calculation_type_alt
// <
// CalculationType, Geometry
// >::type type;
// };
//
// template <typename Point>
// inline bool apply(Point const& p1, Point const& p2,
// typename result_type<Point>::type & azi,
// typename result_type<Point>::type & razi) const
// {
// typedef typename result_type<Point>::type calc_t;
//
// if (equals_point_point(p1, p2))
// {
// return false;
// }
//
// calc_t lon1 = geometry::get_as_radian<0>(p1);
// calc_t lat1 = geometry::get_as_radian<1>(p1);
// calc_t lon2 = geometry::get_as_radian<0>(p2);
// calc_t lat2 = geometry::get_as_radian<1>(p2);
//
// convert_latitudes<Point>(lat1, lat2);
//
// formula::result_spherical<calc_t>
// res = formula::spherical_azimuth<calc_t, true>(lon1, lat1, lon2, lat2);
//
// azi = res.azimuth;
// razi = res.reverse_azimuth;
//
// return true;
// }
//
// template <typename Point>
// inline typename result_type<Point>::type
// apply(Point const& /*p0*/, Point const& /*p1*/, Point const& /*p2*/,
// typename result_type<Point>::type const& azi1,
// typename result_type<Point>::type const& azi2) const
// {
// // TODO: support poles
// return math::longitude_distance_signed<radian>(azi1, azi2);
// }
//
//private:
// template <typename Point>
// static bool equals_point_point(Point const& p0, Point const& p1)
// {
// return strategy::within::spherical_point_point::apply(p0, p1);
// }
//
// template <typename Point, typename CalcT>
// static void convert_latitudes(CalcT & lat1, CalcT & lat2)
// {
// static const bool is_polar = std::is_same
// <
// typename geometry::cs_tag<Point>::type,
// spherical_polar_tag
// >::value;
//
// if (BOOST_GEOMETRY_CONDITION(is_polar))
// {
// CalcT pi_half = math::half_pi<CalcT>();
// lat1 = pi_half - lat1;
// lat2 = pi_half - lat2;
// }
// }
//};
template <typename CalculationType = void>
struct spherical
: strategy::area::spherical<double, CalculationType>
{
typedef area_tag version_tag;
// TEMP
static strategy::area::spherical<double, CalculationType> get_area_strategy()
{
return strategy::area::spherical<double, CalculationType>();
}
};
namespace services
{
template <>
struct default_strategy<spherical_equatorial_tag>
{
typedef spherical<> type;
};
/*template <>
struct default_strategy<spherical_polar_tag>
{
typedef spherical<> type;
};*/
} // namespace services
}} // namespace strategy::point_order
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP
@@ -0,0 +1,98 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2014-2021.
// Modifications copyright (c) 2014-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SIDE_BY_CROSS_TRACK_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SIDE_BY_CROSS_TRACK_HPP
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/formulas/spherical.hpp>
//#include <boost/geometry/strategies/concepts/side_concept.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace side
{
/*!
\brief Check at which side of a Great Circle segment a point lies
left of segment (> 0), right of segment (< 0), on segment (0)
\ingroup strategies
\tparam CalculationType \tparam_calculation
*/
template <typename CalculationType = void>
class side_by_cross_track
{
public :
template <typename P1, typename P2, typename P>
static inline int apply(P1 const& p1, P2 const& p2, P const& p)
{
typedef strategy::within::spherical_point_point
equals_point_point_strategy_type;
if (equals_point_point_strategy_type::apply(p, p1)
|| equals_point_point_strategy_type::apply(p, p2)
|| equals_point_point_strategy_type::apply(p1, p2))
{
return 0;
}
typedef typename promote_floating_point
<
typename select_calculation_type_alt
<
CalculationType,
P1, P2, P
>::type
>::type calc_t;
calc_t d1 = 0.001; // m_strategy.apply(sp1, p);
calc_t lon1 = geometry::get_as_radian<0>(p1);
calc_t lat1 = geometry::get_as_radian<1>(p1);
calc_t lon2 = geometry::get_as_radian<0>(p2);
calc_t lat2 = geometry::get_as_radian<1>(p2);
calc_t lon = geometry::get_as_radian<0>(p);
calc_t lat = geometry::get_as_radian<1>(p);
calc_t crs_AD = geometry::formula::spherical_azimuth<calc_t, false>
(lon1, lat1, lon, lat).azimuth;
calc_t crs_AB = geometry::formula::spherical_azimuth<calc_t, false>
(lon1, lat1, lon2, lat2).azimuth;
calc_t XTD = asin(sin(d1) * sin(crs_AD - crs_AB));
return math::equals(XTD, 0) ? 0 : XTD < 0 ? 1 : -1;
}
};
}} // namespace strategy::side
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SIDE_BY_CROSS_TRACK_HPP
+147
View File
@@ -0,0 +1,147 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2016-2021.
// Modifications copyright (c) 2016-2021, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, 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
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SSF_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SSF_HPP
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_promotion.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>
#include <boost/geometry/strategy/spherical/envelope.hpp>
#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/spherical/disjoint_segment_box.hpp>
//#include <boost/geometry/strategies/concepts/side_concept.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
namespace boost { namespace geometry
{
namespace strategy { namespace side
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename T>
int spherical_side_formula(T const& lambda1, T const& delta1,
T const& lambda2, T const& delta2,
T const& lambda, T const& delta)
{
// Create temporary points (vectors) on unit a sphere
T const cos_delta1 = cos(delta1);
T const c1x = cos_delta1 * cos(lambda1);
T const c1y = cos_delta1 * sin(lambda1);
T const c1z = sin(delta1);
T const cos_delta2 = cos(delta2);
T const c2x = cos_delta2 * cos(lambda2);
T const c2y = cos_delta2 * sin(lambda2);
T const c2z = sin(delta2);
// (Third point is converted directly)
T const cos_delta = cos(delta);
// Apply the "Spherical Side Formula" as presented on my blog
T const dist
= (c1y * c2z - c1z * c2y) * cos_delta * cos(lambda)
+ (c1z * c2x - c1x * c2z) * cos_delta * sin(lambda)
+ (c1x * c2y - c1y * c2x) * sin(delta);
T zero = T();
return math::equals(dist, zero) ? 0
: dist > zero ? 1
: -1; // dist < zero
}
}
#endif // DOXYGEN_NO_DETAIL
/*!
\brief Check at which side of a Great Circle segment a point lies
left of segment (> 0), right of segment (< 0), on segment (0)
\ingroup strategies
\tparam CalculationType \tparam_calculation
*/
template <typename CalculationType = void>
class spherical_side_formula
{
public :
typedef spherical_tag cs_tag;
template <typename P1, typename P2, typename P>
static inline int apply(P1 const& p1, P2 const& p2, P const& p)
{
typedef typename promote_floating_point
<
typename select_calculation_type_alt
<
CalculationType,
P1, P2, P
>::type
>::type calculation_type;
calculation_type const lambda1 = get_as_radian<0>(p1);
calculation_type const delta1 = get_as_radian<1>(p1);
calculation_type const lambda2 = get_as_radian<0>(p2);
calculation_type const delta2 = get_as_radian<1>(p2);
calculation_type const lambda = get_as_radian<0>(p);
calculation_type const delta = get_as_radian<1>(p);
return detail::spherical_side_formula(lambda1, delta1,
lambda2, delta2,
lambda, delta);
}
};
#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{
/*template <typename CalculationType>
struct default_strategy<spherical_polar_tag, CalculationType>
{
typedef spherical_side_formula<CalculationType> type;
};*/
template <typename CalculationType>
struct default_strategy<spherical_equatorial_tag, CalculationType>
{
typedef spherical_side_formula<CalculationType> type;
};
template <typename CalculationType>
struct default_strategy<geographic_tag, CalculationType>
{
typedef spherical_side_formula<CalculationType> type;
};
}
#endif
}} // namespace strategy::side
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_SSF_HPP