summaryrefslogtreecommitdiff
path: root/boost/geometry/formulas/spherical.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/formulas/spherical.hpp')
-rw-r--r--boost/geometry/formulas/spherical.hpp152
1 files changed, 140 insertions, 12 deletions
diff --git a/boost/geometry/formulas/spherical.hpp b/boost/geometry/formulas/spherical.hpp
index 2195bbbe10..ff24c51a88 100644
--- a/boost/geometry/formulas/spherical.hpp
+++ b/boost/geometry/formulas/spherical.hpp
@@ -1,6 +1,7 @@
// Boost.Geometry
// Copyright (c) 2016, 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,
@@ -27,38 +28,67 @@ namespace boost { namespace geometry {
namespace formula {
+template <typename T>
+struct result_spherical
+{
+ result_spherical()
+ : azimuth(0)
+ , reverse_azimuth(0)
+ {}
+
+ T azimuth;
+ T reverse_azimuth;
+};
+
+template <typename T>
+static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z)
+{
+ T const cos_lat = cos(lat);
+ x = cos_lat * cos(lon);
+ y = cos_lat * sin(lon);
+ z = sin(lat);
+}
+
template <typename Point3d, typename PointSph>
static inline Point3d sph_to_cart3d(PointSph const& point_sph)
{
typedef typename coordinate_type<Point3d>::type calc_t;
- Point3d res;
-
- calc_t lon = get_as_radian<0>(point_sph);
- calc_t lat = get_as_radian<1>(point_sph);
+ calc_t const lon = get_as_radian<0>(point_sph);
+ calc_t const lat = get_as_radian<1>(point_sph);
+ calc_t x, y, z;
+ sph_to_cart3d(lon, lat, x, y, z);
- calc_t const cos_lat = cos(lat);
- set<0>(res, cos_lat * cos(lon));
- set<1>(res, cos_lat * sin(lon));
- set<2>(res, sin(lat));
+ Point3d res;
+ set<0>(res, x);
+ set<1>(res, y);
+ set<2>(res, z);
return res;
}
+template <typename T>
+static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat)
+{
+ lon = atan2(y, x);
+ lat = asin(z);
+}
+
template <typename PointSph, typename Point3d>
static inline PointSph cart3d_to_sph(Point3d const& point_3d)
{
typedef typename coordinate_type<PointSph>::type coord_t;
typedef typename coordinate_type<Point3d>::type calc_t;
- PointSph res;
-
calc_t const x = get<0>(point_3d);
calc_t const y = get<1>(point_3d);
calc_t const z = get<2>(point_3d);
+ calc_t lonr, latr;
+ cart3d_to_sph(x, y, z, lonr, latr);
- set_from_radian<0>(res, atan2(y, x));
- set_from_radian<1>(res, asin(z));
+ PointSph res;
+ set_from_radian<0>(res, lonr);
+ set_from_radian<1>(res, latr);
coord_t lon = get<0>(res);
coord_t lat = get<1>(res);
@@ -89,6 +119,104 @@ static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt)
: -1; // d < 0
}
+template <typename CT, bool ReverseAzimuth, typename T1, typename T2>
+static inline result_spherical<CT> spherical_azimuth(T1 const& lon1,
+ T1 const& lat1,
+ T2 const& lon2,
+ T2 const& lat2)
+{
+ typedef result_spherical<CT> result_type;
+ result_type result;
+
+ // http://williams.best.vwh.net/avform.htm#Crs
+ // https://en.wikipedia.org/wiki/Great-circle_navigation
+ CT dlon = lon2 - lon1;
+
+ // An optimization which should kick in often for Boxes
+ //if ( math::equals(dlon, ReturnType(0)) )
+ //if ( get<0>(p1) == get<0>(p2) )
+ //{
+ // return - sin(get_as_radian<1>(p1)) * cos_p2lat);
+ //}
+
+ CT const cos_dlon = cos(dlon);
+ CT const sin_dlon = sin(dlon);
+ CT const cos_lat1 = cos(lat1);
+ CT const cos_lat2 = cos(lat2);
+ CT const sin_lat1 = sin(lat1);
+ CT const sin_lat2 = sin(lat2);
+
+ {
+ // "An alternative formula, not requiring the pre-computation of d"
+ // In the formula below dlon is used as "d"
+ CT const y = sin_dlon * cos_lat2;
+ CT const x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon;
+ result.azimuth = atan2(y, x);
+ }
+
+ if (ReverseAzimuth)
+ {
+ CT const y = sin_dlon * cos_lat1;
+ CT const x = sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1;
+ result.reverse_azimuth = atan2(y, x);
+ }
+
+ return result;
+}
+
+template <typename ReturnType, typename T1, typename T2>
+inline ReturnType spherical_azimuth(T1 const& lon1, T1 const& lat1,
+ T2 const& lon2, T2 const& lat2)
+{
+ return spherical_azimuth<ReturnType, false>(lon1, lat1, lon2, lat2).azimuth;
+}
+
+template <typename T>
+inline T spherical_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2)
+{
+ return spherical_azimuth<T, false>(lon1, lat1, lon2, lat2).azimuth;
+}
+
+template <typename T>
+inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2)
+{
+ T const pi = math::pi<T>();
+ T const two_pi = math::two_pi<T>();
+
+ // instead of the formula from XTD
+ //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2));
+
+ T a_diff = azi_a1_p - azi_a1_a2;
+ // normalize, angle in [-pi, pi]
+ while (a_diff > pi)
+ a_diff -= two_pi;
+ while (a_diff < -pi)
+ a_diff += two_pi;
+
+ // NOTE: in general it shouldn't be required to support the pi/-pi case
+ // because in non-cartesian systems it makes sense to check the side
+ // only "between" the endpoints.
+ // However currently the winding strategy calls the side strategy
+ // for vertical segments to check if the point is "between the endpoints.
+ // This could be avoided since the side strategy is not required for that
+ // because meridian is the shortest path. So a difference of
+ // longitudes would be sufficient (of course normalized to [-pi, pi]).
+
+ // NOTE: with the above said, the pi/-pi check is temporary
+ // however in case if this was required
+ // the geodesics on ellipsoid aren't "symmetrical"
+ // therefore instead of comparing a_diff to pi and -pi
+ // one should probably use inverse azimuths and compare
+ // the difference to 0 as well
+
+ // positive azimuth is on the right side
+ return math::equals(a_diff, 0)
+ || math::equals(a_diff, pi)
+ || math::equals(a_diff, -pi) ? 0
+ : a_diff > 0 ? -1 // right
+ : 1; // left
+}
+
} // namespace formula
}} // namespace boost::geometry