intersection.hpp 37 KB

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