intersection.hpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. // Boost.Geometry
  2. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2016-2019, 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_GEOGRAPHIC_INTERSECTION_HPP
  9. #define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_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/formulas/andoyer_inverse.hpp>
  20. #include <boost/geometry/formulas/sjoberg_intersection.hpp>
  21. #include <boost/geometry/formulas/spherical.hpp>
  22. #include <boost/geometry/formulas/unit_spheroid.hpp>
  23. #include <boost/geometry/geometries/concepts/point_concept.hpp>
  24. #include <boost/geometry/geometries/concepts/segment_concept.hpp>
  25. #include <boost/geometry/policies/robustness/segment_ratio.hpp>
  26. #include <boost/geometry/srs/spheroid.hpp>
  27. #include <boost/geometry/strategies/geographic/area.hpp>
  28. #include <boost/geometry/strategies/geographic/disjoint_segment_box.hpp>
  29. #include <boost/geometry/strategies/geographic/distance.hpp>
  30. #include <boost/geometry/strategies/geographic/envelope.hpp>
  31. #include <boost/geometry/strategies/geographic/parameters.hpp>
  32. #include <boost/geometry/strategies/geographic/point_in_poly_winding.hpp>
  33. #include <boost/geometry/strategies/geographic/side.hpp>
  34. #include <boost/geometry/strategies/spherical/expand_box.hpp>
  35. #include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
  36. #include <boost/geometry/strategies/spherical/point_in_point.hpp>
  37. #include <boost/geometry/strategies/intersection.hpp>
  38. #include <boost/geometry/strategies/intersection_result.hpp>
  39. #include <boost/geometry/strategies/side_info.hpp>
  40. #include <boost/geometry/util/math.hpp>
  41. #include <boost/geometry/util/select_calculation_type.hpp>
  42. namespace boost { namespace geometry
  43. {
  44. namespace strategy { namespace intersection
  45. {
  46. // CONSIDER: Improvement of the robustness/accuracy/repeatability by
  47. // moving all segments to 0 longitude
  48. // picking latitudes closer to 0
  49. // etc.
  50. template
  51. <
  52. typename FormulaPolicy = strategy::andoyer,
  53. unsigned int Order = strategy::default_order<FormulaPolicy>::value,
  54. typename Spheroid = srs::spheroid<double>,
  55. typename CalculationType = void
  56. >
  57. struct geographic_segments
  58. {
  59. typedef side::geographic
  60. <
  61. FormulaPolicy, Spheroid, CalculationType
  62. > side_strategy_type;
  63. inline side_strategy_type get_side_strategy() const
  64. {
  65. return side_strategy_type(m_spheroid);
  66. }
  67. template <typename Geometry1, typename Geometry2>
  68. struct point_in_geometry_strategy
  69. {
  70. typedef strategy::within::geographic_winding
  71. <
  72. typename point_type<Geometry1>::type,
  73. typename point_type<Geometry2>::type,
  74. FormulaPolicy,
  75. Spheroid,
  76. CalculationType
  77. > type;
  78. };
  79. template <typename Geometry1, typename Geometry2>
  80. inline typename point_in_geometry_strategy<Geometry1, Geometry2>::type
  81. get_point_in_geometry_strategy() const
  82. {
  83. typedef typename point_in_geometry_strategy
  84. <
  85. Geometry1, Geometry2
  86. >::type strategy_type;
  87. return strategy_type(m_spheroid);
  88. }
  89. template <typename Geometry>
  90. struct area_strategy
  91. {
  92. typedef area::geographic
  93. <
  94. FormulaPolicy,
  95. Order,
  96. Spheroid,
  97. CalculationType
  98. > type;
  99. };
  100. template <typename Geometry>
  101. inline typename area_strategy<Geometry>::type get_area_strategy() const
  102. {
  103. typedef typename area_strategy<Geometry>::type strategy_type;
  104. return strategy_type(m_spheroid);
  105. }
  106. template <typename Geometry>
  107. struct distance_strategy
  108. {
  109. typedef distance::geographic
  110. <
  111. FormulaPolicy,
  112. Spheroid,
  113. CalculationType
  114. > type;
  115. };
  116. template <typename Geometry>
  117. inline typename distance_strategy<Geometry>::type get_distance_strategy() const
  118. {
  119. typedef typename distance_strategy<Geometry>::type strategy_type;
  120. return strategy_type(m_spheroid);
  121. }
  122. typedef envelope::geographic<FormulaPolicy, Spheroid, CalculationType>
  123. envelope_strategy_type;
  124. inline envelope_strategy_type get_envelope_strategy() const
  125. {
  126. return envelope_strategy_type(m_spheroid);
  127. }
  128. typedef expand::geographic_segment<FormulaPolicy, Spheroid, CalculationType>
  129. expand_strategy_type;
  130. inline expand_strategy_type get_expand_strategy() const
  131. {
  132. return expand_strategy_type(m_spheroid);
  133. }
  134. typedef within::spherical_point_point point_in_point_strategy_type;
  135. static inline point_in_point_strategy_type get_point_in_point_strategy()
  136. {
  137. return point_in_point_strategy_type();
  138. }
  139. typedef within::spherical_point_point equals_point_point_strategy_type;
  140. static inline equals_point_point_strategy_type get_equals_point_point_strategy()
  141. {
  142. return equals_point_point_strategy_type();
  143. }
  144. typedef disjoint::spherical_box_box disjoint_box_box_strategy_type;
  145. static inline disjoint_box_box_strategy_type get_disjoint_box_box_strategy()
  146. {
  147. return disjoint_box_box_strategy_type();
  148. }
  149. typedef disjoint::segment_box_geographic
  150. <
  151. FormulaPolicy, Spheroid, CalculationType
  152. > disjoint_segment_box_strategy_type;
  153. inline disjoint_segment_box_strategy_type get_disjoint_segment_box_strategy() const
  154. {
  155. return disjoint_segment_box_strategy_type(m_spheroid);
  156. }
  157. typedef covered_by::spherical_point_box disjoint_point_box_strategy_type;
  158. typedef expand::spherical_box expand_box_strategy_type;
  159. enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 };
  160. template <typename CoordinateType, typename SegmentRatio>
  161. struct segment_intersection_info
  162. {
  163. template <typename Point, typename Segment1, typename Segment2>
  164. void calculate(Point& point, Segment1 const& a, Segment2 const& b) const
  165. {
  166. if (ip_flag == ipi_inters)
  167. {
  168. // TODO: assign the rest of coordinates
  169. set_from_radian<0>(point, lon);
  170. set_from_radian<1>(point, lat);
  171. }
  172. else if (ip_flag == ipi_at_a1)
  173. {
  174. detail::assign_point_from_index<0>(a, point);
  175. }
  176. else if (ip_flag == ipi_at_a2)
  177. {
  178. detail::assign_point_from_index<1>(a, point);
  179. }
  180. else if (ip_flag == ipi_at_b1)
  181. {
  182. detail::assign_point_from_index<0>(b, point);
  183. }
  184. else // ip_flag == ipi_at_b2
  185. {
  186. detail::assign_point_from_index<1>(b, point);
  187. }
  188. }
  189. CoordinateType lon;
  190. CoordinateType lat;
  191. SegmentRatio robust_ra;
  192. SegmentRatio robust_rb;
  193. intersection_point_flag ip_flag;
  194. };
  195. explicit geographic_segments(Spheroid const& spheroid = Spheroid())
  196. : m_spheroid(spheroid)
  197. {}
  198. // Relate segments a and b
  199. template
  200. <
  201. typename Segment1,
  202. typename Segment2,
  203. typename Policy,
  204. typename RobustPolicy
  205. >
  206. inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b,
  207. Policy const& policy,
  208. RobustPolicy const& robust_policy) const
  209. {
  210. typedef typename point_type<Segment1>::type point1_t;
  211. typedef typename point_type<Segment2>::type point2_t;
  212. point1_t a1, a2;
  213. point2_t b1, b2;
  214. detail::assign_point_from_index<0>(a, a1);
  215. detail::assign_point_from_index<1>(a, a2);
  216. detail::assign_point_from_index<0>(b, b1);
  217. detail::assign_point_from_index<1>(b, b2);
  218. return apply(a, b, policy, robust_policy, a1, a2, b1, b2);
  219. }
  220. // Relate segments a and b
  221. template
  222. <
  223. typename Segment1,
  224. typename Segment2,
  225. typename Policy,
  226. typename RobustPolicy,
  227. typename Point1,
  228. typename Point2
  229. >
  230. inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b,
  231. Policy const&, RobustPolicy const&,
  232. Point1 a1, Point1 a2, Point2 b1, Point2 b2) const
  233. {
  234. bool is_a_reversed = get<1>(a1) > get<1>(a2);
  235. bool is_b_reversed = get<1>(b1) > get<1>(b2);
  236. /*
  237. typename coordinate_type<Point1>::type
  238. const a1_lon = get<0>(a1),
  239. const a2_lon = get<0>(a2);
  240. typename coordinate_type<Point2>::type
  241. const b1_lon = get<0>(b1),
  242. const b2_lon = get<0>(b2);
  243. bool is_a_reversed = a1_lon > a2_lon || a1_lon == a2_lon && get<1>(a1) > get<1>(a2);
  244. bool is_b_reversed = b1_lon > b2_lon || b1_lon == b2_lon && get<1>(b1) > get<1>(b2);
  245. */
  246. if (is_a_reversed)
  247. {
  248. std::swap(a1, a2);
  249. }
  250. if (is_b_reversed)
  251. {
  252. std::swap(b1, b2);
  253. }
  254. return apply<Policy>(a, b, a1, a2, b1, b2, is_a_reversed, is_b_reversed);
  255. }
  256. private:
  257. // Relate segments a and b
  258. template
  259. <
  260. typename Policy,
  261. typename Segment1,
  262. typename Segment2,
  263. typename Point1,
  264. typename Point2
  265. >
  266. inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b,
  267. Point1 const& a1, Point1 const& a2,
  268. Point2 const& b1, Point2 const& b2,
  269. bool is_a_reversed, bool is_b_reversed) const
  270. {
  271. BOOST_CONCEPT_ASSERT( (concepts::ConstSegment<Segment1>) );
  272. BOOST_CONCEPT_ASSERT( (concepts::ConstSegment<Segment2>) );
  273. typedef typename select_calculation_type
  274. <Segment1, Segment2, CalculationType>::type calc_t;
  275. typedef srs::spheroid<calc_t> spheroid_type;
  276. static const calc_t c0 = 0;
  277. // normalized spheroid
  278. spheroid_type spheroid = formula::unit_spheroid<spheroid_type>(m_spheroid);
  279. // TODO: check only 2 first coordinates here?
  280. bool a_is_point = equals_point_point(a1, a2);
  281. bool b_is_point = equals_point_point(b1, b2);
  282. if(a_is_point && b_is_point)
  283. {
  284. return equals_point_point(a1, b2)
  285. ? Policy::degenerate(a, true)
  286. : Policy::disjoint()
  287. ;
  288. }
  289. calc_t const a1_lon = get_as_radian<0>(a1);
  290. calc_t const a1_lat = get_as_radian<1>(a1);
  291. calc_t const a2_lon = get_as_radian<0>(a2);
  292. calc_t const a2_lat = get_as_radian<1>(a2);
  293. calc_t const b1_lon = get_as_radian<0>(b1);
  294. calc_t const b1_lat = get_as_radian<1>(b1);
  295. calc_t const b2_lon = get_as_radian<0>(b2);
  296. calc_t const b2_lat = get_as_radian<1>(b2);
  297. side_info sides;
  298. // NOTE: potential optimization, don't calculate distance at this point
  299. // this would require to reimplement inverse strategy to allow
  300. // calculation of distance if needed, probably also storing intermediate
  301. // results somehow inside an object.
  302. typedef typename FormulaPolicy::template inverse<calc_t, true, true, false, false, false> inverse_dist_azi;
  303. typedef typename inverse_dist_azi::result_type inverse_result;
  304. // TODO: no need to call inverse formula if we know that the points are equal
  305. // distance can be set to 0 in this case and azimuth may be not calculated
  306. bool is_equal_a1_b1 = equals_point_point(a1, b1);
  307. bool is_equal_a2_b1 = equals_point_point(a2, b1);
  308. bool degen_neq_coords = false;
  309. inverse_result res_b1_b2, res_b1_a1, res_b1_a2;
  310. if (! b_is_point)
  311. {
  312. res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid);
  313. if (math::equals(res_b1_b2.distance, c0))
  314. {
  315. b_is_point = true;
  316. degen_neq_coords = true;
  317. }
  318. else
  319. {
  320. res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid);
  321. if (math::equals(res_b1_a1.distance, c0))
  322. {
  323. is_equal_a1_b1 = true;
  324. }
  325. res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid);
  326. if (math::equals(res_b1_a2.distance, c0))
  327. {
  328. is_equal_a2_b1 = true;
  329. }
  330. sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth),
  331. is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth));
  332. if (sides.same<0>())
  333. {
  334. // Both points are at the same side of other segment, we can leave
  335. return Policy::disjoint();
  336. }
  337. }
  338. }
  339. bool is_equal_a1_b2 = equals_point_point(a1, b2);
  340. inverse_result res_a1_a2, res_a1_b1, res_a1_b2;
  341. if (! a_is_point)
  342. {
  343. res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid);
  344. if (math::equals(res_a1_a2.distance, c0))
  345. {
  346. a_is_point = true;
  347. degen_neq_coords = true;
  348. }
  349. else
  350. {
  351. res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid);
  352. if (math::equals(res_a1_b1.distance, c0))
  353. {
  354. is_equal_a1_b1 = true;
  355. }
  356. res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid);
  357. if (math::equals(res_a1_b2.distance, c0))
  358. {
  359. is_equal_a1_b2 = true;
  360. }
  361. sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth),
  362. is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth));
  363. if (sides.same<1>())
  364. {
  365. // Both points are at the same side of other segment, we can leave
  366. return Policy::disjoint();
  367. }
  368. }
  369. }
  370. if(a_is_point && b_is_point)
  371. {
  372. return is_equal_a1_b2
  373. ? Policy::degenerate(a, true)
  374. : Policy::disjoint()
  375. ;
  376. }
  377. // NOTE: at this point the segments may still be disjoint
  378. // NOTE: at this point one of the segments may be degenerated
  379. bool collinear = sides.collinear();
  380. if (! collinear)
  381. {
  382. // WARNING: the side strategy doesn't have the info about the other
  383. // segment so it may return results inconsistent with this intersection
  384. // strategy, as it checks both segments for consistency
  385. if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0)
  386. {
  387. collinear = true;
  388. sides.set<1>(0, 0);
  389. }
  390. else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0)
  391. {
  392. collinear = true;
  393. sides.set<0>(0, 0);
  394. }
  395. }
  396. if (collinear)
  397. {
  398. if (a_is_point)
  399. {
  400. return collinear_one_degenerated<Policy, calc_t>(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, is_b_reversed, degen_neq_coords);
  401. }
  402. else if (b_is_point)
  403. {
  404. return collinear_one_degenerated<Policy, calc_t>(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, is_a_reversed, degen_neq_coords);
  405. }
  406. else
  407. {
  408. calc_t dist_a1_a2, dist_a1_b1, dist_a1_b2;
  409. calc_t dist_b1_b2, dist_b1_a1, dist_b1_a2;
  410. // use shorter segment
  411. if (res_a1_a2.distance <= res_b1_b2.distance)
  412. {
  413. calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_a1_a2, dist_a1_b1);
  414. calculate_collinear_data(a1, a2, b2, b1, res_a1_a2, res_a1_b2, res_a1_b1, dist_a1_a2, dist_a1_b2);
  415. dist_b1_b2 = dist_a1_b2 - dist_a1_b1;
  416. dist_b1_a1 = -dist_a1_b1;
  417. dist_b1_a2 = dist_a1_a2 - dist_a1_b1;
  418. }
  419. else
  420. {
  421. calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, dist_b1_b2, dist_b1_a1);
  422. calculate_collinear_data(b1, b2, a2, a1, res_b1_b2, res_b1_a2, res_b1_a1, dist_b1_b2, dist_b1_a2);
  423. dist_a1_a2 = dist_b1_a2 - dist_b1_a1;
  424. dist_a1_b1 = -dist_b1_a1;
  425. dist_a1_b2 = dist_b1_b2 - dist_b1_a1;
  426. }
  427. // NOTE: this is probably not needed
  428. calc_t const c0 = 0;
  429. int a1_on_b = position_value(c0, dist_a1_b1, dist_a1_b2);
  430. int a2_on_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2);
  431. int b1_on_a = position_value(c0, dist_b1_a1, dist_b1_a2);
  432. int b2_on_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2);
  433. if ((a1_on_b < 1 && a2_on_b < 1) || (a1_on_b > 3 && a2_on_b > 3))
  434. {
  435. return Policy::disjoint();
  436. }
  437. if (a1_on_b == 1)
  438. {
  439. dist_b1_a1 = 0;
  440. dist_a1_b1 = 0;
  441. }
  442. else if (a1_on_b == 3)
  443. {
  444. dist_b1_a1 = dist_b1_b2;
  445. dist_a1_b2 = 0;
  446. }
  447. if (a2_on_b == 1)
  448. {
  449. dist_b1_a2 = 0;
  450. dist_a1_b1 = dist_a1_a2;
  451. }
  452. else if (a2_on_b == 3)
  453. {
  454. dist_b1_a2 = dist_b1_b2;
  455. dist_a1_b2 = dist_a1_a2;
  456. }
  457. bool opposite = ! same_direction(res_a1_a2.azimuth, res_b1_b2.azimuth);
  458. // NOTE: If segment was reversed opposite, positions and segment ratios has to be altered
  459. if (is_a_reversed)
  460. {
  461. // opposite
  462. opposite = ! opposite;
  463. // positions
  464. std::swap(a1_on_b, a2_on_b);
  465. b1_on_a = 4 - b1_on_a;
  466. b2_on_a = 4 - b2_on_a;
  467. // distances for ratios
  468. std::swap(dist_b1_a1, dist_b1_a2);
  469. dist_a1_b1 = dist_a1_a2 - dist_a1_b1;
  470. dist_a1_b2 = dist_a1_a2 - dist_a1_b2;
  471. }
  472. if (is_b_reversed)
  473. {
  474. // opposite
  475. opposite = ! opposite;
  476. // positions
  477. a1_on_b = 4 - a1_on_b;
  478. a2_on_b = 4 - a2_on_b;
  479. std::swap(b1_on_a, b2_on_a);
  480. // distances for ratios
  481. dist_b1_a1 = dist_b1_b2 - dist_b1_a1;
  482. dist_b1_a2 = dist_b1_b2 - dist_b1_a2;
  483. std::swap(dist_a1_b1, dist_a1_b2);
  484. }
  485. segment_ratio<calc_t> ra_from(dist_b1_a1, dist_b1_b2);
  486. segment_ratio<calc_t> ra_to(dist_b1_a2, dist_b1_b2);
  487. segment_ratio<calc_t> rb_from(dist_a1_b1, dist_a1_a2);
  488. segment_ratio<calc_t> rb_to(dist_a1_b2, dist_a1_a2);
  489. return Policy::segments_collinear(a, b, opposite,
  490. a1_on_b, a2_on_b, b1_on_a, b2_on_a,
  491. ra_from, ra_to, rb_from, rb_to);
  492. }
  493. }
  494. else // crossing or touching
  495. {
  496. if (a_is_point || b_is_point)
  497. {
  498. return Policy::disjoint();
  499. }
  500. calc_t lon = 0, lat = 0;
  501. intersection_point_flag ip_flag;
  502. calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1;
  503. if (calculate_ip_data(a1, a2, b1, b2,
  504. a1_lon, a1_lat, a2_lon, a2_lat,
  505. b1_lon, b1_lat, b2_lon, b2_lat,
  506. res_a1_a2, res_a1_b1, res_a1_b2,
  507. res_b1_b2, res_b1_a1, res_b1_a2,
  508. sides, spheroid,
  509. lon, lat,
  510. dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1,
  511. ip_flag))
  512. {
  513. // NOTE: If segment was reversed sides and segment ratios has to be altered
  514. if (is_a_reversed)
  515. {
  516. // sides
  517. sides_reverse_segment<0>(sides);
  518. // distance for ratio
  519. dist_a1_i1 = dist_a1_a2 - dist_a1_i1;
  520. // ip flag
  521. ip_flag_reverse_segment(ip_flag, ipi_at_a1, ipi_at_a2);
  522. }
  523. if (is_b_reversed)
  524. {
  525. // sides
  526. sides_reverse_segment<1>(sides);
  527. // distance for ratio
  528. dist_b1_i1 = dist_b1_b2 - dist_b1_i1;
  529. // ip flag
  530. ip_flag_reverse_segment(ip_flag, ipi_at_b1, ipi_at_b2);
  531. }
  532. // intersects
  533. segment_intersection_info
  534. <
  535. calc_t,
  536. segment_ratio<calc_t>
  537. > sinfo;
  538. sinfo.lon = lon;
  539. sinfo.lat = lat;
  540. sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2);
  541. sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2);
  542. sinfo.ip_flag = ip_flag;
  543. return Policy::segments_crosses(sides, sinfo, a, b);
  544. }
  545. else
  546. {
  547. return Policy::disjoint();
  548. }
  549. }
  550. }
  551. template <typename Policy, typename CalcT, typename Segment, typename Point1, typename Point2, typename ResultInverse>
  552. static inline typename Policy::return_type
  553. collinear_one_degenerated(Segment const& segment, bool degenerated_a,
  554. Point1 const& a1, Point1 const& a2,
  555. Point2 const& b1, Point2 const& b2,
  556. ResultInverse const& res_a1_a2,
  557. ResultInverse const& res_a1_b1,
  558. ResultInverse const& res_a1_b2,
  559. bool is_other_reversed,
  560. bool degen_neq_coords)
  561. {
  562. CalcT dist_1_2, dist_1_o;
  563. if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_1_2, dist_1_o, degen_neq_coords))
  564. {
  565. return Policy::disjoint();
  566. }
  567. // NOTE: If segment was reversed segment ratio has to be altered
  568. if (is_other_reversed)
  569. {
  570. // distance for ratio
  571. dist_1_o = dist_1_2 - dist_1_o;
  572. }
  573. return Policy::one_degenerate(segment, segment_ratio<CalcT>(dist_1_o, dist_1_2), degenerated_a);
  574. }
  575. // TODO: instead of checks below test bi against a1 and a2 here?
  576. // in order to make this independent from is_near()
  577. template <typename Point1, typename Point2, typename ResultInverse, typename CalcT>
  578. static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in
  579. Point2 const& b1, Point2 const& /*b2*/, // in
  580. ResultInverse const& res_a1_a2, // in
  581. ResultInverse const& res_a1_b1, // in
  582. ResultInverse const& res_a1_b2, // in
  583. CalcT& dist_a1_a2, // out
  584. CalcT& dist_a1_b1, // out
  585. bool degen_neq_coords = false) // in
  586. {
  587. dist_a1_a2 = res_a1_a2.distance;
  588. dist_a1_b1 = res_a1_b1.distance;
  589. if (! same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth))
  590. {
  591. dist_a1_b1 = -dist_a1_b1;
  592. }
  593. // if b1 is close a1
  594. if (is_endpoint_equal(dist_a1_b1, a1, b1))
  595. {
  596. dist_a1_b1 = 0;
  597. return true;
  598. }
  599. // if b1 is close a2
  600. else if (is_endpoint_equal(dist_a1_a2 - dist_a1_b1, a2, b1))
  601. {
  602. dist_a1_b1 = dist_a1_a2;
  603. return true;
  604. }
  605. // check the other endpoint of degenerated segment near a pole
  606. if (degen_neq_coords)
  607. {
  608. static CalcT const c0 = 0;
  609. if (math::equals(res_a1_b2.distance, c0))
  610. {
  611. dist_a1_b1 = 0;
  612. return true;
  613. }
  614. else if (math::equals(dist_a1_a2 - res_a1_b2.distance, c0))
  615. {
  616. dist_a1_b1 = dist_a1_a2;
  617. return true;
  618. }
  619. }
  620. // or i1 is on b
  621. return segment_ratio<CalcT>(dist_a1_b1, dist_a1_a2).on_segment();
  622. }
  623. template <typename Point1, typename Point2, typename CalcT, typename ResultInverse, typename Spheroid_>
  624. static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in
  625. Point2 const& b1, Point2 const& b2, // in
  626. CalcT const& a1_lon, CalcT const& a1_lat, // in
  627. CalcT const& a2_lon, CalcT const& a2_lat, // in
  628. CalcT const& b1_lon, CalcT const& b1_lat, // in
  629. CalcT const& b2_lon, CalcT const& b2_lat, // in
  630. ResultInverse const& res_a1_a2, // in
  631. ResultInverse const& res_a1_b1, // in
  632. ResultInverse const& res_a1_b2, // in
  633. ResultInverse const& res_b1_b2, // in
  634. ResultInverse const& res_b1_a1, // in
  635. ResultInverse const& res_b1_a2, // in
  636. side_info const& sides, // in
  637. Spheroid_ const& spheroid, // in
  638. CalcT & lon, CalcT & lat, // out
  639. CalcT& dist_a1_a2, CalcT& dist_a1_ip, // out
  640. CalcT& dist_b1_b2, CalcT& dist_b1_ip, // out
  641. intersection_point_flag& ip_flag) // out
  642. {
  643. dist_a1_a2 = res_a1_a2.distance;
  644. dist_b1_b2 = res_b1_b2.distance;
  645. // assign the IP if some endpoints overlap
  646. if (equals_point_point(a1, b1))
  647. {
  648. lon = a1_lon;
  649. lat = a1_lat;
  650. dist_a1_ip = 0;
  651. dist_b1_ip = 0;
  652. ip_flag = ipi_at_a1;
  653. return true;
  654. }
  655. else if (equals_point_point(a1, b2))
  656. {
  657. lon = a1_lon;
  658. lat = a1_lat;
  659. dist_a1_ip = 0;
  660. dist_b1_ip = dist_b1_b2;
  661. ip_flag = ipi_at_a1;
  662. return true;
  663. }
  664. else if (equals_point_point(a2, b1))
  665. {
  666. lon = a2_lon;
  667. lat = a2_lat;
  668. dist_a1_ip = dist_a1_a2;
  669. dist_b1_ip = 0;
  670. ip_flag = ipi_at_a2;
  671. return true;
  672. }
  673. else if (equals_point_point(a2, b2))
  674. {
  675. lon = a2_lon;
  676. lat = a2_lat;
  677. dist_a1_ip = dist_a1_a2;
  678. dist_b1_ip = dist_b1_b2;
  679. ip_flag = ipi_at_a2;
  680. return true;
  681. }
  682. // at this point we know that the endpoints doesn't overlap
  683. // check cases when an endpoint lies on the other geodesic
  684. if (sides.template get<0, 0>() == 0) // a1 wrt b
  685. {
  686. if (res_b1_a1.distance <= res_b1_b2.distance
  687. && same_direction(res_b1_a1.azimuth, res_b1_b2.azimuth))
  688. {
  689. lon = a1_lon;
  690. lat = a1_lat;
  691. dist_a1_ip = 0;
  692. dist_b1_ip = res_b1_a1.distance;
  693. ip_flag = ipi_at_a1;
  694. return true;
  695. }
  696. else
  697. {
  698. return false;
  699. }
  700. }
  701. else if (sides.template get<0, 1>() == 0) // a2 wrt b
  702. {
  703. if (res_b1_a2.distance <= res_b1_b2.distance
  704. && same_direction(res_b1_a2.azimuth, res_b1_b2.azimuth))
  705. {
  706. lon = a2_lon;
  707. lat = a2_lat;
  708. dist_a1_ip = res_a1_a2.distance;
  709. dist_b1_ip = res_b1_a2.distance;
  710. ip_flag = ipi_at_a2;
  711. return true;
  712. }
  713. else
  714. {
  715. return false;
  716. }
  717. }
  718. else if (sides.template get<1, 0>() == 0) // b1 wrt a
  719. {
  720. if (res_a1_b1.distance <= res_a1_a2.distance
  721. && same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth))
  722. {
  723. lon = b1_lon;
  724. lat = b1_lat;
  725. dist_a1_ip = res_a1_b1.distance;
  726. dist_b1_ip = 0;
  727. ip_flag = ipi_at_b1;
  728. return true;
  729. }
  730. else
  731. {
  732. return false;
  733. }
  734. }
  735. else if (sides.template get<1, 1>() == 0) // b2 wrt a
  736. {
  737. if (res_a1_b2.distance <= res_a1_a2.distance
  738. && same_direction(res_a1_b2.azimuth, res_a1_a2.azimuth))
  739. {
  740. lon = b2_lon;
  741. lat = b2_lat;
  742. dist_a1_ip = res_a1_b2.distance;
  743. dist_b1_ip = res_b1_b2.distance;
  744. ip_flag = ipi_at_b2;
  745. return true;
  746. }
  747. else
  748. {
  749. return false;
  750. }
  751. }
  752. // At this point neither the endpoints overlaps
  753. // nor any andpoint lies on the other geodesic
  754. // So the endpoints should lie on the opposite sides of both geodesics
  755. bool const ok = formula::sjoberg_intersection<CalcT, FormulaPolicy::template inverse, Order>
  756. ::apply(a1_lon, a1_lat, a2_lon, a2_lat, res_a1_a2.azimuth,
  757. b1_lon, b1_lat, b2_lon, b2_lat, res_b1_b2.azimuth,
  758. lon, lat, spheroid);
  759. if (! ok)
  760. {
  761. return false;
  762. }
  763. typedef typename FormulaPolicy::template inverse<CalcT, true, true, false, false, false> inverse_dist_azi;
  764. typedef typename inverse_dist_azi::result_type inverse_result;
  765. inverse_result const res_a1_ip = inverse_dist_azi::apply(a1_lon, a1_lat, lon, lat, spheroid);
  766. dist_a1_ip = res_a1_ip.distance;
  767. if (! same_direction(res_a1_ip.azimuth, res_a1_a2.azimuth))
  768. {
  769. dist_a1_ip = -dist_a1_ip;
  770. }
  771. bool is_on_a = segment_ratio<CalcT>(dist_a1_ip, dist_a1_a2).on_segment();
  772. // NOTE: not fully consistent with equals_point_point() since radians are always used.
  773. bool is_on_a1 = math::equals(lon, a1_lon) && math::equals(lat, a1_lat);
  774. bool is_on_a2 = math::equals(lon, a2_lon) && math::equals(lat, a2_lat);
  775. if (! (is_on_a || is_on_a1 || is_on_a2))
  776. {
  777. return false;
  778. }
  779. inverse_result const res_b1_ip = inverse_dist_azi::apply(b1_lon, b1_lat, lon, lat, spheroid);
  780. dist_b1_ip = res_b1_ip.distance;
  781. if (! same_direction(res_b1_ip.azimuth, res_b1_b2.azimuth))
  782. {
  783. dist_b1_ip = -dist_b1_ip;
  784. }
  785. bool is_on_b = segment_ratio<CalcT>(dist_b1_ip, dist_b1_b2).on_segment();
  786. // NOTE: not fully consistent with equals_point_point() since radians are always used.
  787. bool is_on_b1 = math::equals(lon, b1_lon) && math::equals(lat, b1_lat);
  788. bool is_on_b2 = math::equals(lon, b2_lon) && math::equals(lat, b2_lat);
  789. if (! (is_on_b || is_on_b1 || is_on_b2))
  790. {
  791. return false;
  792. }
  793. ip_flag = ipi_inters;
  794. if (is_on_b1)
  795. {
  796. lon = b1_lon;
  797. lat = b1_lat;
  798. dist_b1_ip = 0;
  799. ip_flag = ipi_at_b1;
  800. }
  801. else if (is_on_b2)
  802. {
  803. lon = b2_lon;
  804. lat = b2_lat;
  805. dist_b1_ip = res_b1_b2.distance;
  806. ip_flag = ipi_at_b2;
  807. }
  808. if (is_on_a1)
  809. {
  810. lon = a1_lon;
  811. lat = a1_lat;
  812. dist_a1_ip = 0;
  813. ip_flag = ipi_at_a1;
  814. }
  815. else if (is_on_a2)
  816. {
  817. lon = a2_lon;
  818. lat = a2_lat;
  819. dist_a1_ip = res_a1_a2.distance;
  820. ip_flag = ipi_at_a2;
  821. }
  822. return true;
  823. }
  824. template <typename CalcT, typename P1, typename P2>
  825. static inline bool is_endpoint_equal(CalcT const& dist,
  826. P1 const& ai, P2 const& b1)
  827. {
  828. static CalcT const c0 = 0;
  829. return is_near(dist) && (math::equals(dist, c0) || equals_point_point(ai, b1));
  830. }
  831. template <typename CalcT>
  832. static inline bool is_near(CalcT const& dist)
  833. {
  834. // NOTE: This strongly depends on the Inverse method
  835. CalcT const small_number = CalcT(boost::is_same<CalcT, float>::value ? 0.0001 : 0.00000001);
  836. return math::abs(dist) <= small_number;
  837. }
  838. template <typename ProjCoord1, typename ProjCoord2>
  839. static inline int position_value(ProjCoord1 const& ca1,
  840. ProjCoord2 const& cb1,
  841. ProjCoord2 const& cb2)
  842. {
  843. // S1x 0 1 2 3 4
  844. // S2 |---------->
  845. return math::equals(ca1, cb1) ? 1
  846. : math::equals(ca1, cb2) ? 3
  847. : cb1 < cb2 ?
  848. ( ca1 < cb1 ? 0
  849. : ca1 > cb2 ? 4
  850. : 2 )
  851. : ( ca1 > cb1 ? 0
  852. : ca1 < cb2 ? 4
  853. : 2 );
  854. }
  855. template <typename CalcT>
  856. static inline bool same_direction(CalcT const& azimuth1, CalcT const& azimuth2)
  857. {
  858. // distance between two angles normalized to (-180, 180]
  859. CalcT const angle_diff = math::longitude_distance_signed<radian>(azimuth1, azimuth2);
  860. return math::abs(angle_diff) <= math::half_pi<CalcT>();
  861. }
  862. template <int Which>
  863. static inline void sides_reverse_segment(side_info & sides)
  864. {
  865. // names assuming segment A is reversed (Which == 0)
  866. int a1_wrt_b = sides.template get<Which, 0>();
  867. int a2_wrt_b = sides.template get<Which, 1>();
  868. std::swap(a1_wrt_b, a2_wrt_b);
  869. sides.template set<Which>(a1_wrt_b, a2_wrt_b);
  870. int b1_wrt_a = sides.template get<1 - Which, 0>();
  871. int b2_wrt_a = sides.template get<1 - Which, 1>();
  872. sides.template set<1 - Which>(-b1_wrt_a, -b2_wrt_a);
  873. }
  874. static inline void ip_flag_reverse_segment(intersection_point_flag & ip_flag,
  875. intersection_point_flag const& ipi_at_p1,
  876. intersection_point_flag const& ipi_at_p2)
  877. {
  878. ip_flag = ip_flag == ipi_at_p1 ? ipi_at_p2 :
  879. ip_flag == ipi_at_p2 ? ipi_at_p1 :
  880. ip_flag;
  881. }
  882. template <typename Point1, typename Point2>
  883. static inline bool equals_point_point(Point1 const& point1, Point2 const& point2)
  884. {
  885. return detail::equals::equals_point_point(point1, point2,
  886. point_in_point_strategy_type());
  887. }
  888. private:
  889. Spheroid m_spheroid;
  890. };
  891. }} // namespace strategy::intersection
  892. }} // namespace boost::geometry
  893. #endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP