| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 | // Boost.Geometry// Copyright (c) 2016-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_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 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 ontemplate <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>();    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, 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;    }    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
 |