linear_areal.hpp 62 KB

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