vertex_longitude.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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_MAXIMUM_LONGITUDE_HPP
  9. #define BOOST_GEOMETRY_FORMULAS_MAXIMUM_LONGITUDE_HPP
  10. #include <boost/geometry/core/static_assert.hpp>
  11. #include <boost/geometry/formulas/spherical.hpp>
  12. #include <boost/geometry/formulas/flattening.hpp>
  13. #include <boost/math/special_functions/hypot.hpp>
  14. namespace boost { namespace geometry { namespace formula
  15. {
  16. /*!
  17. \brief Algorithm to compute the vertex longitude of a geodesic segment. Vertex is
  18. a point on the geodesic that maximizes (or minimizes) the latitude. The algorithm
  19. is given the vertex latitude.
  20. */
  21. //Classes for spesific CS
  22. template <typename CT>
  23. class vertex_longitude_on_sphere
  24. {
  25. public:
  26. template <typename T>
  27. static inline CT apply(T const& lat1, //segment point 1
  28. T const& lat2, //segment point 2
  29. T const& lat3, //vertex latitude
  30. T const& sin_l12,
  31. T const& cos_l12) //lon1 -lon2
  32. {
  33. //https://en.wikipedia.org/wiki/Great-circle_navigation#Finding_way-points
  34. CT const A = sin(lat1) * cos(lat2) * cos(lat3) * sin_l12;
  35. CT const B = sin(lat1) * cos(lat2) * cos(lat3) * cos_l12
  36. - cos(lat1) * sin(lat2) * cos(lat3);
  37. CT lon = atan2(B, A);
  38. return lon + math::pi<CT>();
  39. }
  40. };
  41. template <typename CT>
  42. class vertex_longitude_on_spheroid
  43. {
  44. template<typename T>
  45. static inline void normalize(T& x, T& y)
  46. {
  47. T h = boost::math::hypot(x, y);
  48. x /= h;
  49. y /= h;
  50. }
  51. public:
  52. template <typename T, typename Spheroid>
  53. static inline CT apply(T const& lat1, //segment point 1
  54. T const& lat2, //segment point 2
  55. T const& lat3, //vertex latitude
  56. T& alp1,
  57. Spheroid const& spheroid)
  58. {
  59. // We assume that segment points lay on different side w.r.t.
  60. // the vertex
  61. // Constants
  62. CT const c0 = 0;
  63. CT const c2 = 2;
  64. CT const half_pi = math::pi<CT>() / c2;
  65. if (math::equals(lat1, half_pi)
  66. || math::equals(lat2, half_pi)
  67. || math::equals(lat1, -half_pi)
  68. || math::equals(lat2, -half_pi))
  69. {
  70. // one segment point is the pole
  71. return c0;
  72. }
  73. // More constants
  74. CT const f = flattening<CT>(spheroid);
  75. CT const pi = math::pi<CT>();
  76. CT const c1 = 1;
  77. CT const cminus1 = -1;
  78. // First, compute longitude on auxiliary sphere
  79. CT const one_minus_f = c1 - f;
  80. CT const bet1 = atan(one_minus_f * tan(lat1));
  81. CT const bet2 = atan(one_minus_f * tan(lat2));
  82. CT const bet3 = atan(one_minus_f * tan(lat3));
  83. CT cos_bet1 = cos(bet1);
  84. CT cos_bet2 = cos(bet2);
  85. CT const sin_bet1 = sin(bet1);
  86. CT const sin_bet2 = sin(bet2);
  87. CT const sin_bet3 = sin(bet3);
  88. CT omg12 = 0;
  89. if (bet1 < c0)
  90. {
  91. cos_bet1 *= cminus1;
  92. omg12 += pi;
  93. }
  94. if (bet2 < c0)
  95. {
  96. cos_bet2 *= cminus1;
  97. omg12 += pi;
  98. }
  99. CT const sin_alp1 = sin(alp1);
  100. CT const cos_alp1 = math::sqrt(c1 - math::sqr(sin_alp1));
  101. CT const norm = math::sqrt(math::sqr(cos_alp1) + math::sqr(sin_alp1 * sin_bet1));
  102. CT const sin_alp0 = sin(atan2(sin_alp1 * cos_bet1, norm));
  103. BOOST_ASSERT(cos_bet2 != c0);
  104. CT const sin_alp2 = sin_alp1 * cos_bet1 / cos_bet2;
  105. CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
  106. CT const cos_alp2 = math::sqrt(c1 - math::sqr(sin_alp2));
  107. CT const sig1 = atan2(sin_bet1, cos_alp1 * cos_bet1);
  108. CT const sig2 = atan2(sin_bet2, -cos_alp2 * cos_bet2); //lat3 is a vertex
  109. CT const cos_sig1 = cos(sig1);
  110. CT const sin_sig1 = math::sqrt(c1 - math::sqr(cos_sig1));
  111. CT const cos_sig2 = cos(sig2);
  112. CT const sin_sig2 = math::sqrt(c1 - math::sqr(cos_sig2));
  113. CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1);
  114. CT const omg2 = atan2(sin_alp0 * sin_sig2, cos_sig2);
  115. omg12 += omg1 - omg2;
  116. CT const sin_omg12 = sin(omg12);
  117. CT const cos_omg12 = cos(omg12);
  118. CT omg13 = geometry::formula::vertex_longitude_on_sphere<CT>
  119. ::apply(bet1, bet2, bet3, sin_omg12, cos_omg12);
  120. if (lat1 * lat2 < c0)//different hemispheres
  121. {
  122. if ((lat2 - lat1) * lat3 > c0)// ascending segment
  123. {
  124. omg13 = pi - omg13;
  125. }
  126. }
  127. // Second, compute the ellipsoidal longitude
  128. CT const e2 = f * (c2 - f);
  129. CT const ep = math::sqrt(e2 / (c1 - e2));
  130. CT const k2 = math::sqr(ep * cos_alp0);
  131. CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
  132. CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
  133. CT const eps2 = eps * eps;
  134. CT const n = f / (c2 - f);
  135. // sig3 is the length from equator to the vertex
  136. CT sig3;
  137. if(sin_bet3 > c0)
  138. {
  139. sig3 = half_pi;
  140. } else {
  141. sig3 = -half_pi;
  142. }
  143. CT const cos_sig3 = 0;
  144. CT const sin_sig3 = 1;
  145. CT sig13 = sig3 - sig1;
  146. if (sig13 > pi)
  147. {
  148. sig13 -= 2 * pi;
  149. }
  150. // Order 2 approximation
  151. CT const c1over2 = 0.5;
  152. CT const c1over4 = 0.25;
  153. CT const c1over8 = 0.125;
  154. CT const c1over16 = 0.0625;
  155. CT const c4 = 4;
  156. CT const c8 = 8;
  157. CT const A3 = 1 - (c1over2 - c1over2 * n) * eps - c1over4 * eps2;
  158. CT const C31 = (c1over4 - c1over4 * n) * eps + c1over8 * eps2;
  159. CT const C32 = c1over16 * eps2;
  160. CT const sin2_sig3 = c2 * cos_sig3 * sin_sig3;
  161. CT const sin4_sig3 = sin_sig3 * (-c4 * cos_sig3
  162. + c8 * cos_sig3 * cos_sig3 * cos_sig3);
  163. CT const sin2_sig1 = c2 * cos_sig1 * sin_sig1;
  164. CT const sin4_sig1 = sin_sig1 * (-c4 * cos_sig1
  165. + c8 * cos_sig1 * cos_sig1 * cos_sig1);
  166. CT const I3 = A3 * (sig13
  167. + C31 * (sin2_sig3 - sin2_sig1)
  168. + C32 * (sin4_sig3 - sin4_sig1));
  169. CT const sign = bet3 >= c0
  170. ? c1
  171. : cminus1;
  172. CT const dlon_max = omg13 - sign * f * sin_alp0 * I3;
  173. return dlon_max;
  174. }
  175. };
  176. //CS_tag dispatching
  177. template <typename CT, typename CS_Tag>
  178. struct compute_vertex_lon
  179. {
  180. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  181. "Not implemented for this coordinate system.",
  182. CT, CS_Tag);
  183. };
  184. template <typename CT>
  185. struct compute_vertex_lon<CT, spherical_equatorial_tag>
  186. {
  187. template <typename Strategy>
  188. static inline CT apply(CT const& lat1,
  189. CT const& lat2,
  190. CT const& vertex_lat,
  191. CT const& sin_l12,
  192. CT const& cos_l12,
  193. CT,
  194. Strategy)
  195. {
  196. return vertex_longitude_on_sphere<CT>
  197. ::apply(lat1,
  198. lat2,
  199. vertex_lat,
  200. sin_l12,
  201. cos_l12);
  202. }
  203. };
  204. template <typename CT>
  205. struct compute_vertex_lon<CT, geographic_tag>
  206. {
  207. template <typename Strategy>
  208. static inline CT apply(CT const& lat1,
  209. CT const& lat2,
  210. CT const& vertex_lat,
  211. CT,
  212. CT,
  213. CT& alp1,
  214. Strategy const& azimuth_strategy)
  215. {
  216. return vertex_longitude_on_spheroid<CT>
  217. ::apply(lat1,
  218. lat2,
  219. vertex_lat,
  220. alp1,
  221. azimuth_strategy.model());
  222. }
  223. };
  224. // Vertex longitude interface
  225. // Assume that lon1 < lon2 and vertex_lat is the latitude of the vertex
  226. template <typename CT, typename CS_Tag>
  227. class vertex_longitude
  228. {
  229. public :
  230. template <typename Strategy>
  231. static inline CT apply(CT& lon1,
  232. CT& lat1,
  233. CT& lon2,
  234. CT& lat2,
  235. CT const& vertex_lat,
  236. CT& alp1,
  237. Strategy const& azimuth_strategy)
  238. {
  239. CT const c0 = 0;
  240. CT pi = math::pi<CT>();
  241. //Vertex is a segment's point
  242. if (math::equals(vertex_lat, lat1))
  243. {
  244. return lon1;
  245. }
  246. if (math::equals(vertex_lat, lat2))
  247. {
  248. return lon2;
  249. }
  250. //Segment lay on meridian
  251. if (math::equals(lon1, lon2))
  252. {
  253. return (std::max)(lat1, lat2);
  254. }
  255. BOOST_ASSERT(lon1 < lon2);
  256. CT dlon = compute_vertex_lon<CT, CS_Tag>::apply(lat1, lat2,
  257. vertex_lat,
  258. sin(lon1 - lon2),
  259. cos(lon1 - lon2),
  260. alp1,
  261. azimuth_strategy);
  262. CT vertex_lon = std::fmod(lon1 + dlon, 2 * pi);
  263. if (vertex_lat < c0)
  264. {
  265. vertex_lon -= pi;
  266. }
  267. if (std::abs(lon1 - lon2) > pi)
  268. {
  269. vertex_lon -= pi;
  270. }
  271. return vertex_lon;
  272. }
  273. };
  274. template <typename CT>
  275. class vertex_longitude<CT, cartesian_tag>
  276. {
  277. public :
  278. template <typename Strategy>
  279. static inline CT apply(CT& /*lon1*/,
  280. CT& /*lat1*/,
  281. CT& lon2,
  282. CT& /*lat2*/,
  283. CT const& /*vertex_lat*/,
  284. CT& /*alp1*/,
  285. Strategy const& /*azimuth_strategy*/)
  286. {
  287. return lon2;
  288. }
  289. };
  290. }}} // namespace boost::geometry::formula
  291. #endif // BOOST_GEOMETRY_FORMULAS_MAXIMUM_LONGITUDE_HPP