geographic.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Boost.Geometry
  2. // Copyright (c) 2016-2021, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  4. // Use, modification and distribution is subject to the Boost Software License,
  5. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP
  8. #define BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP
  9. #include <boost/geometry/core/coordinate_system.hpp>
  10. #include <boost/geometry/core/coordinate_type.hpp>
  11. #include <boost/geometry/core/access.hpp>
  12. #include <boost/geometry/core/radian_access.hpp>
  13. #include <boost/geometry/arithmetic/arithmetic.hpp>
  14. #include <boost/geometry/arithmetic/cross_product.hpp>
  15. #include <boost/geometry/arithmetic/dot_product.hpp>
  16. #include <boost/geometry/arithmetic/normalize.hpp>
  17. #include <boost/geometry/formulas/eccentricity_sqr.hpp>
  18. #include <boost/geometry/formulas/flattening.hpp>
  19. #include <boost/geometry/formulas/unit_spheroid.hpp>
  20. #include <boost/geometry/util/math.hpp>
  21. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  22. #include <boost/geometry/util/select_coordinate_type.hpp>
  23. namespace boost { namespace geometry {
  24. namespace formula {
  25. template <typename Point3d, typename PointGeo, typename Spheroid>
  26. inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid)
  27. {
  28. typedef typename coordinate_type<Point3d>::type calc_t;
  29. calc_t const c1 = 1;
  30. calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid);
  31. calc_t const lon = get_as_radian<0>(point_geo);
  32. calc_t const lat = get_as_radian<1>(point_geo);
  33. Point3d res;
  34. calc_t const sin_lat = sin(lat);
  35. // "unit" spheroid, a = 1
  36. calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat));
  37. calc_t const N_cos_lat = N * cos(lat);
  38. set<0>(res, N_cos_lat * cos(lon));
  39. set<1>(res, N_cos_lat * sin(lon));
  40. set<2>(res, N * (c1 - e_sqr) * sin_lat);
  41. return res;
  42. }
  43. template <typename PointGeo, typename Spheroid, typename Point3d>
  44. inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid)
  45. {
  46. typedef typename coordinate_type<Point3d>::type calc_t;
  47. calc_t const c1 = 1;
  48. calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid);
  49. calc_t const lon = get_as_radian<0>(point_geo);
  50. calc_t const lat = get_as_radian<1>(point_geo);
  51. calc_t const sin_lon = sin(lon);
  52. calc_t const cos_lon = cos(lon);
  53. calc_t const sin_lat = sin(lat);
  54. calc_t const cos_lat = cos(lat);
  55. // "unit" spheroid, a = 1
  56. calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat));
  57. calc_t const N_cos_lat = N * cos_lat;
  58. set<0>(result, N_cos_lat * cos_lon);
  59. set<1>(result, N_cos_lat * sin_lon);
  60. set<2>(result, N * (c1 - e_sqr) * sin_lat);
  61. set<0>(east, -sin_lon);
  62. set<1>(east, cos_lon);
  63. set<2>(east, 0);
  64. set<0>(north, -sin_lat * cos_lon);
  65. set<1>(north, -sin_lat * sin_lon);
  66. set<2>(north, cos_lat);
  67. }
  68. template <typename PointGeo, typename Point3d, typename Spheroid>
  69. inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid)
  70. {
  71. typedef typename coordinate_type<PointGeo>::type coord_t;
  72. typedef typename coordinate_type<Point3d>::type calc_t;
  73. calc_t const c1 = 1;
  74. //calc_t const c2 = 2;
  75. calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid);
  76. calc_t const x = get<0>(point_3d);
  77. calc_t const y = get<1>(point_3d);
  78. calc_t const z = get<2>(point_3d);
  79. calc_t const xy_l = math::sqrt(math::sqr(x) + math::sqr(y));
  80. calc_t const lonr = atan2(y, x);
  81. // NOTE: Alternative version
  82. // http://www.iag-aig.org/attach/989c8e501d9c5b5e2736955baf2632f5/V60N2_5FT.pdf
  83. // calc_t const lonr = c2 * atan2(y, x + xy_l);
  84. calc_t const latr = atan2(z, (c1 - e_sqr) * xy_l);
  85. // NOTE: If h is equal to 0 then there is no need to improve value of latitude
  86. // because then N_i / (N_i + h_i) = 1
  87. // http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion
  88. PointGeo res;
  89. set_from_radian<0>(res, lonr);
  90. set_from_radian<1>(res, latr);
  91. coord_t lon = get<0>(res);
  92. coord_t lat = get<1>(res);
  93. math::normalize_spheroidal_coordinates
  94. <
  95. typename coordinate_system<PointGeo>::type::units,
  96. coord_t
  97. >(lon, lat);
  98. set<0>(res, lon);
  99. set<1>(res, lat);
  100. return res;
  101. }
  102. template <typename Point3d, typename Spheroid>
  103. inline Point3d projected_to_xy(Point3d const& point_3d, Spheroid const& spheroid)
  104. {
  105. typedef typename coordinate_type<Point3d>::type coord_t;
  106. // len_xy = sqrt(x^2 + y^2)
  107. // r = len_xy - |z / tan(lat)|
  108. // assuming h = 0
  109. // lat = atan2(z, (1 - e^2) * len_xy);
  110. // |z / tan(lat)| = (1 - e^2) * len_xy
  111. // r = e^2 * len_xy
  112. // x_res = r * cos(lon) = e^2 * len_xy * x / len_xy = e^2 * x
  113. // y_res = r * sin(lon) = e^2 * len_xy * y / len_xy = e^2 * y
  114. coord_t const c0 = 0;
  115. coord_t const e_sqr = formula::eccentricity_sqr<coord_t>(spheroid);
  116. Point3d res;
  117. set<0>(res, e_sqr * get<0>(point_3d));
  118. set<1>(res, e_sqr * get<1>(point_3d));
  119. set<2>(res, c0);
  120. return res;
  121. }
  122. template <typename Point3d, typename Spheroid>
  123. inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& spheroid)
  124. {
  125. typedef typename coordinate_type<Point3d>::type coord_t;
  126. //coord_t const c0 = 0;
  127. coord_t const c2 = 2;
  128. coord_t const c4 = 4;
  129. // calculate the point of intersection of a ray and spheroid's surface
  130. // the origin is the origin of the coordinate system
  131. //(x*x+y*y)/(a*a) + z*z/(b*b) = 1
  132. // x = d.x * t
  133. // y = d.y * t
  134. // z = d.z * t
  135. coord_t const dx = get<0>(direction);
  136. coord_t const dy = get<1>(direction);
  137. coord_t const dz = get<2>(direction);
  138. //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid));
  139. //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid));
  140. // "unit" spheroid, a = 1
  141. coord_t const a_sqr = 1;
  142. coord_t const b_sqr = math::sqr(formula::unit_spheroid_b<coord_t>(spheroid));
  143. coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr;
  144. coord_t const delta = c4 * param_a;
  145. // delta >= 0
  146. coord_t const t = math::sqrt(delta) / (c2 * param_a);
  147. // result = direction * t
  148. Point3d result = direction;
  149. multiply_value(result, t);
  150. return result;
  151. }
  152. template <typename Point3d, typename Spheroid>
  153. inline bool projected_to_surface(Point3d const& origin, Point3d const& direction,
  154. Point3d & result1, Point3d & result2,
  155. Spheroid const& spheroid)
  156. {
  157. typedef typename coordinate_type<Point3d>::type coord_t;
  158. coord_t const c0 = 0;
  159. coord_t const c1 = 1;
  160. coord_t const c2 = 2;
  161. coord_t const c4 = 4;
  162. // calculate the point of intersection of a ray and spheroid's surface
  163. //(x*x+y*y)/(a*a) + z*z/(b*b) = 1
  164. // x = o.x + d.x * t
  165. // y = o.y + d.y * t
  166. // z = o.z + d.z * t
  167. coord_t const ox = get<0>(origin);
  168. coord_t const oy = get<1>(origin);
  169. coord_t const oz = get<2>(origin);
  170. coord_t const dx = get<0>(direction);
  171. coord_t const dy = get<1>(direction);
  172. coord_t const dz = get<2>(direction);
  173. //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid));
  174. //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid));
  175. // "unit" spheroid, a = 1
  176. coord_t const a_sqr = 1;
  177. coord_t const b_sqr = math::sqr(formula::unit_spheroid_b<coord_t>(spheroid));
  178. coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr;
  179. coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr);
  180. coord_t const param_c = (ox*ox + oy*oy) / a_sqr + oz*oz / b_sqr - c1;
  181. coord_t const delta = math::sqr(param_b) - c4 * param_a*param_c;
  182. // equals() ?
  183. if (delta < c0 || param_a == 0)
  184. {
  185. return false;
  186. }
  187. // result = origin + direction * t
  188. coord_t const sqrt_delta = math::sqrt(delta);
  189. coord_t const two_a = c2 * param_a;
  190. coord_t const t1 = (-param_b + sqrt_delta) / two_a;
  191. coord_t const t2 = (-param_b - sqrt_delta) / two_a;
  192. geometry::detail::for_each_dimension<Point3d>([&](auto index)
  193. {
  194. set<index>(result1, get<index>(origin) + get<index>(direction) * t1);
  195. set<index>(result2, get<index>(origin) + get<index>(direction) * t2);
  196. });
  197. return true;
  198. }
  199. template <typename Point3d, typename Spheroid>
  200. inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2,
  201. Point3d const& b1, Point3d const& b2,
  202. Point3d & result,
  203. Spheroid const& spheroid)
  204. {
  205. typedef typename coordinate_type<Point3d>::type coord_t;
  206. coord_t c0 = 0;
  207. coord_t c1 = 1;
  208. Point3d n1 = cross_product(a1, a2);
  209. Point3d n2 = cross_product(b1, b2);
  210. // intersection direction
  211. Point3d id = cross_product(n1, n2);
  212. coord_t id_len_sqr = dot_product(id, id);
  213. if (math::equals(id_len_sqr, c0))
  214. {
  215. return false;
  216. }
  217. // no need to normalize a1 and a2 because the intersection point on
  218. // the opposite side of the globe is at the same distance from the origin
  219. coord_t cos_a1i = dot_product(a1, id);
  220. coord_t cos_a2i = dot_product(a2, id);
  221. coord_t gri = math::detail::greatest(cos_a1i, cos_a2i);
  222. Point3d neg_id = id;
  223. multiply_value(neg_id, -c1);
  224. coord_t cos_a1ni = dot_product(a1, neg_id);
  225. coord_t cos_a2ni = dot_product(a2, neg_id);
  226. coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni);
  227. if (gri >= grni)
  228. {
  229. result = projected_to_surface(id, spheroid);
  230. }
  231. else
  232. {
  233. result = projected_to_surface(neg_id, spheroid);
  234. }
  235. return true;
  236. }
  237. template <typename Point3d1, typename Point3d2>
  238. static inline int elliptic_side_value(Point3d1 const& origin, Point3d1 const& norm, Point3d2 const& pt)
  239. {
  240. typedef typename coordinate_type<Point3d1>::type calc_t;
  241. calc_t c0 = 0;
  242. // vector oposite to pt - origin
  243. // only for the purpose of assigning origin
  244. Point3d1 vec = origin;
  245. subtract_point(vec, pt);
  246. calc_t d = dot_product(norm, vec);
  247. // since the vector is opposite the signs are opposite
  248. return math::equals(d, c0) ? 0
  249. : d < c0 ? 1
  250. : -1; // d > 0
  251. }
  252. template <typename Point3d, typename Spheroid>
  253. inline bool planes_spheroid_intersection(Point3d const& o1, Point3d const& n1,
  254. Point3d const& o2, Point3d const& n2,
  255. Point3d & ip1, Point3d & ip2,
  256. Spheroid const& spheroid)
  257. {
  258. typedef typename coordinate_type<Point3d>::type coord_t;
  259. coord_t c0 = 0;
  260. coord_t c1 = 1;
  261. // Below
  262. // n . (p - o) = 0
  263. // n . p - n . o = 0
  264. // n . p + d = 0
  265. // n . p = h
  266. // intersection direction
  267. Point3d id = cross_product(n1, n2);
  268. if (math::equals(dot_product(id, id), c0))
  269. {
  270. return false;
  271. }
  272. coord_t dot_n1_n2 = dot_product(n1, n2);
  273. coord_t dot_n1_n2_sqr = math::sqr(dot_n1_n2);
  274. coord_t h1 = dot_product(n1, o1);
  275. coord_t h2 = dot_product(n2, o2);
  276. coord_t denom = c1 - dot_n1_n2_sqr;
  277. coord_t C1 = (h1 - h2 * dot_n1_n2) / denom;
  278. coord_t C2 = (h2 - h1 * dot_n1_n2) / denom;
  279. // C1 * n1 + C2 * n2
  280. Point3d io;
  281. geometry::detail::for_each_dimension<Point3d>([&](auto index)
  282. {
  283. set<index>(io, C1 * get<index>(n1) + C2 * get<index>(n2));
  284. });
  285. if (! projected_to_surface(io, id, ip1, ip2, spheroid))
  286. {
  287. return false;
  288. }
  289. return true;
  290. }
  291. template <typename Point3d, typename Spheroid>
  292. inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2,
  293. Point3d & v1, Point3d & v2,
  294. Point3d & origin, Point3d & normal,
  295. Spheroid const& spheroid)
  296. {
  297. typedef typename coordinate_type<Point3d>::type coord_t;
  298. Point3d xy1 = projected_to_xy(p1, spheroid);
  299. Point3d xy2 = projected_to_xy(p2, spheroid);
  300. // origin = (xy1 + xy2) / 2
  301. // v1 = p1 - origin
  302. // v2 = p2 - origin
  303. coord_t const half = coord_t(0.5);
  304. geometry::detail::for_each_dimension<Point3d>([&](auto index)
  305. {
  306. coord_t const o = (get<index>(xy1) + get<index>(xy2)) * half;
  307. set<index>(origin, o);
  308. set<index>(v1, get<index>(p1) - o);
  309. set<index>(v2, get<index>(p1) - o);
  310. });
  311. normal = cross_product(v1, v2);
  312. }
  313. template <typename Point3d, typename Spheroid>
  314. inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2,
  315. Point3d & origin, Point3d & normal,
  316. Spheroid const& spheroid)
  317. {
  318. Point3d v1, v2;
  319. experimental_elliptic_plane(p1, p2, v1, v2, origin, normal, spheroid);
  320. }
  321. template <typename Point3d, typename Spheroid>
  322. inline bool experimental_elliptic_intersection(Point3d const& a1, Point3d const& a2,
  323. Point3d const& b1, Point3d const& b2,
  324. Point3d & result,
  325. Spheroid const& spheroid)
  326. {
  327. typedef typename coordinate_type<Point3d>::type coord_t;
  328. coord_t c0 = 0;
  329. coord_t c1 = 1;
  330. Point3d a1v, a2v, o1, n1;
  331. experimental_elliptic_plane(a1, a2, a1v, a2v, o1, n1, spheroid);
  332. Point3d b1v, b2v, o2, n2;
  333. experimental_elliptic_plane(b1, b2, b1v, b2v, o2, n2, spheroid);
  334. if (! geometry::detail::vec_normalize(n1) || ! geometry::detail::vec_normalize(n2))
  335. {
  336. return false;
  337. }
  338. Point3d ip1_s, ip2_s;
  339. if (! planes_spheroid_intersection(o1, n1, o2, n2, ip1_s, ip2_s, spheroid))
  340. {
  341. return false;
  342. }
  343. // NOTE: simplified test, may not work in all cases
  344. coord_t dot_a1i1 = dot_product(a1, ip1_s);
  345. coord_t dot_a2i1 = dot_product(a2, ip1_s);
  346. coord_t gri1 = math::detail::greatest(dot_a1i1, dot_a2i1);
  347. coord_t dot_a1i2 = dot_product(a1, ip2_s);
  348. coord_t dot_a2i2 = dot_product(a2, ip2_s);
  349. coord_t gri2 = math::detail::greatest(dot_a1i2, dot_a2i2);
  350. result = gri1 >= gri2 ? ip1_s : ip2_s;
  351. return true;
  352. }
  353. } // namespace formula
  354. }} // namespace boost::geometry
  355. #endif // BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP