spherical.hpp 8.4 KB

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