123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- // Boost.Geometry
- // Copyright (c) 2016-2020, 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_FORMULAS_SPHERICAL_HPP
- #define BOOST_GEOMETRY_FORMULAS_SPHERICAL_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/access.hpp>
- #include <boost/geometry/core/radian_access.hpp>
- #include <boost/geometry/core/radius.hpp>
- //#include <boost/geometry/arithmetic/arithmetic.hpp>
- #include <boost/geometry/arithmetic/cross_product.hpp>
- #include <boost/geometry/arithmetic/dot_product.hpp>
- #include <boost/geometry/util/math.hpp>
- #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
- #include <boost/geometry/util/select_coordinate_type.hpp>
- #include <boost/geometry/formulas/result_direct.hpp>
- 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;
- 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);
- 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;
- 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);
- 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);
- math::normalize_spheroidal_coordinates
- <
- typename geometry::detail::cs_angular_units<PointSph>::type,
- coord_t
- >(lon, lat);
- set<0>(res, lon);
- set<1>(res, lat);
- return res;
- }
- // -1 right
- // 1 left
- // 0 on
- template <typename Point3d1, typename Point3d2>
- static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt)
- {
- typedef typename select_coordinate_type<Point3d1, Point3d2>::type calc_t;
- calc_t c0 = 0;
- calc_t d = dot_product(norm, pt);
- return math::equals(d, c0) ? 0
- : d > c0 ? 1
- : -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 c0 = 0;
- T const pi = math::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]
- math::detail::normalize_angle_loop<radian>(a_diff);
- // 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, c0)
- || math::equals(a_diff, pi)
- || math::equals(a_diff, -pi) ? 0
- : a_diff > 0 ? -1 // right
- : 1; // left
- }
- template
- <
- bool Coordinates,
- bool ReverseAzimuth,
- typename CT,
- typename Sphere
- >
- inline result_direct<CT> spherical_direct(CT const& lon1,
- CT const& lat1,
- CT const& sig12,
- CT const& alp1,
- Sphere const& sphere)
- {
- result_direct<CT> result;
- CT const sin_alp1 = sin(alp1);
- CT const sin_lat1 = sin(lat1);
- CT const cos_alp1 = cos(alp1);
- CT const cos_lat1 = cos(lat1);
- CT const norm = math::sqrt(cos_alp1 * cos_alp1 + sin_alp1 * sin_alp1
- * sin_lat1 * sin_lat1);
- CT const alp0 = atan2(sin_alp1 * cos_lat1, norm);
- CT const sig1 = atan2(sin_lat1, cos_alp1 * cos_lat1);
- CT const sig2 = sig1 + sig12 / get_radius<0>(sphere);
- CT const cos_sig2 = cos(sig2);
- CT const sin_alp0 = sin(alp0);
- CT const cos_alp0 = cos(alp0);
- if (Coordinates)
- {
- CT const sin_sig2 = sin(sig2);
- CT const sin_sig1 = sin(sig1);
- CT const cos_sig1 = cos(sig1);
- CT const norm2 = math::sqrt(cos_alp0 * cos_alp0 * cos_sig2 * cos_sig2
- + sin_alp0 * sin_alp0);
- CT const lat2 = atan2(cos_alp0 * sin_sig2, norm2);
- CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1);
- CT const lon2 = atan2(sin_alp0 * sin_sig2, cos_sig2);
- result.lon2 = lon1 + lon2 - omg1;
- result.lat2 = lat2;
- // For longitudes close to the antimeridian the result can be out
- // of range. Therefore normalize.
- math::detail::normalize_angle_cond<radian>(result.lon2);
- }
- if (ReverseAzimuth)
- {
- CT const alp2 = atan2(sin_alp0, cos_alp0 * cos_sig2);
- result.reverse_azimuth = alp2;
- }
- return result;
- }
- } // namespace formula
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
|