intersection.hpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  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