intersection.hpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. // Boost.Geometry
  2. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2016-2018, Oracle and/or its affiliates.
  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_STRATEGIES_SPHERICAL_INTERSECTION_HPP
  9. #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_INTERSECTION_HPP
  10. #include <algorithm>
  11. #include <boost/geometry/core/cs.hpp>
  12. #include <boost/geometry/core/access.hpp>
  13. #include <boost/geometry/core/radian_access.hpp>
  14. #include <boost/geometry/core/tags.hpp>
  15. #include <boost/geometry/algorithms/detail/assign_values.hpp>
  16. #include <boost/geometry/algorithms/detail/assign_indexed_point.hpp>
  17. #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
  18. #include <boost/geometry/algorithms/detail/recalculate.hpp>
  19. #include <boost/geometry/arithmetic/arithmetic.hpp>
  20. #include <boost/geometry/arithmetic/cross_product.hpp>
  21. #include <boost/geometry/arithmetic/dot_product.hpp>
  22. #include <boost/geometry/arithmetic/normalize.hpp>
  23. #include <boost/geometry/formulas/spherical.hpp>
  24. #include <boost/geometry/geometries/concepts/point_concept.hpp>
  25. #include <boost/geometry/geometries/concepts/segment_concept.hpp>
  26. #include <boost/geometry/policies/robustness/segment_ratio.hpp>
  27. #include <boost/geometry/strategies/covered_by.hpp>
  28. #include <boost/geometry/strategies/intersection.hpp>
  29. #include <boost/geometry/strategies/intersection_result.hpp>
  30. #include <boost/geometry/strategies/side.hpp>
  31. #include <boost/geometry/strategies/side_info.hpp>
  32. #include <boost/geometry/strategies/spherical/area.hpp>
  33. #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
  34. #include <boost/geometry/strategies/spherical/envelope_segment.hpp>
  35. #include <boost/geometry/strategies/spherical/point_in_poly_winding.hpp>
  36. #include <boost/geometry/strategies/spherical/ssf.hpp>
  37. #include <boost/geometry/strategies/within.hpp>
  38. #include <boost/geometry/util/math.hpp>
  39. #include <boost/geometry/util/select_calculation_type.hpp>
  40. namespace boost { namespace geometry
  41. {
  42. namespace strategy { namespace intersection
  43. {
  44. // NOTE:
  45. // The coordinates of crossing IP may be calculated with small precision in some cases.
  46. // For double, near the equator noticed error ~1e-9 so far greater than
  47. // machine epsilon which is ~1e-16. This error is ~0.04m.
  48. // E.g. consider two cases, one near the origin and the second one rotated by 90 deg around Z or SN axis.
  49. // After the conversion from spherical degrees to cartesian 3d the following coordinates
  50. // are calculated:
  51. // for sph (-1 -1, 1 1) deg cart3d ys are -0.017449748351250485 and 0.017449748351250485
  52. // for sph (89 -1, 91 1) deg cart3d xs are 0.017449748351250571 and -0.017449748351250450
  53. // During the conversion degrees must first be converted to radians and then radians
  54. // are passed into trigonometric functions. The error may have several causes:
  55. // 1. Radians cannot represent exactly the same angles as degrees.
  56. // 2. Different longitudes are passed into sin() for x, corresponding to cos() for y,
  57. // and for different angle the error of the result may be different.
  58. // 3. These non-corresponding cartesian coordinates are used in calculation,
  59. // e.g. multiplied several times in cross and dot products.
  60. // If it was a problem this strategy could e.g. "normalize" longitudes before the conversion using the source units
  61. // by rotating the globe around Z axis, so moving longitudes always the same way towards the origin,
  62. // assuming this could help which is not clear.
  63. // For now, intersection points near the endpoints are checked explicitly if needed (if the IP is near the endpoint)
  64. // to generate precise result for them. Only the crossing (i) case may suffer from lower precision.
  65. template
  66. <
  67. typename CalcPolicy,
  68. typename CalculationType = void
  69. >
  70. struct ecef_segments
  71. {
  72. typedef side::spherical_side_formula<CalculationType> side_strategy_type;
  73. static inline side_strategy_type get_side_strategy()
  74. {
  75. return side_strategy_type();
  76. }
  77. template <typename Geometry1, typename Geometry2>
  78. struct point_in_geometry_strategy
  79. {
  80. typedef strategy::within::spherical_winding
  81. <
  82. typename point_type<Geometry1>::type,
  83. typename point_type<Geometry2>::type,
  84. CalculationType
  85. > type;
  86. };
  87. template <typename Geometry1, typename Geometry2>
  88. static inline typename point_in_geometry_strategy<Geometry1, Geometry2>::type
  89. get_point_in_geometry_strategy()
  90. {
  91. typedef typename point_in_geometry_strategy
  92. <
  93. Geometry1, Geometry2
  94. >::type strategy_type;
  95. return strategy_type();
  96. }
  97. template <typename Geometry>
  98. struct area_strategy
  99. {
  100. typedef area::spherical
  101. <
  102. typename coordinate_type<Geometry>::type,
  103. CalculationType
  104. > type;
  105. };
  106. template <typename Geometry>
  107. static inline typename area_strategy<Geometry>::type get_area_strategy()
  108. {
  109. typedef typename area_strategy<Geometry>::type strategy_type;
  110. return strategy_type();
  111. }
  112. template <typename Geometry>
  113. struct distance_strategy
  114. {
  115. typedef distance::haversine
  116. <
  117. typename coordinate_type<Geometry>::type,
  118. CalculationType
  119. > type;
  120. };
  121. template <typename Geometry>
  122. static inline typename distance_strategy<Geometry>::type get_distance_strategy()
  123. {
  124. typedef typename distance_strategy<Geometry>::type strategy_type;
  125. return strategy_type();
  126. }
  127. typedef envelope::spherical_segment<CalculationType>
  128. envelope_strategy_type;
  129. static inline envelope_strategy_type get_envelope_strategy()
  130. {
  131. return envelope_strategy_type();
  132. }
  133. enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 };
  134. // segment_intersection_info cannot outlive relate_ecef_segments
  135. template <typename CoordinateType, typename SegmentRatio, typename Vector3d>
  136. struct segment_intersection_info
  137. {
  138. segment_intersection_info(CalcPolicy const& calc)
  139. : calc_policy(calc)
  140. {}
  141. template <typename Point, typename Segment1, typename Segment2>
  142. void calculate(Point& point, Segment1 const& a, Segment2 const& b) const
  143. {
  144. if (ip_flag == ipi_inters)
  145. {
  146. // TODO: assign the rest of coordinates
  147. point = calc_policy.template from_cart3d<Point>(intersection_point);
  148. }
  149. else if (ip_flag == ipi_at_a1)
  150. {
  151. detail::assign_point_from_index<0>(a, point);
  152. }
  153. else if (ip_flag == ipi_at_a2)
  154. {
  155. detail::assign_point_from_index<1>(a, point);
  156. }
  157. else if (ip_flag == ipi_at_b1)
  158. {
  159. detail::assign_point_from_index<0>(b, point);
  160. }
  161. else // ip_flag == ipi_at_b2
  162. {
  163. detail::assign_point_from_index<1>(b, point);
  164. }
  165. }
  166. Vector3d intersection_point;
  167. SegmentRatio robust_ra;
  168. SegmentRatio robust_rb;
  169. intersection_point_flag ip_flag;
  170. CalcPolicy const& calc_policy;
  171. };
  172. // Relate segments a and b
  173. template
  174. <
  175. typename Segment1,
  176. typename Segment2,
  177. typename Policy,
  178. typename RobustPolicy
  179. >
  180. static inline typename Policy::return_type
  181. apply(Segment1 const& a, Segment2 const& b,
  182. Policy const& policy, RobustPolicy const& robust_policy)
  183. {
  184. typedef typename point_type<Segment1>::type point1_t;
  185. typedef typename point_type<Segment2>::type point2_t;
  186. point1_t a1, a2;
  187. point2_t b1, b2;
  188. // TODO: use indexed_point_view if possible?
  189. detail::assign_point_from_index<0>(a, a1);
  190. detail::assign_point_from_index<1>(a, a2);
  191. detail::assign_point_from_index<0>(b, b1);
  192. detail::assign_point_from_index<1>(b, b2);
  193. return apply(a, b, policy, robust_policy, a1, a2, b1, b2);
  194. }
  195. // Relate segments a and b
  196. template
  197. <
  198. typename Segment1,
  199. typename Segment2,
  200. typename Policy,
  201. typename RobustPolicy,
  202. typename Point1,
  203. typename Point2
  204. >
  205. static inline typename Policy::return_type
  206. apply(Segment1 const& a, Segment2 const& b,
  207. Policy const&, RobustPolicy const&,
  208. Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2)
  209. {
  210. // For now create it using default constructor. In the future it could
  211. // be stored in strategy. However then apply() wouldn't be static and
  212. // all relops and setops would have to take the strategy or model.
  213. // Initialize explicitly to prevent compiler errors in case of PoD type
  214. CalcPolicy const calc_policy = CalcPolicy();
  215. BOOST_CONCEPT_ASSERT( (concepts::ConstSegment<Segment1>) );
  216. BOOST_CONCEPT_ASSERT( (concepts::ConstSegment<Segment2>) );
  217. // TODO: check only 2 first coordinates here?
  218. using geometry::detail::equals::equals_point_point;
  219. bool a_is_point = equals_point_point(a1, a2);
  220. bool b_is_point = equals_point_point(b1, b2);
  221. if(a_is_point && b_is_point)
  222. {
  223. return equals_point_point(a1, b2)
  224. ? Policy::degenerate(a, true)
  225. : Policy::disjoint()
  226. ;
  227. }
  228. typedef typename select_calculation_type
  229. <Segment1, Segment2, CalculationType>::type calc_t;
  230. calc_t const c0 = 0;
  231. calc_t const c1 = 1;
  232. typedef model::point<calc_t, 3, cs::cartesian> vec3d_t;
  233. vec3d_t const a1v = calc_policy.template to_cart3d<vec3d_t>(a1);
  234. vec3d_t const a2v = calc_policy.template to_cart3d<vec3d_t>(a2);
  235. vec3d_t const b1v = calc_policy.template to_cart3d<vec3d_t>(b1);
  236. vec3d_t const b2v = calc_policy.template to_cart3d<vec3d_t>(b2);
  237. bool degen_neq_coords = false;
  238. side_info sides;
  239. typename CalcPolicy::template plane<vec3d_t>
  240. plane2 = calc_policy.get_plane(b1v, b2v);
  241. calc_t dist_b1_b2 = 0;
  242. if (! b_is_point)
  243. {
  244. calculate_dist(b1v, b2v, plane2, dist_b1_b2);
  245. if (math::equals(dist_b1_b2, c0))
  246. {
  247. degen_neq_coords = true;
  248. b_is_point = true;
  249. dist_b1_b2 = 0;
  250. }
  251. else
  252. {
  253. // not normalized normals, the same as in side strategy
  254. sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v));
  255. if (sides.same<0>())
  256. {
  257. // Both points are at same side of other segment, we can leave
  258. return Policy::disjoint();
  259. }
  260. }
  261. }
  262. typename CalcPolicy::template plane<vec3d_t>
  263. plane1 = calc_policy.get_plane(a1v, a2v);
  264. calc_t dist_a1_a2 = 0;
  265. if (! a_is_point)
  266. {
  267. calculate_dist(a1v, a2v, plane1, dist_a1_a2);
  268. if (math::equals(dist_a1_a2, c0))
  269. {
  270. degen_neq_coords = true;
  271. a_is_point = true;
  272. dist_a1_a2 = 0;
  273. }
  274. else
  275. {
  276. // not normalized normals, the same as in side strategy
  277. sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v));
  278. if (sides.same<1>())
  279. {
  280. // Both points are at same side of other segment, we can leave
  281. return Policy::disjoint();
  282. }
  283. }
  284. }
  285. // NOTE: at this point the segments may still be disjoint
  286. calc_t len1 = 0;
  287. // point or opposite sides of a sphere/spheroid, assume point
  288. if (! a_is_point && ! detail::vec_normalize(plane1.normal, len1))
  289. {
  290. a_is_point = true;
  291. if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0)
  292. {
  293. sides.set<0>(0, 0);
  294. }
  295. }
  296. calc_t len2 = 0;
  297. if (! b_is_point && ! detail::vec_normalize(plane2.normal, len2))
  298. {
  299. b_is_point = true;
  300. if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0)
  301. {
  302. sides.set<1>(0, 0);
  303. }
  304. }
  305. // check both degenerated once more
  306. if (a_is_point && b_is_point)
  307. {
  308. return equals_point_point(a1, b2)
  309. ? Policy::degenerate(a, true)
  310. : Policy::disjoint()
  311. ;
  312. }
  313. // NOTE: at this point the segments may still be disjoint
  314. // NOTE: at this point one of the segments may be degenerated
  315. bool collinear = sides.collinear();
  316. if (! collinear)
  317. {
  318. // NOTE: for some approximations it's possible that both points may lie
  319. // on the same geodesic but still some of the sides may be != 0.
  320. // This is e.g. true for long segments represented as elliptic arcs
  321. // with origin different than the center of the coordinate system.
  322. // So make the sides consistent
  323. // WARNING: the side strategy doesn't have the info about the other
  324. // segment so it may return results inconsistent with this intersection
  325. // strategy, as it checks both segments for consistency
  326. if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0)
  327. {
  328. collinear = true;
  329. sides.set<1>(0, 0);
  330. }
  331. else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0)
  332. {
  333. collinear = true;
  334. sides.set<0>(0, 0);
  335. }
  336. }
  337. calc_t dot_n1n2 = dot_product(plane1.normal, plane2.normal);
  338. // NOTE: this is technically not needed since theoretically above sides
  339. // are calculated, but just in case check the normals.
  340. // Have in mind that SSF side strategy doesn't check this.
  341. // collinear if normals are equal or opposite: cos(a) in {-1, 1}
  342. if (! collinear && math::equals(math::abs(dot_n1n2), c1))
  343. {
  344. collinear = true;
  345. sides.set<0>(0, 0);
  346. sides.set<1>(0, 0);
  347. }
  348. if (collinear)
  349. {
  350. if (a_is_point)
  351. {
  352. return collinear_one_degenerated<Policy, calc_t>(a, true, b1, b2, a1, a2, b1v, b2v,
  353. plane2, a1v, a2v, dist_b1_b2, degen_neq_coords);
  354. }
  355. else if (b_is_point)
  356. {
  357. // b2 used to be consistent with (degenerated) checks above (is it needed?)
  358. return collinear_one_degenerated<Policy, calc_t>(b, false, a1, a2, b1, b2, a1v, a2v,
  359. plane1, b1v, b2v, dist_a1_a2, degen_neq_coords);
  360. }
  361. else
  362. {
  363. calc_t dist_a1_b1, dist_a1_b2;
  364. calc_t dist_b1_a1, dist_b1_a2;
  365. // use shorter segment
  366. if (len1 <= len2)
  367. {
  368. calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, b2v, dist_a1_a2, dist_a1_b1);
  369. calculate_collinear_data(a1, a2, b2, b1, a1v, a2v, plane1, b2v, b1v, dist_a1_a2, dist_a1_b2);
  370. dist_b1_b2 = dist_a1_b2 - dist_a1_b1;
  371. dist_b1_a1 = -dist_a1_b1;
  372. dist_b1_a2 = dist_a1_a2 - dist_a1_b1;
  373. }
  374. else
  375. {
  376. calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, a2v, dist_b1_b2, dist_b1_a1);
  377. calculate_collinear_data(b1, b2, a2, a1, b1v, b2v, plane2, a2v, a1v, dist_b1_b2, dist_b1_a2);
  378. dist_a1_a2 = dist_b1_a2 - dist_b1_a1;
  379. dist_a1_b1 = -dist_b1_a1;
  380. dist_a1_b2 = dist_b1_b2 - dist_b1_a1;
  381. }
  382. segment_ratio<calc_t> ra_from(dist_b1_a1, dist_b1_b2);
  383. segment_ratio<calc_t> ra_to(dist_b1_a2, dist_b1_b2);
  384. segment_ratio<calc_t> rb_from(dist_a1_b1, dist_a1_a2);
  385. segment_ratio<calc_t> rb_to(dist_a1_b2, dist_a1_a2);
  386. // NOTE: this is probably not needed
  387. int const a1_wrt_b = position_value(c0, dist_a1_b1, dist_a1_b2);
  388. int const a2_wrt_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2);
  389. int const b1_wrt_a = position_value(c0, dist_b1_a1, dist_b1_a2);
  390. int const b2_wrt_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2);
  391. if (a1_wrt_b == 1)
  392. {
  393. ra_from.assign(0, dist_b1_b2);
  394. rb_from.assign(0, dist_a1_a2);
  395. }
  396. else if (a1_wrt_b == 3)
  397. {
  398. ra_from.assign(dist_b1_b2, dist_b1_b2);
  399. rb_to.assign(0, dist_a1_a2);
  400. }
  401. if (a2_wrt_b == 1)
  402. {
  403. ra_to.assign(0, dist_b1_b2);
  404. rb_from.assign(dist_a1_a2, dist_a1_a2);
  405. }
  406. else if (a2_wrt_b == 3)
  407. {
  408. ra_to.assign(dist_b1_b2, dist_b1_b2);
  409. rb_to.assign(dist_a1_a2, dist_a1_a2);
  410. }
  411. if ((a1_wrt_b < 1 && a2_wrt_b < 1) || (a1_wrt_b > 3 && a2_wrt_b > 3))
  412. {
  413. return Policy::disjoint();
  414. }
  415. bool const opposite = dot_n1n2 < c0;
  416. return Policy::segments_collinear(a, b, opposite,
  417. a1_wrt_b, a2_wrt_b, b1_wrt_a, b2_wrt_a,
  418. ra_from, ra_to, rb_from, rb_to);
  419. }
  420. }
  421. else // crossing
  422. {
  423. if (a_is_point || b_is_point)
  424. {
  425. return Policy::disjoint();
  426. }
  427. vec3d_t i1;
  428. intersection_point_flag ip_flag;
  429. calc_t dist_a1_i1, dist_b1_i1;
  430. if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v,
  431. plane1, plane2, calc_policy,
  432. sides, dist_a1_a2, dist_b1_b2,
  433. i1, dist_a1_i1, dist_b1_i1, ip_flag))
  434. {
  435. // intersects
  436. segment_intersection_info
  437. <
  438. calc_t,
  439. segment_ratio<calc_t>,
  440. vec3d_t
  441. > sinfo(calc_policy);
  442. sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2);
  443. sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2);
  444. sinfo.intersection_point = i1;
  445. sinfo.ip_flag = ip_flag;
  446. return Policy::segments_crosses(sides, sinfo, a, b);
  447. }
  448. else
  449. {
  450. return Policy::disjoint();
  451. }
  452. }
  453. }
  454. private:
  455. template <typename Policy, typename CalcT, typename Segment, typename Point1, typename Point2, typename Vec3d, typename Plane>
  456. static inline typename Policy::return_type
  457. collinear_one_degenerated(Segment const& segment, bool degenerated_a,
  458. Point1 const& a1, Point1 const& a2,
  459. Point2 const& b1, Point2 const& b2,
  460. Vec3d const& a1v, Vec3d const& a2v,
  461. Plane const& plane,
  462. Vec3d const& b1v, Vec3d const& b2v,
  463. CalcT const& dist_1_2,
  464. bool degen_neq_coords)
  465. {
  466. CalcT dist_1_o;
  467. return ! calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane, b1v, b2v, dist_1_2, dist_1_o, degen_neq_coords)
  468. ? Policy::disjoint()
  469. : Policy::one_degenerate(segment, segment_ratio<CalcT>(dist_1_o, dist_1_2), degenerated_a);
  470. }
  471. template <typename Point1, typename Point2, typename Vec3d, typename Plane, typename CalcT>
  472. static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in
  473. Point2 const& b1, Point2 const& /*b2*/, // in
  474. Vec3d const& a1v, // in
  475. Vec3d const& a2v, // in
  476. Plane const& plane1, // in
  477. Vec3d const& b1v, // in
  478. Vec3d const& b2v, // in
  479. CalcT const& dist_a1_a2, // in
  480. CalcT& dist_a1_b1, // out
  481. bool degen_neq_coords = false) // in
  482. {
  483. // calculate dist_a1_b1
  484. calculate_dist(a1v, a2v, plane1, b1v, dist_a1_b1);
  485. // if b1 is equal to a1
  486. if (is_endpoint_equal(dist_a1_b1, a1, b1))
  487. {
  488. dist_a1_b1 = 0;
  489. return true;
  490. }
  491. // or b1 is equal to a2
  492. else if (is_endpoint_equal(dist_a1_a2 - dist_a1_b1, a2, b1))
  493. {
  494. dist_a1_b1 = dist_a1_a2;
  495. return true;
  496. }
  497. // check the other endpoint of degenerated segment near a pole
  498. if (degen_neq_coords)
  499. {
  500. static CalcT const c0 = 0;
  501. CalcT dist_a1_b2 = 0;
  502. calculate_dist(a1v, a2v, plane1, b2v, dist_a1_b2);
  503. if (math::equals(dist_a1_b2, c0))
  504. {
  505. dist_a1_b1 = 0;
  506. return true;
  507. }
  508. else if (math::equals(dist_a1_a2 - dist_a1_b2, c0))
  509. {
  510. dist_a1_b1 = dist_a1_a2;
  511. return true;
  512. }
  513. }
  514. // or i1 is on b
  515. return segment_ratio<CalcT>(dist_a1_b1, dist_a1_a2).on_segment();
  516. }
  517. template <typename Point1, typename Point2, typename Vec3d, typename Plane, typename CalcT>
  518. static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in
  519. Point2 const& b1, Point2 const& b2, // in
  520. Vec3d const& a1v, Vec3d const& a2v, // in
  521. Vec3d const& b1v, Vec3d const& b2v, // in
  522. Plane const& plane1, // in
  523. Plane const& plane2, // in
  524. CalcPolicy const& calc_policy, // in
  525. side_info const& sides, // in
  526. CalcT const& dist_a1_a2, // in
  527. CalcT const& dist_b1_b2, // in
  528. Vec3d & ip, // out
  529. CalcT& dist_a1_ip, // out
  530. CalcT& dist_b1_ip, // out
  531. intersection_point_flag& ip_flag) // out
  532. {
  533. Vec3d ip1, ip2;
  534. calc_policy.intersection_points(plane1, plane2, ip1, ip2);
  535. calculate_dist(a1v, a2v, plane1, ip1, dist_a1_ip);
  536. ip = ip1;
  537. // choose the opposite side of the globe if the distance is shorter
  538. {
  539. CalcT const d = abs_distance(dist_a1_a2, dist_a1_ip);
  540. if (d > CalcT(0))
  541. {
  542. // TODO: this should be ok not only for sphere
  543. // but requires more investigation
  544. CalcT const dist_a1_i2 = dist_of_i2(dist_a1_ip);
  545. CalcT const d2 = abs_distance(dist_a1_a2, dist_a1_i2);
  546. if (d2 < d)
  547. {
  548. dist_a1_ip = dist_a1_i2;
  549. ip = ip2;
  550. }
  551. }
  552. }
  553. bool is_on_a = false, is_near_a1 = false, is_near_a2 = false;
  554. if (! is_potentially_crossing(dist_a1_a2, dist_a1_ip, is_on_a, is_near_a1, is_near_a2))
  555. {
  556. return false;
  557. }
  558. calculate_dist(b1v, b2v, plane2, ip, dist_b1_ip);
  559. bool is_on_b = false, is_near_b1 = false, is_near_b2 = false;
  560. if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2))
  561. {
  562. return false;
  563. }
  564. // reassign the IP if some endpoints overlap
  565. using geometry::detail::equals::equals_point_point;
  566. if (is_near_a1)
  567. {
  568. if (is_near_b1 && equals_point_point(a1, b1))
  569. {
  570. dist_a1_ip = 0;
  571. dist_b1_ip = 0;
  572. //i1 = a1v;
  573. ip_flag = ipi_at_a1;
  574. return true;
  575. }
  576. if (is_near_b2 && equals_point_point(a1, b2))
  577. {
  578. dist_a1_ip = 0;
  579. dist_b1_ip = dist_b1_b2;
  580. //i1 = a1v;
  581. ip_flag = ipi_at_a1;
  582. return true;
  583. }
  584. }
  585. if (is_near_a2)
  586. {
  587. if (is_near_b1 && equals_point_point(a2, b1))
  588. {
  589. dist_a1_ip = dist_a1_a2;
  590. dist_b1_ip = 0;
  591. //i1 = a2v;
  592. ip_flag = ipi_at_a2;
  593. return true;
  594. }
  595. if (is_near_b2 && equals_point_point(a2, b2))
  596. {
  597. dist_a1_ip = dist_a1_a2;
  598. dist_b1_ip = dist_b1_b2;
  599. //i1 = a2v;
  600. ip_flag = ipi_at_a2;
  601. return true;
  602. }
  603. }
  604. // at this point we know that the endpoints doesn't overlap
  605. // reassign IP and distance if the IP is on a segment and one of
  606. // the endpoints of the other segment lies on the former segment
  607. if (is_on_a)
  608. {
  609. if (is_near_b1 && sides.template get<1, 0>() == 0) // b1 wrt a
  610. {
  611. dist_b1_ip = 0;
  612. //i1 = b1v;
  613. ip_flag = ipi_at_b1;
  614. return true;
  615. }
  616. if (is_near_b2 && sides.template get<1, 1>() == 0) // b2 wrt a
  617. {
  618. dist_b1_ip = dist_b1_b2;
  619. //i1 = b2v;
  620. ip_flag = ipi_at_b2;
  621. return true;
  622. }
  623. }
  624. if (is_on_b)
  625. {
  626. if (is_near_a1 && sides.template get<0, 0>() == 0) // a1 wrt b
  627. {
  628. dist_a1_ip = 0;
  629. //i1 = a1v;
  630. ip_flag = ipi_at_a1;
  631. return true;
  632. }
  633. if (is_near_a2 && sides.template get<0, 1>() == 0) // a2 wrt b
  634. {
  635. dist_a1_ip = dist_a1_a2;
  636. //i1 = a2v;
  637. ip_flag = ipi_at_a2;
  638. return true;
  639. }
  640. }
  641. ip_flag = ipi_inters;
  642. return is_on_a && is_on_b;
  643. }
  644. template <typename Vec3d, typename Plane, typename CalcT>
  645. static inline void calculate_dist(Vec3d const& a1v, // in
  646. Vec3d const& a2v, // in
  647. Plane const& plane1, // in
  648. CalcT& dist_a1_a2) // out
  649. {
  650. static CalcT const c1 = 1;
  651. CalcT const cos_a1_a2 = plane1.cos_angle_between(a1v, a2v);
  652. dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi]
  653. }
  654. template <typename Vec3d, typename Plane, typename CalcT>
  655. static inline void calculate_dist(Vec3d const& a1v, // in
  656. Vec3d const& /*a2v*/, // in
  657. Plane const& plane1, // in
  658. Vec3d const& i1, // in
  659. CalcT& dist_a1_i1) // out
  660. {
  661. static CalcT const c1 = 1;
  662. static CalcT const c2 = 2;
  663. static CalcT const c4 = 4;
  664. bool is_forward = true;
  665. CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward);
  666. dist_a1_i1 = -cos_a1_i1 + c1; // [0, 2] representing [0, pi]
  667. if (! is_forward) // left or right of a1 on a
  668. {
  669. dist_a1_i1 = -dist_a1_i1; // [0, 2] -> [0, -2] representing [0, -pi]
  670. }
  671. if (dist_a1_i1 <= -c2) // <= -pi
  672. {
  673. dist_a1_i1 += c4; // += 2pi
  674. }
  675. }
  676. /*
  677. template <typename Vec3d, typename Plane, typename CalcT>
  678. static inline void calculate_dists(Vec3d const& a1v, // in
  679. Vec3d const& a2v, // in
  680. Plane const& plane1, // in
  681. Vec3d const& i1, // in
  682. CalcT& dist_a1_a2, // out
  683. CalcT& dist_a1_i1) // out
  684. {
  685. calculate_dist(a1v, a2v, plane1, dist_a1_a2);
  686. calculate_dist(a1v, a2v, plane1, i1, dist_a1_i1);
  687. }
  688. */
  689. // the dist of the ip on the other side of the sphere
  690. template <typename CalcT>
  691. static inline CalcT dist_of_i2(CalcT const& dist_a1_i1)
  692. {
  693. CalcT const c2 = 2;
  694. CalcT const c4 = 4;
  695. CalcT dist_a1_i2 = dist_a1_i1 - c2; // dist_a1_i2 = dist_a1_i1 - pi;
  696. if (dist_a1_i2 <= -c2) // <= -pi
  697. {
  698. dist_a1_i2 += c4; // += 2pi;
  699. }
  700. return dist_a1_i2;
  701. }
  702. template <typename CalcT>
  703. static inline CalcT abs_distance(CalcT const& dist_a1_a2, CalcT const& dist_a1_i1)
  704. {
  705. if (dist_a1_i1 < CalcT(0))
  706. return -dist_a1_i1;
  707. else if (dist_a1_i1 > dist_a1_a2)
  708. return dist_a1_i1 - dist_a1_a2;
  709. else
  710. return CalcT(0);
  711. }
  712. template <typename CalcT>
  713. static inline bool is_potentially_crossing(CalcT const& dist_a1_a2, CalcT const& dist_a1_i1, // in
  714. bool& is_on_a, bool& is_near_a1, bool& is_near_a2) // out
  715. {
  716. is_on_a = segment_ratio<CalcT>(dist_a1_i1, dist_a1_a2).on_segment();
  717. is_near_a1 = is_near(dist_a1_i1);
  718. is_near_a2 = is_near(dist_a1_a2 - dist_a1_i1);
  719. return is_on_a || is_near_a1 || is_near_a2;
  720. }
  721. template <typename CalcT, typename P1, typename P2>
  722. static inline bool is_endpoint_equal(CalcT const& dist,
  723. P1 const& ai, P2 const& b1)
  724. {
  725. static CalcT const c0 = 0;
  726. using geometry::detail::equals::equals_point_point;
  727. return is_near(dist) && (math::equals(dist, c0) || equals_point_point(ai, b1));
  728. }
  729. template <typename CalcT>
  730. static inline bool is_near(CalcT const& dist)
  731. {
  732. CalcT const small_number = CalcT(boost::is_same<CalcT, float>::value ? 0.0001 : 0.00000001);
  733. return math::abs(dist) <= small_number;
  734. }
  735. template <typename ProjCoord1, typename ProjCoord2>
  736. static inline int position_value(ProjCoord1 const& ca1,
  737. ProjCoord2 const& cb1,
  738. ProjCoord2 const& cb2)
  739. {
  740. // S1x 0 1 2 3 4
  741. // S2 |---------->
  742. return math::equals(ca1, cb1) ? 1
  743. : math::equals(ca1, cb2) ? 3
  744. : cb1 < cb2 ?
  745. ( ca1 < cb1 ? 0
  746. : ca1 > cb2 ? 4
  747. : 2 )
  748. : ( ca1 > cb1 ? 0
  749. : ca1 < cb2 ? 4
  750. : 2 );
  751. }
  752. };
  753. struct spherical_segments_calc_policy
  754. {
  755. template <typename Point, typename Point3d>
  756. static Point from_cart3d(Point3d const& point_3d)
  757. {
  758. return formula::cart3d_to_sph<Point>(point_3d);
  759. }
  760. template <typename Point3d, typename Point>
  761. static Point3d to_cart3d(Point const& point)
  762. {
  763. return formula::sph_to_cart3d<Point3d>(point);
  764. }
  765. template <typename Point3d>
  766. struct plane
  767. {
  768. typedef typename coordinate_type<Point3d>::type coord_t;
  769. // not normalized
  770. plane(Point3d const& p1, Point3d const& p2)
  771. : normal(cross_product(p1, p2))
  772. {}
  773. int side_value(Point3d const& pt) const
  774. {
  775. return formula::sph_side_value(normal, pt);
  776. }
  777. static coord_t cos_angle_between(Point3d const& p1, Point3d const& p2)
  778. {
  779. return dot_product(p1, p2);
  780. }
  781. coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const
  782. {
  783. coord_t const c0 = 0;
  784. is_forward = dot_product(normal, cross_product(p1, p2)) >= c0;
  785. return dot_product(p1, p2);
  786. }
  787. Point3d normal;
  788. };
  789. template <typename Point3d>
  790. static plane<Point3d> get_plane(Point3d const& p1, Point3d const& p2)
  791. {
  792. return plane<Point3d>(p1, p2);
  793. }
  794. template <typename Point3d>
  795. static bool intersection_points(plane<Point3d> const& plane1,
  796. plane<Point3d> const& plane2,
  797. Point3d & ip1, Point3d & ip2)
  798. {
  799. typedef typename coordinate_type<Point3d>::type coord_t;
  800. ip1 = cross_product(plane1.normal, plane2.normal);
  801. // NOTE: the length should be greater than 0 at this point
  802. // if the normals were not normalized and their dot product
  803. // not checked before this function is called the length
  804. // should be checked here (math::equals(len, c0))
  805. coord_t const len = math::sqrt(dot_product(ip1, ip1));
  806. divide_value(ip1, len); // normalize i1
  807. ip2 = ip1;
  808. multiply_value(ip2, coord_t(-1));
  809. return true;
  810. }
  811. };
  812. template
  813. <
  814. typename CalculationType = void
  815. >
  816. struct spherical_segments
  817. : ecef_segments
  818. <
  819. spherical_segments_calc_policy,
  820. CalculationType
  821. >
  822. {};
  823. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  824. namespace services
  825. {
  826. /*template <typename CalculationType>
  827. struct default_strategy<spherical_polar_tag, CalculationType>
  828. {
  829. typedef spherical_segments<CalculationType> type;
  830. };*/
  831. template <typename CalculationType>
  832. struct default_strategy<spherical_equatorial_tag, CalculationType>
  833. {
  834. typedef spherical_segments<CalculationType> type;
  835. };
  836. template <typename CalculationType>
  837. struct default_strategy<geographic_tag, CalculationType>
  838. {
  839. // NOTE: Spherical strategy returns the same result as the geographic one
  840. // representing segments as great elliptic arcs. If the elliptic arcs are
  841. // not great elliptic arcs (the origin not in the center of the coordinate
  842. // system) then there may be problems with consistency of the side and
  843. // intersection strategies.
  844. typedef spherical_segments<CalculationType> type;
  845. };
  846. } // namespace services
  847. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  848. }} // namespace strategy::intersection
  849. namespace strategy
  850. {
  851. namespace within { namespace services
  852. {
  853. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  854. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, linear_tag, spherical_tag, spherical_tag>
  855. {
  856. typedef strategy::intersection::spherical_segments<> type;
  857. };
  858. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  859. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, polygonal_tag, spherical_tag, spherical_tag>
  860. {
  861. typedef strategy::intersection::spherical_segments<> type;
  862. };
  863. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  864. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, linear_tag, spherical_tag, spherical_tag>
  865. {
  866. typedef strategy::intersection::spherical_segments<> type;
  867. };
  868. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  869. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, polygonal_tag, spherical_tag, spherical_tag>
  870. {
  871. typedef strategy::intersection::spherical_segments<> type;
  872. };
  873. }} // within::services
  874. namespace covered_by { namespace services
  875. {
  876. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  877. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, linear_tag, spherical_tag, spherical_tag>
  878. {
  879. typedef strategy::intersection::spherical_segments<> type;
  880. };
  881. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  882. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, linear_tag, polygonal_tag, spherical_tag, spherical_tag>
  883. {
  884. typedef strategy::intersection::spherical_segments<> type;
  885. };
  886. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  887. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, linear_tag, spherical_tag, spherical_tag>
  888. {
  889. typedef strategy::intersection::spherical_segments<> type;
  890. };
  891. template <typename Geometry1, typename Geometry2, typename AnyTag1, typename AnyTag2>
  892. struct default_strategy<Geometry1, Geometry2, AnyTag1, AnyTag2, polygonal_tag, polygonal_tag, spherical_tag, spherical_tag>
  893. {
  894. typedef strategy::intersection::spherical_segments<> type;
  895. };
  896. }} // within::services
  897. } // strategy
  898. }} // namespace boost::geometry
  899. #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_INTERSECTION_HPP