linear_areal.hpp 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2013-2022.
  5. // Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
  12. #include <boost/core/ignore_unused.hpp>
  13. #include <boost/range/size.hpp>
  14. #include <boost/geometry/algorithms/detail/point_on_border.hpp>
  15. #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
  16. #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
  17. #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
  18. #include <boost/geometry/algorithms/detail/relate/turns.hpp>
  19. #include <boost/geometry/algorithms/detail/single_geometry.hpp>
  20. #include <boost/geometry/algorithms/detail/sub_range.hpp>
  21. #include <boost/geometry/algorithms/num_interior_rings.hpp>
  22. #include <boost/geometry/core/assert.hpp>
  23. #include <boost/geometry/core/topological_dimension.hpp>
  24. #include <boost/geometry/geometries/helper_geometry.hpp>
  25. #include <boost/geometry/util/constexpr.hpp>
  26. #include <boost/geometry/util/range.hpp>
  27. #include <boost/geometry/util/type_traits.hpp>
  28. #include <boost/geometry/views/detail/closed_clockwise_view.hpp>
  29. namespace boost { namespace geometry
  30. {
  31. #ifndef DOXYGEN_NO_DETAIL
  32. namespace detail { namespace relate {
  33. // WARNING!
  34. // TODO: In the worst case calling this Pred in a loop for MultiLinestring/MultiPolygon may take O(NM)
  35. // Use the rtree in this case!
  36. // may be used to set IE and BE for a Linear geometry for which no turns were generated
  37. template
  38. <
  39. typename Geometry2,
  40. typename Result,
  41. typename Strategy,
  42. typename BoundaryChecker,
  43. bool TransposeResult
  44. >
  45. class no_turns_la_linestring_pred
  46. {
  47. public:
  48. no_turns_la_linestring_pred(Geometry2 const& geometry2,
  49. Result & res,
  50. Strategy const& strategy,
  51. BoundaryChecker const& boundary_checker)
  52. : m_geometry2(geometry2)
  53. , m_result(res)
  54. , m_strategy(strategy)
  55. , m_boundary_checker(boundary_checker)
  56. , m_interrupt_flags(0)
  57. {
  58. if ( ! may_update<interior, interior, '1', TransposeResult>(m_result) )
  59. {
  60. m_interrupt_flags |= 1;
  61. }
  62. if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
  63. {
  64. m_interrupt_flags |= 2;
  65. }
  66. if ( ! may_update<boundary, interior, '0', TransposeResult>(m_result) )
  67. {
  68. m_interrupt_flags |= 4;
  69. }
  70. if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
  71. {
  72. m_interrupt_flags |= 8;
  73. }
  74. }
  75. template <typename Linestring>
  76. bool operator()(Linestring const& linestring)
  77. {
  78. std::size_t const count = boost::size(linestring);
  79. // invalid input
  80. if ( count < 2 )
  81. {
  82. // ignore
  83. // TODO: throw an exception?
  84. return true;
  85. }
  86. // if those flags are set nothing will change
  87. if ( m_interrupt_flags == 0xF )
  88. {
  89. return false;
  90. }
  91. int const pig = detail::within::point_in_geometry(range::front(linestring),
  92. m_geometry2,
  93. m_strategy);
  94. //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs");
  95. if ( pig > 0 )
  96. {
  97. update<interior, interior, '1', TransposeResult>(m_result);
  98. m_interrupt_flags |= 1;
  99. }
  100. else
  101. {
  102. update<interior, exterior, '1', TransposeResult>(m_result);
  103. m_interrupt_flags |= 2;
  104. }
  105. // check if there is a boundary
  106. if ((m_interrupt_flags & 0xC) != 0xC // if wasn't already set
  107. && (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
  108. || m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
  109. {
  110. if ( pig > 0 )
  111. {
  112. update<boundary, interior, '0', TransposeResult>(m_result);
  113. m_interrupt_flags |= 4;
  114. }
  115. else
  116. {
  117. update<boundary, exterior, '0', TransposeResult>(m_result);
  118. m_interrupt_flags |= 8;
  119. }
  120. }
  121. return m_interrupt_flags != 0xF
  122. && ! m_result.interrupt;
  123. }
  124. private:
  125. Geometry2 const& m_geometry2;
  126. Result & m_result;
  127. Strategy const& m_strategy;
  128. BoundaryChecker const& m_boundary_checker;
  129. unsigned m_interrupt_flags;
  130. };
  131. // may be used to set EI and EB for an Areal geometry for which no turns were generated
  132. template <typename Result, bool TransposeResult>
  133. class no_turns_la_areal_pred
  134. {
  135. public:
  136. no_turns_la_areal_pred(Result & res)
  137. : m_result(res)
  138. , interrupt(! may_update<interior, exterior, '2', TransposeResult>(m_result)
  139. && ! may_update<boundary, exterior, '1', TransposeResult>(m_result) )
  140. {}
  141. template <typename Areal>
  142. bool operator()(Areal const& areal)
  143. {
  144. if ( interrupt )
  145. {
  146. return false;
  147. }
  148. // TODO:
  149. // handle empty/invalid geometries in a different way than below?
  150. using point_type = typename geometry::point_type<Areal>::type;
  151. typename helper_geometry<point_type>::type pt;
  152. bool const ok = geometry::point_on_border(pt, areal);
  153. // TODO: for now ignore, later throw an exception?
  154. if ( !ok )
  155. {
  156. return true;
  157. }
  158. update<interior, exterior, '2', TransposeResult>(m_result);
  159. update<boundary, exterior, '1', TransposeResult>(m_result);
  160. return false;
  161. }
  162. private:
  163. Result & m_result;
  164. bool const interrupt;
  165. };
  166. template <typename It, typename Strategy>
  167. inline It find_next_non_duplicated(It first, It current, It last, Strategy const& strategy)
  168. {
  169. BOOST_GEOMETRY_ASSERT(current != last);
  170. It it = current;
  171. for (++it ; it != last ; ++it)
  172. {
  173. if (! equals::equals_point_point(*current, *it, strategy))
  174. {
  175. return it;
  176. }
  177. }
  178. // if not found start from the beginning
  179. for (it = first ; it != current ; ++it)
  180. {
  181. if (! equals::equals_point_point(*current, *it, strategy))
  182. {
  183. return it;
  184. }
  185. }
  186. return current;
  187. }
  188. // calculate inside or outside based on side_calc
  189. // this is simplified version of a check from equal<>
  190. template
  191. <
  192. typename Pi, typename Pj, typename Pk,
  193. typename Qi, typename Qj, typename Qk,
  194. typename Strategy
  195. >
  196. inline bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk,
  197. Qi const& , Qj const& qj, Qk const& qk,
  198. Strategy const& strategy)
  199. {
  200. auto const side_strategy = strategy.side();
  201. int const side_pk_p = side_strategy.apply(pi, pj, pk);
  202. int const side_qk_p = side_strategy.apply(pi, pj, qk);
  203. // If they turn to same side (not opposite sides)
  204. if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
  205. {
  206. int const side_pk_q2 = side_strategy.apply(qj, qk, pk);
  207. return side_pk_q2 == -1;
  208. }
  209. else
  210. {
  211. return side_pk_p == -1;
  212. }
  213. }
  214. // check if the passed turn's segment of Linear geometry arrived
  215. // from the inside or the outside of the Areal geometry
  216. template
  217. <
  218. std::size_t OpId,
  219. typename Geometry1, typename Geometry2,
  220. typename Turn, typename Strategy
  221. >
  222. inline bool calculate_from_inside(Geometry1 const& geometry1,
  223. Geometry2 const& geometry2,
  224. Turn const& turn,
  225. Strategy const& strategy)
  226. {
  227. static const std::size_t op_id = OpId;
  228. static const std::size_t other_op_id = (OpId + 1) % 2;
  229. if (turn.operations[op_id].position == overlay::position_front)
  230. {
  231. return false;
  232. }
  233. auto const& range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
  234. using range2_view = detail::closed_clockwise_view<typename ring_type<Geometry2>::type const>;
  235. range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
  236. BOOST_GEOMETRY_ASSERT(boost::size(range1));
  237. std::size_t const s2 = boost::size(range2);
  238. BOOST_GEOMETRY_ASSERT(s2 > 2);
  239. std::size_t const seg_count2 = s2 - 1;
  240. std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
  241. std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
  242. BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
  243. BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
  244. auto const& pi = range::at(range1, p_seg_ij);
  245. auto const& qi = range::at(range2, q_seg_ij);
  246. auto const& qj = range::at(range2, q_seg_ij + 1);
  247. bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy);
  248. // TODO: test this!
  249. // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
  250. // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
  251. if (is_ip_qj)
  252. {
  253. std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
  254. // TODO: the following function should return immediately, however the worst case complexity is O(N)
  255. // It would be good to replace it with some O(1) mechanism
  256. auto qk_it = find_next_non_duplicated(boost::begin(range2),
  257. range::pos(range2, q_seg_jk),
  258. boost::end(range2),
  259. strategy);
  260. // Calculate sides in a different point order for P and Q
  261. // Will this sequence of points be always correct?
  262. return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy);
  263. }
  264. else
  265. {
  266. // Calculate sides with different points for P and Q
  267. return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy);
  268. }
  269. }
  270. // The implementation of an algorithm calculating relate() for L/A
  271. template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
  272. struct linear_areal
  273. {
  274. // check Linear / Areal
  275. BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 1
  276. && topological_dimension<Geometry2>::value == 2);
  277. static const bool interruption_enabled = true;
  278. template <typename Geom1, typename Geom2, typename Strategy>
  279. struct multi_turn_info
  280. : turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
  281. {
  282. multi_turn_info() : priority(0) {}
  283. int priority; // single-geometry sorting priority
  284. };
  285. template <typename Geom1, typename Geom2, typename Strategy>
  286. struct turn_info_type
  287. : std::conditional
  288. <
  289. util::is_multi<Geometry2>::value,
  290. multi_turn_info<Geom1, Geom2, Strategy>,
  291. typename turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
  292. >
  293. {};
  294. template <typename Result, typename Strategy>
  295. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  296. Result & result,
  297. Strategy const& strategy)
  298. {
  299. boundary_checker<Geometry1, Strategy> boundary_checker1(geometry1, strategy);
  300. apply(geometry1, geometry2, boundary_checker1, result, strategy);
  301. }
  302. template <typename BoundaryChecker1, typename Result, typename Strategy>
  303. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  304. BoundaryChecker1 const& boundary_checker1,
  305. Result & result,
  306. Strategy const& strategy)
  307. {
  308. // TODO: If Areal geometry may have infinite size, change the following line:
  309. update<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T
  310. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  311. {
  312. return;
  313. }
  314. // get and analyse turns
  315. using turn_type = typename turn_info_type<Geometry1, Geometry2, Strategy>::type;
  316. std::vector<turn_type> turns;
  317. interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
  318. turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
  319. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  320. {
  321. return;
  322. }
  323. no_turns_la_linestring_pred
  324. <
  325. Geometry2,
  326. Result,
  327. Strategy,
  328. BoundaryChecker1,
  329. TransposeResult
  330. > pred1(geometry2,
  331. result,
  332. strategy,
  333. boundary_checker1);
  334. for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
  335. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  336. {
  337. return;
  338. }
  339. no_turns_la_areal_pred<Result, !TransposeResult> pred2(result);
  340. for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
  341. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  342. {
  343. return;
  344. }
  345. if ( turns.empty() )
  346. {
  347. return;
  348. }
  349. // This is set here because in the case if empty Areal geometry were passed
  350. // those shouldn't be set
  351. update<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd
  352. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  353. {
  354. return;
  355. }
  356. {
  357. sort_dispatch(turns.begin(), turns.end(), strategy, util::is_multi<Geometry2>());
  358. turns_analyser<turn_type> analyser;
  359. analyse_each_turn(result, analyser,
  360. turns.begin(), turns.end(),
  361. geometry1, geometry2,
  362. boundary_checker1,
  363. strategy);
  364. if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
  365. {
  366. return;
  367. }
  368. }
  369. // If 'c' (insersection_boundary) was not found we know that any Ls isn't equal to one of the Rings
  370. if ( !interrupt_policy.is_boundary_found )
  371. {
  372. update<exterior, boundary, '1', TransposeResult>(result);
  373. }
  374. // Don't calculate it if it's required
  375. else if ( may_update<exterior, boundary, '1', TransposeResult>(result) )
  376. {
  377. // TODO: REVISE THIS CODE AND PROBABLY REWRITE SOME PARTS TO BE MORE HUMAN-READABLE
  378. // IN GENERAL IT ANALYSES THE RINGS OF AREAL GEOMETRY AND DETECTS THE ONES THAT
  379. // MAY OVERLAP THE INTERIOR OF LINEAR GEOMETRY (NO IPs OR NON-FAKE 'u' OPERATION)
  380. // NOTE: For one case std::sort may be called again to sort data by operations for data already sorted by ring index
  381. // In the worst case scenario the complexity will be O( NlogN + R*(N/R)log(N/R) )
  382. // So always should remain O(NlogN) -> for R==1 <-> 1(N/1)log(N/1), for R==N <-> N(N/N)log(N/N)
  383. // Some benchmarking should probably be done to check if only one std::sort should be used
  384. // sort by multi_index and rind_index
  385. std::sort(turns.begin(), turns.end(), less_ring());
  386. segment_identifier * prev_seg_id_ptr = NULL;
  387. // for each ring
  388. for (auto it = turns.begin() ; it != turns.end() ; )
  389. {
  390. // it's the next single geometry
  391. if ( prev_seg_id_ptr == NULL
  392. || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index )
  393. {
  394. // if the first ring has no IPs
  395. if ( it->operations[1].seg_id.ring_index > -1 )
  396. {
  397. // we can be sure that the exterior overlaps the boundary
  398. update<exterior, boundary, '1', TransposeResult>(result);
  399. break;
  400. }
  401. // if there was some previous ring
  402. if ( prev_seg_id_ptr != NULL )
  403. {
  404. signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
  405. BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
  406. // if one of the last rings of previous single geometry was ommited
  407. if ( static_cast<std::size_t>(next_ring_index)
  408. < geometry::num_interior_rings(
  409. single_geometry(geometry2, *prev_seg_id_ptr)) )
  410. {
  411. // we can be sure that the exterior overlaps the boundary
  412. update<exterior, boundary, '1', TransposeResult>(result);
  413. break;
  414. }
  415. }
  416. }
  417. // if it's the same single geometry
  418. else /*if ( previous_multi_index == it->operations[1].seg_id.multi_index )*/
  419. {
  420. // and we jumped over one of the rings
  421. if ( prev_seg_id_ptr != NULL // just in case
  422. && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index )
  423. {
  424. // we can be sure that the exterior overlaps the boundary
  425. update<exterior, boundary, '1', TransposeResult>(result);
  426. break;
  427. }
  428. }
  429. prev_seg_id_ptr = boost::addressof(it->operations[1].seg_id);
  430. // find the next ring first iterator and check if the analysis should be performed
  431. has_boundary_intersection has_boundary_inters;
  432. auto next = find_next_ring(it, turns.end(), has_boundary_inters);
  433. // if there is no 1d overlap with the boundary
  434. if ( !has_boundary_inters.result )
  435. {
  436. // we can be sure that the exterior overlaps the boundary
  437. update<exterior, boundary, '1', TransposeResult>(result);
  438. break;
  439. }
  440. // else there is 1d overlap with the boundary so we must analyse the boundary
  441. else
  442. {
  443. // u, c
  444. using less_t = turns::less<1, turns::less_op_areal_linear<1>, Strategy>;
  445. std::sort(it, next, less_t());
  446. // analyse
  447. areal_boundary_analyser<turn_type> analyser;
  448. for (auto rit = it ; rit != next ; ++rit)
  449. {
  450. // if the analyser requests, break the search
  451. if ( !analyser.apply(it, rit, next, strategy) )
  452. break;
  453. }
  454. // if the boundary of Areal goes out of the Linear
  455. if ( analyser.is_union_detected )
  456. {
  457. // we can be sure that the boundary of Areal overlaps the exterior of Linear
  458. update<exterior, boundary, '1', TransposeResult>(result);
  459. break;
  460. }
  461. }
  462. it = next;
  463. }
  464. // if there was some previous ring
  465. if ( prev_seg_id_ptr != NULL )
  466. {
  467. signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
  468. BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
  469. // if one of the last rings of previous single geometry was ommited
  470. if ( static_cast<std::size_t>(next_ring_index)
  471. < geometry::num_interior_rings(
  472. single_geometry(geometry2, *prev_seg_id_ptr)) )
  473. {
  474. // we can be sure that the exterior overlaps the boundary
  475. update<exterior, boundary, '1', TransposeResult>(result);
  476. }
  477. }
  478. }
  479. }
  480. template <typename It, typename Pred, typename Comp>
  481. static void for_each_equal_range(It first, It last, Pred pred, Comp comp)
  482. {
  483. if ( first == last )
  484. {
  485. return;
  486. }
  487. It first_equal = first;
  488. It prev = first;
  489. for ( ++first ; ; ++first, ++prev )
  490. {
  491. if ( first == last || !comp(*prev, *first) )
  492. {
  493. pred(first_equal, first);
  494. first_equal = first;
  495. }
  496. if ( first == last )
  497. {
  498. break;
  499. }
  500. }
  501. }
  502. struct same_ip
  503. {
  504. template <typename Turn>
  505. bool operator()(Turn const& left, Turn const& right) const
  506. {
  507. return left.operations[0].seg_id == right.operations[0].seg_id
  508. && left.operations[0].fraction == right.operations[0].fraction;
  509. }
  510. };
  511. struct same_ip_and_multi_index
  512. {
  513. template <typename Turn>
  514. bool operator()(Turn const& left, Turn const& right) const
  515. {
  516. return same_ip()(left, right)
  517. && left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index;
  518. }
  519. };
  520. template <typename OpToPriority>
  521. struct set_turns_group_priority
  522. {
  523. template <typename TurnIt>
  524. void operator()(TurnIt first, TurnIt last) const
  525. {
  526. BOOST_GEOMETRY_ASSERT(first != last);
  527. static OpToPriority op_to_priority;
  528. // find the operation with the least priority
  529. int least_priority = op_to_priority(first->operations[0]);
  530. for ( TurnIt it = first + 1 ; it != last ; ++it )
  531. {
  532. int priority = op_to_priority(it->operations[0]);
  533. if ( priority < least_priority )
  534. least_priority = priority;
  535. }
  536. // set the least priority for all turns of the group
  537. for ( TurnIt it = first ; it != last ; ++it )
  538. {
  539. it->priority = least_priority;
  540. }
  541. }
  542. };
  543. template <typename SingleLess>
  544. struct sort_turns_group
  545. {
  546. struct less
  547. {
  548. template <typename Turn>
  549. bool operator()(Turn const& left, Turn const& right) const
  550. {
  551. return left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index ?
  552. SingleLess()(left, right) :
  553. left.priority < right.priority;
  554. }
  555. };
  556. template <typename TurnIt>
  557. void operator()(TurnIt first, TurnIt last) const
  558. {
  559. std::sort(first, last, less());
  560. }
  561. };
  562. template <typename TurnIt, typename Strategy>
  563. static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
  564. std::true_type const& /*is_multi*/)
  565. {
  566. // sort turns by Linear seg_id, then by fraction, then by other multi_index
  567. using less_t = turns::less<0, turns::less_other_multi_index<0>, Strategy>;
  568. std::sort(first, last, less_t());
  569. // For the same IP and multi_index - the same other's single geometry
  570. // set priorities as the least operation found for the whole single geometry
  571. // so e.g. single geometries containing 'u' will always be before those only containing 'i'
  572. typedef turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic;
  573. for_each_equal_range(first, last,
  574. set_turns_group_priority<op_to_int_xuic>(), // least operation in xuic order
  575. same_ip_and_multi_index()); // other's multi_index
  576. // When priorities for single geometries are set now sort turns for the same IP
  577. // if multi_index is the same sort them according to the single-less
  578. // else use priority of the whole single-geometry set earlier
  579. using single_less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
  580. for_each_equal_range(first, last,
  581. sort_turns_group<single_less_t>(),
  582. same_ip());
  583. }
  584. template <typename TurnIt, typename Strategy>
  585. static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
  586. std::false_type const& /*is_multi*/)
  587. {
  588. // sort turns by Linear seg_id, then by fraction, then
  589. // for same ring id: x, u, i, c
  590. // for different ring id: c, i, u, x
  591. using less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
  592. std::sort(first, last, less_t());
  593. }
  594. // interrupt policy which may be passed to get_turns to interrupt the analysis
  595. // based on the info in the passed result/mask
  596. template <typename Areal, typename Result>
  597. class interrupt_policy_linear_areal
  598. {
  599. public:
  600. static bool const enabled = true;
  601. interrupt_policy_linear_areal(Areal const& areal, Result & result)
  602. : m_result(result), m_areal(areal)
  603. , is_boundary_found(false)
  604. {}
  605. // TODO: since we update result for some operations here, we may not do it in the analyser!
  606. template <typename Range>
  607. inline bool apply(Range const& turns)
  608. {
  609. for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
  610. {
  611. if ( it->operations[0].operation == overlay::operation_intersection )
  612. {
  613. bool const no_interior_rings
  614. = geometry::num_interior_rings(
  615. single_geometry(m_areal, it->operations[1].seg_id)) == 0;
  616. // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE!
  617. // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID)
  618. if ( no_interior_rings )
  619. update<interior, interior, '1', TransposeResult>(m_result);
  620. }
  621. else if ( it->operations[0].operation == overlay::operation_continue )
  622. {
  623. update<interior, boundary, '1', TransposeResult>(m_result);
  624. is_boundary_found = true;
  625. }
  626. else if ( ( it->operations[0].operation == overlay::operation_union
  627. || it->operations[0].operation == overlay::operation_blocked )
  628. && it->operations[0].position == overlay::position_middle )
  629. {
  630. // TODO: here we could also check the boundaries and set BB at this point
  631. update<interior, boundary, '0', TransposeResult>(m_result);
  632. }
  633. }
  634. return m_result.interrupt;
  635. }
  636. private:
  637. Result & m_result;
  638. Areal const& m_areal;
  639. public:
  640. bool is_boundary_found;
  641. };
  642. // This analyser should be used like Input or SinglePass Iterator
  643. // IMPORTANT! It should be called also for the end iterator - last
  644. template <typename TurnInfo>
  645. class turns_analyser
  646. {
  647. typedef typename TurnInfo::point_type turn_point_type;
  648. static const std::size_t op_id = 0;
  649. static const std::size_t other_op_id = 1;
  650. public:
  651. turns_analyser()
  652. : m_previous_turn_ptr(NULL)
  653. , m_previous_operation(overlay::operation_none)
  654. , m_boundary_counter(0)
  655. , m_interior_detected(false)
  656. , m_first_interior_other_id_ptr(NULL)
  657. , m_first_from_unknown(false)
  658. , m_first_from_unknown_boundary_detected(false)
  659. {}
  660. template <typename Result,
  661. typename TurnIt,
  662. typename Geometry,
  663. typename OtherGeometry,
  664. typename BoundaryChecker,
  665. typename Strategy>
  666. void apply(Result & res, TurnIt it,
  667. Geometry const& geometry,
  668. OtherGeometry const& other_geometry,
  669. BoundaryChecker const& boundary_checker,
  670. Strategy const& strategy)
  671. {
  672. overlay::operation_type op = it->operations[op_id].operation;
  673. if ( op != overlay::operation_union
  674. && op != overlay::operation_intersection
  675. && op != overlay::operation_blocked
  676. && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection
  677. {
  678. return;
  679. }
  680. segment_identifier const& seg_id = it->operations[op_id].seg_id;
  681. segment_identifier const& other_id = it->operations[other_op_id].seg_id;
  682. const bool first_in_range = m_seg_watcher.update(seg_id);
  683. // TODO: should apply() for the post-last ip be called if first_in_range ?
  684. // this would unify how last points in ranges are handled
  685. // possibly replacing parts of the code below
  686. // e.g. for is_multi and m_interior_detected
  687. // handle possible exit
  688. bool fake_enter_detected = false;
  689. if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
  690. {
  691. // real exit point - may be multiple
  692. // we know that we entered and now we exit
  693. if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
  694. strategy) )
  695. {
  696. m_exit_watcher.reset_detected_exit();
  697. update<interior, exterior, '1', TransposeResult>(res);
  698. // next single geometry
  699. if ( first_in_range && m_previous_turn_ptr )
  700. {
  701. // NOTE: similar code is in the post-last-ip-apply()
  702. segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
  703. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  704. range::back(sub_range(geometry, prev_seg_id)));
  705. // if there is a boundary on the last point
  706. if ( prev_back_b )
  707. {
  708. update<boundary, exterior, '0', TransposeResult>(res);
  709. }
  710. }
  711. }
  712. // fake exit point, reset state
  713. else if ( op == overlay::operation_intersection
  714. || op == overlay::operation_continue ) // operation_boundary
  715. {
  716. m_exit_watcher.reset_detected_exit();
  717. fake_enter_detected = true;
  718. }
  719. }
  720. else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
  721. {
  722. // ignore multiple BLOCKs for this same single geometry1
  723. if ( op == overlay::operation_blocked
  724. && seg_id.multi_index == m_previous_turn_ptr->operations[op_id].seg_id.multi_index )
  725. {
  726. return;
  727. }
  728. if ( ( op == overlay::operation_intersection
  729. || op == overlay::operation_continue )
  730. && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
  731. strategy) )
  732. {
  733. fake_enter_detected = true;
  734. }
  735. m_exit_watcher.reset_detected_exit();
  736. }
  737. if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
  738. {
  739. if (m_first_from_unknown)
  740. {
  741. // For MultiPolygon many x/u operations may be generated as a first IP
  742. // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
  743. // then we know that the LineString is outside
  744. // Similar with the u/u turns, if it was the first one it doesn't mean that the
  745. // Linestring came from the exterior
  746. if ((m_previous_operation == overlay::operation_blocked
  747. && (op != overlay::operation_blocked // operation different than block
  748. || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index)) // or the next single-geometry
  749. || (m_previous_operation == overlay::operation_union
  750. && ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it, strategy)))
  751. {
  752. update<interior, exterior, '1', TransposeResult>(res);
  753. if ( m_first_from_unknown_boundary_detected )
  754. {
  755. update<boundary, exterior, '0', TransposeResult>(res);
  756. }
  757. m_first_from_unknown = false;
  758. m_first_from_unknown_boundary_detected = false;
  759. }
  760. }
  761. }
  762. // NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY)
  763. // BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES
  764. // IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE
  765. // TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED
  766. // UPDATE: THEY SHOULD BE NORMALIZED NOW
  767. // TODO: THIS IS POTENTIALLY ERROREOUS!
  768. // THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS
  769. // IT WOULD GIVE WRONG RESULTS E.G.
  770. // IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u'
  771. // handle the interior overlap
  772. if ( m_interior_detected )
  773. {
  774. BOOST_GEOMETRY_ASSERT_MSG(m_previous_turn_ptr, "non-NULL ptr expected");
  775. // real interior overlap
  776. if ( ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it,
  777. strategy) )
  778. {
  779. update<interior, interior, '1', TransposeResult>(res);
  780. m_interior_detected = false;
  781. // new range detected - reset previous state and check the boundary
  782. if ( first_in_range )
  783. {
  784. segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
  785. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  786. range::back(sub_range(geometry, prev_seg_id)));
  787. // if there is a boundary on the last point
  788. if ( prev_back_b )
  789. {
  790. update<boundary, interior, '0', TransposeResult>(res);
  791. }
  792. // The exit_watcher is reset below
  793. // m_exit_watcher.reset();
  794. }
  795. }
  796. // fake interior overlap
  797. else if ( op == overlay::operation_continue )
  798. {
  799. m_interior_detected = false;
  800. }
  801. else if ( op == overlay::operation_union )
  802. {
  803. // TODO: this probably is not a good way of handling the interiors/enters
  804. // the solution similar to exit_watcher would be more robust
  805. // all enters should be kept and handled.
  806. // maybe integrate it with the exit_watcher -> enter_exit_watcher
  807. if ( m_first_interior_other_id_ptr
  808. && m_first_interior_other_id_ptr->multi_index == other_id.multi_index )
  809. {
  810. m_interior_detected = false;
  811. }
  812. }
  813. }
  814. // NOTE: If post-last-ip apply() was called this wouldn't be needed
  815. if ( first_in_range )
  816. {
  817. m_exit_watcher.reset();
  818. m_boundary_counter = 0;
  819. m_first_from_unknown = false;
  820. m_first_from_unknown_boundary_detected = false;
  821. }
  822. // i/u, c/u
  823. if ( op == overlay::operation_intersection
  824. || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection
  825. {
  826. bool const first_point = first_in_range || m_first_from_unknown;
  827. bool no_enters_detected = m_exit_watcher.is_outside();
  828. m_exit_watcher.enter(*it);
  829. if ( op == overlay::operation_intersection )
  830. {
  831. if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
  832. --m_boundary_counter;
  833. if ( m_boundary_counter == 0 )
  834. {
  835. // interiors overlaps
  836. //update<interior, interior, '1', TransposeResult>(res);
  837. // TODO: think about the implementation of the more robust version
  838. // this way only the first enter will be handled
  839. if ( !m_interior_detected )
  840. {
  841. // don't update now
  842. // we might enter a boundary of some other ring on the same IP
  843. m_interior_detected = true;
  844. m_first_interior_other_id_ptr = boost::addressof(other_id);
  845. }
  846. }
  847. }
  848. else // operation_boundary
  849. {
  850. // don't add to the count for all met boundaries
  851. // only if this is the "new" boundary
  852. if ( first_point || !it->operations[op_id].is_collinear )
  853. ++m_boundary_counter;
  854. update<interior, boundary, '1', TransposeResult>(res);
  855. }
  856. bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
  857. boundary_checker);
  858. // going inside on boundary point
  859. if ( this_b )
  860. {
  861. update<boundary, boundary, '0', TransposeResult>(res);
  862. }
  863. // going inside on non-boundary point
  864. else
  865. {
  866. update<interior, boundary, '0', TransposeResult>(res);
  867. // if we didn't enter in the past, we were outside
  868. if ( no_enters_detected
  869. && ! fake_enter_detected
  870. && it->operations[op_id].position != overlay::position_front )
  871. {
  872. // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
  873. bool const from_inside =
  874. first_point && calculate_from_inside<op_id>(geometry,
  875. other_geometry,
  876. *it,
  877. strategy);
  878. if ( from_inside )
  879. update<interior, interior, '1', TransposeResult>(res);
  880. else
  881. update<interior, exterior, '1', TransposeResult>(res);
  882. // if it's the first IP then the first point is outside
  883. if ( first_point )
  884. {
  885. bool const front_b = boundary_checker.is_endpoint_boundary(
  886. range::front(sub_range(geometry, seg_id)));
  887. // if there is a boundary on the first point
  888. if ( front_b )
  889. {
  890. if ( from_inside )
  891. update<boundary, interior, '0', TransposeResult>(res);
  892. else
  893. update<boundary, exterior, '0', TransposeResult>(res);
  894. }
  895. }
  896. }
  897. }
  898. if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
  899. {
  900. m_first_from_unknown = false;
  901. m_first_from_unknown_boundary_detected = false;
  902. }
  903. }
  904. // u/u, x/u
  905. else if ( op == overlay::operation_union || op == overlay::operation_blocked )
  906. {
  907. bool const op_blocked = op == overlay::operation_blocked;
  908. bool const no_enters_detected = m_exit_watcher.is_outside()
  909. // TODO: is this condition ok?
  910. // TODO: move it into the exit_watcher?
  911. && m_exit_watcher.get_exit_operation() == overlay::operation_none;
  912. if ( op == overlay::operation_union )
  913. {
  914. if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
  915. --m_boundary_counter;
  916. }
  917. else // overlay::operation_blocked
  918. {
  919. m_boundary_counter = 0;
  920. }
  921. // we're inside, possibly going out right now
  922. if ( ! no_enters_detected )
  923. {
  924. if ( op_blocked
  925. && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
  926. {
  927. // check if this is indeed the boundary point
  928. // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
  929. if (boundary_checker.is_endpoint_boundary(it->point))
  930. {
  931. update<boundary, boundary, '0', TransposeResult>(res);
  932. }
  933. }
  934. // union, inside, but no exit -> collinear on self-intersection point
  935. // not needed since we're already inside the boundary
  936. /*else if ( !exit_detected )
  937. {
  938. update<interior, boundary, '0', TransposeResult>(res);
  939. }*/
  940. }
  941. // we're outside or inside and this is the first turn
  942. else
  943. {
  944. bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
  945. boundary_checker);
  946. // if current IP is on boundary of the geometry
  947. if ( this_b )
  948. {
  949. update<boundary, boundary, '0', TransposeResult>(res);
  950. }
  951. // if current IP is not on boundary of the geometry
  952. else
  953. {
  954. update<interior, boundary, '0', TransposeResult>(res);
  955. }
  956. // TODO: very similar code is used in the handling of intersection
  957. if ( it->operations[op_id].position != overlay::position_front )
  958. {
  959. // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
  960. // NOTE: this is not enough for MultiPolygon and operation_blocked
  961. // For LS/MultiPolygon multiple x/u turns may be generated
  962. // the first checked Polygon may be the one which LS is outside for.
  963. bool const first_point = first_in_range || m_first_from_unknown;
  964. bool const first_from_inside =
  965. first_point && calculate_from_inside<op_id>(geometry,
  966. other_geometry,
  967. *it,
  968. strategy);
  969. if ( first_from_inside )
  970. {
  971. update<interior, interior, '1', TransposeResult>(res);
  972. // notify the exit_watcher that we started inside
  973. m_exit_watcher.enter(*it);
  974. // and reset unknown flags since we know that we started inside
  975. m_first_from_unknown = false;
  976. m_first_from_unknown_boundary_detected = false;
  977. }
  978. else
  979. {
  980. if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
  981. /*&& ( op == overlay::operation_blocked
  982. || op == overlay::operation_union )*/ // if we're here it's u or x
  983. {
  984. m_first_from_unknown = true;
  985. }
  986. else
  987. {
  988. update<interior, exterior, '1', TransposeResult>(res);
  989. }
  990. }
  991. // first IP on the last segment point - this means that the first point is outside or inside
  992. if ( first_point && ( !this_b || op_blocked ) )
  993. {
  994. bool const front_b = boundary_checker.is_endpoint_boundary(
  995. range::front(sub_range(geometry, seg_id)));
  996. // if there is a boundary on the first point
  997. if ( front_b )
  998. {
  999. if ( first_from_inside )
  1000. {
  1001. update<boundary, interior, '0', TransposeResult>(res);
  1002. }
  1003. else
  1004. {
  1005. if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
  1006. /*&& ( op == overlay::operation_blocked
  1007. || op == overlay::operation_union )*/ // if we're here it's u or x
  1008. {
  1009. BOOST_GEOMETRY_ASSERT(m_first_from_unknown);
  1010. m_first_from_unknown_boundary_detected = true;
  1011. }
  1012. else
  1013. {
  1014. update<boundary, exterior, '0', TransposeResult>(res);
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. }
  1021. // if we're going along a boundary, we exit only if the linestring was collinear
  1022. if ( m_boundary_counter == 0
  1023. || it->operations[op_id].is_collinear )
  1024. {
  1025. // notify the exit watcher about the possible exit
  1026. m_exit_watcher.exit(*it);
  1027. }
  1028. }
  1029. // store ref to previously analysed (valid) turn
  1030. m_previous_turn_ptr = boost::addressof(*it);
  1031. // and previously analysed (valid) operation
  1032. m_previous_operation = op;
  1033. }
  1034. // it == last
  1035. template <typename Result,
  1036. typename TurnIt,
  1037. typename Geometry,
  1038. typename OtherGeometry,
  1039. typename BoundaryChecker>
  1040. void apply(Result & res,
  1041. TurnIt first, TurnIt last,
  1042. Geometry const& geometry,
  1043. OtherGeometry const& /*other_geometry*/,
  1044. BoundaryChecker const& boundary_checker)
  1045. {
  1046. boost::ignore_unused(first, last);
  1047. //BOOST_GEOMETRY_ASSERT( first != last );
  1048. // For MultiPolygon many x/u operations may be generated as a first IP
  1049. // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
  1050. // then we know that the LineString is outside
  1051. if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
  1052. {
  1053. if (m_first_from_unknown)
  1054. {
  1055. update<interior, exterior, '1', TransposeResult>(res);
  1056. if ( m_first_from_unknown_boundary_detected )
  1057. {
  1058. update<boundary, exterior, '0', TransposeResult>(res);
  1059. }
  1060. // done below
  1061. //m_first_from_unknown = false;
  1062. //m_first_from_unknown_boundary_detected = false;
  1063. }
  1064. }
  1065. // here, the possible exit is the real one
  1066. // we know that we entered and now we exit
  1067. if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
  1068. ||*/ m_previous_operation == overlay::operation_union
  1069. && !m_interior_detected )
  1070. {
  1071. // for sure
  1072. update<interior, exterior, '1', TransposeResult>(res);
  1073. BOOST_GEOMETRY_ASSERT(first != last);
  1074. BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
  1075. segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
  1076. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  1077. range::back(sub_range(geometry, prev_seg_id)));
  1078. // if there is a boundary on the last point
  1079. if ( prev_back_b )
  1080. {
  1081. update<boundary, exterior, '0', TransposeResult>(res);
  1082. }
  1083. }
  1084. // we might enter some Areal and didn't go out,
  1085. else if ( m_previous_operation == overlay::operation_intersection
  1086. || m_interior_detected )
  1087. {
  1088. // just in case
  1089. update<interior, interior, '1', TransposeResult>(res);
  1090. m_interior_detected = false;
  1091. BOOST_GEOMETRY_ASSERT(first != last);
  1092. BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
  1093. segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
  1094. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  1095. range::back(sub_range(geometry, prev_seg_id)));
  1096. // if there is a boundary on the last point
  1097. if ( prev_back_b )
  1098. {
  1099. update<boundary, interior, '0', TransposeResult>(res);
  1100. }
  1101. }
  1102. // This condition may be false if the Linestring is lying on the Polygon's collinear spike
  1103. // if Polygon's spikes are not handled in get_turns() or relate() (they currently aren't)
  1104. //BOOST_GEOMETRY_ASSERT_MSG(m_previous_operation != overlay::operation_continue,
  1105. // "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)");
  1106. // Currently one c/c turn is generated for the exit
  1107. // when a Linestring is going out on a collinear spike
  1108. // When a Linestring is going in on a collinear spike
  1109. // the turn is not generated for the entry
  1110. // So assume it's the former
  1111. if ( m_previous_operation == overlay::operation_continue )
  1112. {
  1113. update<interior, exterior, '1', TransposeResult>(res);
  1114. segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
  1115. bool const prev_back_b = boundary_checker.is_endpoint_boundary(
  1116. range::back(sub_range(geometry, prev_seg_id)));
  1117. // if there is a boundary on the last point
  1118. if ( prev_back_b )
  1119. {
  1120. update<boundary, exterior, '0', TransposeResult>(res);
  1121. }
  1122. }
  1123. // Reset exit watcher before the analysis of the next Linestring
  1124. m_exit_watcher.reset();
  1125. m_boundary_counter = 0;
  1126. m_first_from_unknown = false;
  1127. m_first_from_unknown_boundary_detected = false;
  1128. }
  1129. private:
  1130. exit_watcher<TurnInfo, op_id> m_exit_watcher;
  1131. segment_watcher<same_single> m_seg_watcher;
  1132. TurnInfo * m_previous_turn_ptr;
  1133. overlay::operation_type m_previous_operation;
  1134. unsigned m_boundary_counter;
  1135. bool m_interior_detected;
  1136. const segment_identifier * m_first_interior_other_id_ptr;
  1137. bool m_first_from_unknown;
  1138. bool m_first_from_unknown_boundary_detected;
  1139. };
  1140. // call analyser.apply() for each turn in range
  1141. // IMPORTANT! The analyser is also called for the end iterator - last
  1142. template <typename Result,
  1143. typename TurnIt,
  1144. typename Analyser,
  1145. typename Geometry,
  1146. typename OtherGeometry,
  1147. typename BoundaryChecker,
  1148. typename Strategy>
  1149. static inline void analyse_each_turn(Result & res,
  1150. Analyser & analyser,
  1151. TurnIt first, TurnIt last,
  1152. Geometry const& geometry,
  1153. OtherGeometry const& other_geometry,
  1154. BoundaryChecker const& boundary_checker,
  1155. Strategy const& strategy)
  1156. {
  1157. if ( first == last )
  1158. {
  1159. return;
  1160. }
  1161. for ( TurnIt it = first ; it != last ; ++it )
  1162. {
  1163. analyser.apply(res, it,
  1164. geometry, other_geometry,
  1165. boundary_checker,
  1166. strategy);
  1167. if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
  1168. {
  1169. return;
  1170. }
  1171. }
  1172. analyser.apply(res, first, last,
  1173. geometry, other_geometry,
  1174. boundary_checker);
  1175. }
  1176. // less comparator comparing multi_index then ring_index
  1177. // may be used to sort turns by ring
  1178. struct less_ring
  1179. {
  1180. template <typename Turn>
  1181. inline bool operator()(Turn const& left, Turn const& right) const
  1182. {
  1183. return left.operations[1].seg_id.multi_index < right.operations[1].seg_id.multi_index
  1184. || ( left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index
  1185. && left.operations[1].seg_id.ring_index < right.operations[1].seg_id.ring_index );
  1186. }
  1187. };
  1188. // policy/functor checking if a turn's operation is operation_continue
  1189. struct has_boundary_intersection
  1190. {
  1191. has_boundary_intersection()
  1192. : result(false) {}
  1193. template <typename Turn>
  1194. inline void operator()(Turn const& turn)
  1195. {
  1196. if ( turn.operations[1].operation == overlay::operation_continue )
  1197. result = true;
  1198. }
  1199. bool result;
  1200. };
  1201. // iterate through the range and search for the different multi_index or ring_index
  1202. // also call fun for each turn in the current range
  1203. template <typename It, typename Fun>
  1204. static inline It find_next_ring(It first, It last, Fun & fun)
  1205. {
  1206. if ( first == last )
  1207. return last;
  1208. signed_size_type const multi_index = first->operations[1].seg_id.multi_index;
  1209. signed_size_type const ring_index = first->operations[1].seg_id.ring_index;
  1210. fun(*first);
  1211. ++first;
  1212. for ( ; first != last ; ++first )
  1213. {
  1214. if ( multi_index != first->operations[1].seg_id.multi_index
  1215. || ring_index != first->operations[1].seg_id.ring_index )
  1216. {
  1217. return first;
  1218. }
  1219. fun(*first);
  1220. }
  1221. return last;
  1222. }
  1223. // analyser which called for turns sorted by seg/distance/operation
  1224. // checks if the boundary of Areal geometry really got out
  1225. // into the exterior of Linear geometry
  1226. template <typename TurnInfo>
  1227. class areal_boundary_analyser
  1228. {
  1229. public:
  1230. areal_boundary_analyser()
  1231. : is_union_detected(false)
  1232. , m_previous_turn_ptr(NULL)
  1233. {}
  1234. template <typename TurnIt, typename Strategy>
  1235. bool apply(TurnIt /*first*/, TurnIt it, TurnIt last,
  1236. Strategy const& strategy)
  1237. {
  1238. overlay::operation_type op = it->operations[1].operation;
  1239. if ( it != last )
  1240. {
  1241. if ( op != overlay::operation_union
  1242. && op != overlay::operation_continue )
  1243. {
  1244. return true;
  1245. }
  1246. if ( is_union_detected )
  1247. {
  1248. BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr != NULL);
  1249. if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point, strategy) )
  1250. {
  1251. // break
  1252. return false;
  1253. }
  1254. else if ( op == overlay::operation_continue ) // operation_boundary
  1255. {
  1256. is_union_detected = false;
  1257. }
  1258. }
  1259. if ( op == overlay::operation_union )
  1260. {
  1261. is_union_detected = true;
  1262. m_previous_turn_ptr = boost::addressof(*it);
  1263. }
  1264. return true;
  1265. }
  1266. else
  1267. {
  1268. return false;
  1269. }
  1270. }
  1271. bool is_union_detected;
  1272. private:
  1273. const TurnInfo * m_previous_turn_ptr;
  1274. };
  1275. };
  1276. template <typename Geometry1, typename Geometry2>
  1277. struct areal_linear
  1278. {
  1279. typedef linear_areal<Geometry2, Geometry1, true> linear_areal_type;
  1280. static const bool interruption_enabled = linear_areal_type::interruption_enabled;
  1281. template <typename Result, typename Strategy>
  1282. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  1283. Result & result,
  1284. Strategy const& strategy)
  1285. {
  1286. linear_areal_type::apply(geometry2, geometry1, result, strategy);
  1287. }
  1288. template <typename BoundaryChecker2, typename Result, typename Strategy>
  1289. static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
  1290. BoundaryChecker2 const& boundary_checker2,
  1291. Result & result,
  1292. Strategy const& strategy)
  1293. {
  1294. linear_areal_type::apply(geometry2, geometry1, boundary_checker2, result, strategy);
  1295. }
  1296. };
  1297. }} // namespace detail::relate
  1298. #endif // DOXYGEN_NO_DETAIL
  1299. }} // namespace boost::geometry
  1300. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP