spherical.hpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // Boost.Geometry
  2. // Copyright (c) 2016-2020, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Use, modification and distribution is subject to the Boost Software License,
  6. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. #ifndef BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
  9. #define BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
  10. #include <boost/geometry/core/coordinate_system.hpp>
  11. #include <boost/geometry/core/coordinate_type.hpp>
  12. #include <boost/geometry/core/cs.hpp>
  13. #include <boost/geometry/core/access.hpp>
  14. #include <boost/geometry/core/radian_access.hpp>
  15. #include <boost/geometry/core/radius.hpp>
  16. //#include <boost/geometry/arithmetic/arithmetic.hpp>
  17. #include <boost/geometry/arithmetic/cross_product.hpp>
  18. #include <boost/geometry/arithmetic/dot_product.hpp>
  19. #include <boost/geometry/util/math.hpp>
  20. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  21. #include <boost/geometry/util/select_coordinate_type.hpp>
  22. #include <boost/geometry/formulas/result_direct.hpp>
  23. namespace boost { namespace geometry {
  24. namespace formula {
  25. template <typename T>
  26. struct result_spherical
  27. {
  28. result_spherical()
  29. : azimuth(0)
  30. , reverse_azimuth(0)
  31. {}
  32. T azimuth;
  33. T reverse_azimuth;
  34. };
  35. template <typename T>
  36. static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z)
  37. {
  38. T const cos_lat = cos(lat);
  39. x = cos_lat * cos(lon);
  40. y = cos_lat * sin(lon);
  41. z = sin(lat);
  42. }
  43. template <typename Point3d, typename PointSph>
  44. static inline Point3d sph_to_cart3d(PointSph const& point_sph)
  45. {
  46. typedef typename coordinate_type<Point3d>::type calc_t;
  47. calc_t const lon = get_as_radian<0>(point_sph);
  48. calc_t const lat = get_as_radian<1>(point_sph);
  49. calc_t x, y, z;
  50. sph_to_cart3d(lon, lat, x, y, z);
  51. Point3d res;
  52. set<0>(res, x);
  53. set<1>(res, y);
  54. set<2>(res, z);
  55. return res;
  56. }
  57. template <typename T>
  58. static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat)
  59. {
  60. lon = atan2(y, x);
  61. lat = asin(z);
  62. }
  63. template <typename PointSph, typename Point3d>
  64. static inline PointSph cart3d_to_sph(Point3d const& point_3d)
  65. {
  66. typedef typename coordinate_type<PointSph>::type coord_t;
  67. typedef typename coordinate_type<Point3d>::type calc_t;
  68. calc_t const x = get<0>(point_3d);
  69. calc_t const y = get<1>(point_3d);
  70. calc_t const z = get<2>(point_3d);
  71. calc_t lonr, latr;
  72. cart3d_to_sph(x, y, z, lonr, latr);
  73. PointSph res;
  74. set_from_radian<0>(res, lonr);
  75. set_from_radian<1>(res, latr);
  76. coord_t lon = get<0>(res);
  77. coord_t lat = get<1>(res);
  78. math::normalize_spheroidal_coordinates
  79. <
  80. typename geometry::detail::cs_angular_units<PointSph>::type,
  81. coord_t
  82. >(lon, lat);
  83. set<0>(res, lon);
  84. set<1>(res, lat);
  85. return res;
  86. }
  87. // -1 right
  88. // 1 left
  89. // 0 on
  90. template <typename Point3d1, typename Point3d2>
  91. static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt)
  92. {
  93. typedef typename select_coordinate_type<Point3d1, Point3d2>::type calc_t;
  94. calc_t c0 = 0;
  95. calc_t d = dot_product(norm, pt);
  96. return math::equals(d, c0) ? 0
  97. : d > c0 ? 1
  98. : -1; // d < 0
  99. }
  100. template <typename CT, bool ReverseAzimuth, typename T1, typename T2>
  101. static inline result_spherical<CT> spherical_azimuth(T1 const& lon1,
  102. T1 const& lat1,
  103. T2 const& lon2,
  104. T2 const& lat2)
  105. {
  106. typedef result_spherical<CT> result_type;
  107. result_type result;
  108. // http://williams.best.vwh.net/avform.htm#Crs
  109. // https://en.wikipedia.org/wiki/Great-circle_navigation
  110. CT dlon = lon2 - lon1;
  111. // An optimization which should kick in often for Boxes
  112. //if ( math::equals(dlon, ReturnType(0)) )
  113. //if ( get<0>(p1) == get<0>(p2) )
  114. //{
  115. // return - sin(get_as_radian<1>(p1)) * cos_p2lat);
  116. //}
  117. CT const cos_dlon = cos(dlon);
  118. CT const sin_dlon = sin(dlon);
  119. CT const cos_lat1 = cos(lat1);
  120. CT const cos_lat2 = cos(lat2);
  121. CT const sin_lat1 = sin(lat1);
  122. CT const sin_lat2 = sin(lat2);
  123. {
  124. // "An alternative formula, not requiring the pre-computation of d"
  125. // In the formula below dlon is used as "d"
  126. CT const y = sin_dlon * cos_lat2;
  127. CT const x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon;
  128. result.azimuth = atan2(y, x);
  129. }
  130. if (ReverseAzimuth)
  131. {
  132. CT const y = sin_dlon * cos_lat1;
  133. CT const x = sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1;
  134. result.reverse_azimuth = atan2(y, x);
  135. }
  136. return result;
  137. }
  138. template <typename ReturnType, typename T1, typename T2>
  139. inline ReturnType spherical_azimuth(T1 const& lon1, T1 const& lat1,
  140. T2 const& lon2, T2 const& lat2)
  141. {
  142. return spherical_azimuth<ReturnType, false>(lon1, lat1, lon2, lat2).azimuth;
  143. }
  144. template <typename T>
  145. inline T spherical_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2)
  146. {
  147. return spherical_azimuth<T, false>(lon1, lat1, lon2, lat2).azimuth;
  148. }
  149. template <typename T>
  150. inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2)
  151. {
  152. T const c0 = 0;
  153. T const pi = math::pi<T>();
  154. // instead of the formula from XTD
  155. //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2));
  156. T a_diff = azi_a1_p - azi_a1_a2;
  157. // normalize, angle in (-pi, pi]
  158. math::detail::normalize_angle_loop<radian>(a_diff);
  159. // NOTE: in general it shouldn't be required to support the pi/-pi case
  160. // because in non-cartesian systems it makes sense to check the side
  161. // only "between" the endpoints.
  162. // However currently the winding strategy calls the side strategy
  163. // for vertical segments to check if the point is "between the endpoints.
  164. // This could be avoided since the side strategy is not required for that
  165. // because meridian is the shortest path. So a difference of
  166. // longitudes would be sufficient (of course normalized to (-pi, pi]).
  167. // NOTE: with the above said, the pi/-pi check is temporary
  168. // however in case if this was required
  169. // the geodesics on ellipsoid aren't "symmetrical"
  170. // therefore instead of comparing a_diff to pi and -pi
  171. // one should probably use inverse azimuths and compare
  172. // the difference to 0 as well
  173. // positive azimuth is on the right side
  174. return math::equals(a_diff, c0)
  175. || math::equals(a_diff, pi)
  176. || math::equals(a_diff, -pi) ? 0
  177. : a_diff > 0 ? -1 // right
  178. : 1; // left
  179. }
  180. template
  181. <
  182. bool Coordinates,
  183. bool ReverseAzimuth,
  184. typename CT,
  185. typename Sphere
  186. >
  187. inline result_direct<CT> spherical_direct(CT const& lon1,
  188. CT const& lat1,
  189. CT const& sig12,
  190. CT const& alp1,
  191. Sphere const& sphere)
  192. {
  193. result_direct<CT> result;
  194. CT const sin_alp1 = sin(alp1);
  195. CT const sin_lat1 = sin(lat1);
  196. CT const cos_alp1 = cos(alp1);
  197. CT const cos_lat1 = cos(lat1);
  198. CT const norm = math::sqrt(cos_alp1 * cos_alp1 + sin_alp1 * sin_alp1
  199. * sin_lat1 * sin_lat1);
  200. CT const alp0 = atan2(sin_alp1 * cos_lat1, norm);
  201. CT const sig1 = atan2(sin_lat1, cos_alp1 * cos_lat1);
  202. CT const sig2 = sig1 + sig12 / get_radius<0>(sphere);
  203. CT const cos_sig2 = cos(sig2);
  204. CT const sin_alp0 = sin(alp0);
  205. CT const cos_alp0 = cos(alp0);
  206. if (Coordinates)
  207. {
  208. CT const sin_sig2 = sin(sig2);
  209. CT const sin_sig1 = sin(sig1);
  210. CT const cos_sig1 = cos(sig1);
  211. CT const norm2 = math::sqrt(cos_alp0 * cos_alp0 * cos_sig2 * cos_sig2
  212. + sin_alp0 * sin_alp0);
  213. CT const lat2 = atan2(cos_alp0 * sin_sig2, norm2);
  214. CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1);
  215. CT const lon2 = atan2(sin_alp0 * sin_sig2, cos_sig2);
  216. result.lon2 = lon1 + lon2 - omg1;
  217. result.lat2 = lat2;
  218. // For longitudes close to the antimeridian the result can be out
  219. // of range. Therefore normalize.
  220. math::detail::normalize_angle_cond<radian>(result.lon2);
  221. }
  222. if (ReverseAzimuth)
  223. {
  224. CT const alp2 = atan2(sin_alp0, cos_alp0 * cos_sig2);
  225. result.reverse_azimuth = alp2;
  226. }
  227. return result;
  228. }
  229. } // namespace formula
  230. }} // namespace boost::geometry
  231. #endif // BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP