intersection.hpp 37 KB

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