3 // Copyright (c) 2016, Oracle and/or its affiliates.
4 // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
5 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
7 // Use, modification and distribution is subject to the Boost Software License,
8 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
12 #define BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
14 #include <boost/geometry/core/coordinate_system.hpp>
15 #include <boost/geometry/core/coordinate_type.hpp>
16 #include <boost/geometry/core/access.hpp>
17 #include <boost/geometry/core/radian_access.hpp>
19 //#include <boost/geometry/arithmetic/arithmetic.hpp>
20 #include <boost/geometry/arithmetic/cross_product.hpp>
21 #include <boost/geometry/arithmetic/dot_product.hpp>
23 #include <boost/geometry/util/math.hpp>
24 #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
25 #include <boost/geometry/util/select_coordinate_type.hpp>
27 namespace boost { namespace geometry {
32 struct result_spherical
44 static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z)
46 T const cos_lat = cos(lat);
47 x = cos_lat * cos(lon);
48 y = cos_lat * sin(lon);
52 template <typename Point3d, typename PointSph>
53 static inline Point3d sph_to_cart3d(PointSph const& point_sph)
55 typedef typename coordinate_type<Point3d>::type calc_t;
57 calc_t const lon = get_as_radian<0>(point_sph);
58 calc_t const lat = get_as_radian<1>(point_sph);
60 sph_to_cart3d(lon, lat, x, y, z);
71 static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat)
77 template <typename PointSph, typename Point3d>
78 static inline PointSph cart3d_to_sph(Point3d const& point_3d)
80 typedef typename coordinate_type<PointSph>::type coord_t;
81 typedef typename coordinate_type<Point3d>::type calc_t;
83 calc_t const x = get<0>(point_3d);
84 calc_t const y = get<1>(point_3d);
85 calc_t const z = get<2>(point_3d);
87 cart3d_to_sph(x, y, z, lonr, latr);
90 set_from_radian<0>(res, lonr);
91 set_from_radian<1>(res, latr);
93 coord_t lon = get<0>(res);
94 coord_t lat = get<1>(res);
96 math::normalize_spheroidal_coordinates
98 typename coordinate_system<PointSph>::type::units,
111 template <typename Point3d1, typename Point3d2>
112 static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt)
114 typedef typename select_coordinate_type<Point3d1, Point3d2>::type calc_t;
116 calc_t d = dot_product(norm, pt);
117 return math::equals(d, c0) ? 0
122 template <typename CT, bool ReverseAzimuth, typename T1, typename T2>
123 static inline result_spherical<CT> spherical_azimuth(T1 const& lon1,
128 typedef result_spherical<CT> result_type;
131 // http://williams.best.vwh.net/avform.htm#Crs
132 // https://en.wikipedia.org/wiki/Great-circle_navigation
133 CT dlon = lon2 - lon1;
135 // An optimization which should kick in often for Boxes
136 //if ( math::equals(dlon, ReturnType(0)) )
137 //if ( get<0>(p1) == get<0>(p2) )
139 // return - sin(get_as_radian<1>(p1)) * cos_p2lat);
142 CT const cos_dlon = cos(dlon);
143 CT const sin_dlon = sin(dlon);
144 CT const cos_lat1 = cos(lat1);
145 CT const cos_lat2 = cos(lat2);
146 CT const sin_lat1 = sin(lat1);
147 CT const sin_lat2 = sin(lat2);
150 // "An alternative formula, not requiring the pre-computation of d"
151 // In the formula below dlon is used as "d"
152 CT const y = sin_dlon * cos_lat2;
153 CT const x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon;
154 result.azimuth = atan2(y, x);
159 CT const y = sin_dlon * cos_lat1;
160 CT const x = sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1;
161 result.reverse_azimuth = atan2(y, x);
167 template <typename ReturnType, typename T1, typename T2>
168 inline ReturnType spherical_azimuth(T1 const& lon1, T1 const& lat1,
169 T2 const& lon2, T2 const& lat2)
171 return spherical_azimuth<ReturnType, false>(lon1, lat1, lon2, lat2).azimuth;
174 template <typename T>
175 inline T spherical_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2)
177 return spherical_azimuth<T, false>(lon1, lat1, lon2, lat2).azimuth;
180 template <typename T>
181 inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2)
183 T const pi = math::pi<T>();
184 T const two_pi = math::two_pi<T>();
186 // instead of the formula from XTD
187 //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2));
189 T a_diff = azi_a1_p - azi_a1_a2;
190 // normalize, angle in [-pi, pi]
196 // NOTE: in general it shouldn't be required to support the pi/-pi case
197 // because in non-cartesian systems it makes sense to check the side
198 // only "between" the endpoints.
199 // However currently the winding strategy calls the side strategy
200 // for vertical segments to check if the point is "between the endpoints.
201 // This could be avoided since the side strategy is not required for that
202 // because meridian is the shortest path. So a difference of
203 // longitudes would be sufficient (of course normalized to [-pi, pi]).
205 // NOTE: with the above said, the pi/-pi check is temporary
206 // however in case if this was required
207 // the geodesics on ellipsoid aren't "symmetrical"
208 // therefore instead of comparing a_diff to pi and -pi
209 // one should probably use inverse azimuths and compare
210 // the difference to 0 as well
212 // positive azimuth is on the right side
213 return math::equals(a_diff, 0)
214 || math::equals(a_diff, pi)
215 || math::equals(a_diff, -pi) ? 0
216 : a_diff > 0 ? -1 // right
220 } // namespace formula
222 }} // namespace boost::geometry
224 #endif // BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP