Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:50:01

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
0005 
0006 // This file was modified by Oracle on 2013-2022.
0007 // Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
0008 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
0009 
0010 // Use, modification and distribution is subject to the Boost Software License,
0011 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0012 // http://www.boost.org/LICENSE_1_0.txt)
0013 
0014 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
0015 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
0016 
0017 #include <boost/core/ignore_unused.hpp>
0018 #include <boost/range/size.hpp>
0019 
0020 #include <boost/geometry/algorithms/detail/point_on_border.hpp>
0021 #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
0022 #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
0023 #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
0024 #include <boost/geometry/algorithms/detail/relate/turns.hpp>
0025 #include <boost/geometry/algorithms/detail/single_geometry.hpp>
0026 #include <boost/geometry/algorithms/detail/sub_range.hpp>
0027 #include <boost/geometry/algorithms/num_interior_rings.hpp>
0028 #include <boost/geometry/core/assert.hpp>
0029 #include <boost/geometry/core/topological_dimension.hpp>
0030 #include <boost/geometry/geometries/helper_geometry.hpp>
0031 #include <boost/geometry/util/constexpr.hpp>
0032 #include <boost/geometry/util/range.hpp>
0033 #include <boost/geometry/util/type_traits.hpp>
0034 #include <boost/geometry/views/detail/closed_clockwise_view.hpp>
0035 
0036 namespace boost { namespace geometry
0037 {
0038 
0039 #ifndef DOXYGEN_NO_DETAIL
0040 namespace detail { namespace relate {
0041 
0042 // WARNING!
0043 // TODO: In the worst case calling this Pred in a loop for MultiLinestring/MultiPolygon may take O(NM)
0044 // Use the rtree in this case!
0045 
0046 // may be used to set IE and BE for a Linear geometry for which no turns were generated
0047 template
0048 <
0049     typename Geometry2,
0050     typename Result,
0051     typename Strategy,
0052     typename BoundaryChecker,
0053     bool TransposeResult
0054 >
0055 class no_turns_la_linestring_pred
0056 {
0057 public:
0058     no_turns_la_linestring_pred(Geometry2 const& geometry2,
0059                                 Result & res,
0060                                 Strategy const& strategy,
0061                                 BoundaryChecker const& boundary_checker)
0062         : m_geometry2(geometry2)
0063         , m_result(res)
0064         , m_strategy(strategy)
0065         , m_boundary_checker(boundary_checker)
0066         , m_interrupt_flags(0)
0067     {
0068         if ( ! may_update<interior, interior, '1', TransposeResult>(m_result) )
0069         {
0070             m_interrupt_flags |= 1;
0071         }
0072 
0073         if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
0074         {
0075             m_interrupt_flags |= 2;
0076         }
0077 
0078         if ( ! may_update<boundary, interior, '0', TransposeResult>(m_result) )
0079         {
0080             m_interrupt_flags |= 4;
0081         }
0082 
0083         if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
0084         {
0085             m_interrupt_flags |= 8;
0086         }
0087     }
0088 
0089     template <typename Linestring>
0090     bool operator()(Linestring const& linestring)
0091     {
0092         std::size_t const count = boost::size(linestring);
0093 
0094         // invalid input
0095         if ( count < 2 )
0096         {
0097             // ignore
0098             // TODO: throw an exception?
0099             return true;
0100         }
0101 
0102         // if those flags are set nothing will change
0103         if ( m_interrupt_flags == 0xF )
0104         {
0105             return false;
0106         }
0107 
0108         int const pig = detail::within::point_in_geometry(range::front(linestring),
0109                                                           m_geometry2,
0110                                                           m_strategy);
0111         //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs");
0112 
0113         if ( pig > 0 )
0114         {
0115             update<interior, interior, '1', TransposeResult>(m_result);
0116             m_interrupt_flags |= 1;
0117         }
0118         else
0119         {
0120             update<interior, exterior, '1', TransposeResult>(m_result);
0121             m_interrupt_flags |= 2;
0122         }
0123 
0124         // check if there is a boundary
0125         if ((m_interrupt_flags & 0xC) != 0xC // if wasn't already set
0126             && (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
0127                 || m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
0128         {
0129             if ( pig > 0 )
0130             {
0131                 update<boundary, interior, '0', TransposeResult>(m_result);
0132                 m_interrupt_flags |= 4;
0133             }
0134             else
0135             {
0136                 update<boundary, exterior, '0', TransposeResult>(m_result);
0137                 m_interrupt_flags |= 8;
0138             }
0139         }
0140 
0141         return m_interrupt_flags != 0xF
0142             && ! m_result.interrupt;
0143     }
0144 
0145 private:
0146     Geometry2 const& m_geometry2;
0147     Result & m_result;
0148     Strategy const& m_strategy;
0149     BoundaryChecker const& m_boundary_checker;
0150     unsigned m_interrupt_flags;
0151 };
0152 
0153 // may be used to set EI and EB for an Areal geometry for which no turns were generated
0154 template <typename Result, bool TransposeResult>
0155 class no_turns_la_areal_pred
0156 {
0157 public:
0158     no_turns_la_areal_pred(Result & res)
0159         : m_result(res)
0160         , interrupt(! may_update<interior, exterior, '2', TransposeResult>(m_result)
0161                  && ! may_update<boundary, exterior, '1', TransposeResult>(m_result) )
0162     {}
0163 
0164     template <typename Areal>
0165     bool operator()(Areal const& areal)
0166     {
0167         if ( interrupt )
0168         {
0169             return false;
0170         }
0171 
0172         // TODO:
0173         // handle empty/invalid geometries in a different way than below?
0174 
0175         using point_type = geometry::point_type_t<Areal>;
0176         typename helper_geometry<point_type>::type pt;
0177         bool const ok = geometry::point_on_border(pt, areal);
0178 
0179         // TODO: for now ignore, later throw an exception?
0180         if ( !ok )
0181         {
0182             return true;
0183         }
0184 
0185         update<interior, exterior, '2', TransposeResult>(m_result);
0186         update<boundary, exterior, '1', TransposeResult>(m_result);
0187 
0188         return false;
0189     }
0190 
0191 private:
0192     Result & m_result;
0193     bool const interrupt;
0194 };
0195 
0196 
0197 template <typename It, typename Strategy>
0198 inline It find_next_non_duplicated(It first, It current, It last, Strategy const& strategy)
0199 {
0200     BOOST_GEOMETRY_ASSERT(current != last);
0201 
0202     It it = current;
0203     for (++it ; it != last ; ++it)
0204     {
0205         if (! equals::equals_point_point(*current, *it, strategy))
0206         {
0207             return it;
0208         }
0209     }
0210 
0211     // if not found start from the beginning
0212     for (it = first ; it != current ; ++it)
0213     {
0214         if (! equals::equals_point_point(*current, *it, strategy))
0215         {
0216             return it;
0217         }
0218     }
0219 
0220     return current;
0221 }
0222 
0223 // calculate inside or outside based on side_calc
0224 // this is simplified version of a check from equal<>
0225 template
0226 <
0227     typename Pi, typename Pj, typename Pk,
0228     typename Qi, typename Qj, typename Qk,
0229     typename Strategy
0230 >
0231 inline bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk,
0232                                         Qi const&   , Qj const& qj, Qk const& qk,
0233                                         Strategy const& strategy)
0234 {
0235     auto const side_strategy = strategy.side();
0236 
0237     int const side_pk_p = side_strategy.apply(pi, pj, pk);
0238     int const side_qk_p = side_strategy.apply(pi, pj, qk);
0239     // If they turn to same side (not opposite sides)
0240     if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
0241     {
0242         int const side_pk_q2 = side_strategy.apply(qj, qk, pk);
0243         return side_pk_q2 == -1;
0244     }
0245     else
0246     {
0247         return side_pk_p == -1;
0248     }
0249 }
0250 
0251 // check if the passed turn's segment of Linear geometry arrived
0252 // from the inside or the outside of the Areal geometry
0253 template
0254 <
0255     std::size_t OpId,
0256     typename Geometry1, typename Geometry2,
0257     typename Turn, typename Strategy
0258 >
0259 inline bool calculate_from_inside(Geometry1 const& geometry1,
0260                                   Geometry2 const& geometry2,
0261                                   Turn const& turn,
0262                                   Strategy const& strategy)
0263 {
0264     static const std::size_t op_id = OpId;
0265     static const std::size_t other_op_id = (OpId + 1) % 2;
0266 
0267     if (turn.operations[op_id].position == overlay::position_front)
0268     {
0269         return false;
0270     }
0271 
0272     auto const& range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
0273 
0274     using range2_view = detail::closed_clockwise_view<ring_type_t<Geometry2> const>;
0275     range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
0276 
0277     BOOST_GEOMETRY_ASSERT(boost::size(range1));
0278     std::size_t const s2 = boost::size(range2);
0279     BOOST_GEOMETRY_ASSERT(s2 > 2);
0280     std::size_t const seg_count2 = s2 - 1;
0281 
0282     std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
0283     std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
0284 
0285     BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
0286     BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
0287 
0288     auto const& pi = range::at(range1, p_seg_ij);
0289     auto const& qi = range::at(range2, q_seg_ij);
0290     auto const& qj = range::at(range2, q_seg_ij + 1);
0291 
0292     bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy);
0293     // TODO: test this!
0294     // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
0295     // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
0296 
0297     if (is_ip_qj)
0298     {
0299         std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
0300         // TODO: the following function should return immediately, however the worst case complexity is O(N)
0301         // It would be good to replace it with some O(1) mechanism
0302         auto qk_it = find_next_non_duplicated(boost::begin(range2),
0303                                               range::pos(range2, q_seg_jk),
0304                                               boost::end(range2),
0305                                               strategy);
0306 
0307         // Calculate sides in a different point order for P and Q
0308         // Will this sequence of points be always correct?
0309         return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy);
0310     }
0311     else
0312     {
0313         // Calculate sides with different points for P and Q
0314         return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy);
0315     }
0316 }
0317 
0318 
0319 // The implementation of an algorithm calculating relate() for L/A
0320 template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
0321 struct linear_areal
0322 {
0323     // check Linear / Areal
0324     BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 1
0325                      && topological_dimension<Geometry2>::value == 2);
0326 
0327     static const bool interruption_enabled = true;
0328 
0329     template <typename Geom1, typename Geom2, typename Strategy>
0330     struct multi_turn_info
0331         : turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
0332     {
0333         multi_turn_info() : priority(0) {}
0334         int priority; // single-geometry sorting priority
0335     };
0336 
0337     template <typename Geom1, typename Geom2, typename Strategy>
0338     struct turn_info_type
0339         : std::conditional
0340             <
0341                 util::is_multi<Geometry2>::value,
0342                 multi_turn_info<Geom1, Geom2, Strategy>,
0343                 typename turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
0344             >
0345     {};
0346 
0347     template <typename Result, typename Strategy>
0348     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0349                              Result & result,
0350                              Strategy const& strategy)
0351     {
0352         boundary_checker<Geometry1, Strategy> boundary_checker1(geometry1, strategy);
0353         apply(geometry1, geometry2, boundary_checker1, result, strategy);
0354     }
0355 
0356     template <typename BoundaryChecker1, typename Result, typename Strategy>
0357     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0358                              BoundaryChecker1 const& boundary_checker1,
0359                              Result & result,
0360                              Strategy const& strategy)
0361     {
0362         // TODO: If Areal geometry may have infinite size, change the following line:
0363 
0364         update<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T
0365 
0366         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0367         {
0368             return;
0369         }
0370 
0371         // get and analyse turns
0372         using turn_type = typename turn_info_type<Geometry1, Geometry2, Strategy>::type;
0373         std::vector<turn_type> turns;
0374 
0375         interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
0376 
0377         turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
0378         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0379         {
0380             return;
0381         }
0382 
0383         no_turns_la_linestring_pred
0384             <
0385                 Geometry2,
0386                 Result,
0387                 Strategy,
0388                 BoundaryChecker1,
0389                 TransposeResult
0390             > pred1(geometry2,
0391                     result,
0392                     strategy,
0393                     boundary_checker1);
0394         for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
0395         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0396         {
0397             return;
0398         }
0399 
0400         no_turns_la_areal_pred<Result, !TransposeResult> pred2(result);
0401         for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
0402         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0403         {
0404             return;
0405         }
0406 
0407         if ( turns.empty() )
0408         {
0409             return;
0410         }
0411 
0412         // This is set here because in the case if empty Areal geometry were passed
0413         // those shouldn't be set
0414         update<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd
0415         if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0416         {
0417             return;
0418         }
0419 
0420         {
0421             sort_dispatch(turns.begin(), turns.end(), strategy, util::is_multi<Geometry2>());
0422 
0423             turns_analyser<turn_type> analyser;
0424             analyse_each_turn(result, analyser,
0425                               turns.begin(), turns.end(),
0426                               geometry1, geometry2,
0427                               boundary_checker1,
0428                               strategy);
0429 
0430             if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0431             {
0432                 return;
0433             }
0434         }
0435 
0436         // If 'c' (insersection_boundary) was not found we know that any Ls isn't equal to one of the Rings
0437         if ( !interrupt_policy.is_boundary_found )
0438         {
0439             update<exterior, boundary, '1', TransposeResult>(result);
0440         }
0441         // Don't calculate it if it's required
0442         else if ( may_update<exterior, boundary, '1', TransposeResult>(result) )
0443         {
0444 // TODO: REVISE THIS CODE AND PROBABLY REWRITE SOME PARTS TO BE MORE HUMAN-READABLE
0445 //       IN GENERAL IT ANALYSES THE RINGS OF AREAL GEOMETRY AND DETECTS THE ONES THAT
0446 //       MAY OVERLAP THE INTERIOR OF LINEAR GEOMETRY (NO IPs OR NON-FAKE 'u' OPERATION)
0447 // NOTE: For one case std::sort may be called again to sort data by operations for data already sorted by ring index
0448 //       In the worst case scenario the complexity will be O( NlogN + R*(N/R)log(N/R) )
0449 //       So always should remain O(NlogN) -> for R==1 <-> 1(N/1)log(N/1), for R==N <-> N(N/N)log(N/N)
0450 //       Some benchmarking should probably be done to check if only one std::sort should be used
0451 
0452             // sort by multi_index and rind_index
0453             std::sort(turns.begin(), turns.end(), less_ring());
0454 
0455             segment_identifier * prev_seg_id_ptr = NULL;
0456             // for each ring
0457             for (auto it = turns.begin() ; it != turns.end() ; )
0458             {
0459                 // it's the next single geometry
0460                 if ( prev_seg_id_ptr == NULL
0461                   || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index )
0462                 {
0463                     // if the first ring has no IPs
0464                     if ( it->operations[1].seg_id.ring_index > -1 )
0465                     {
0466                         // we can be sure that the exterior overlaps the boundary
0467                         update<exterior, boundary, '1', TransposeResult>(result);
0468                         break;
0469                     }
0470                     // if there was some previous ring
0471                     if ( prev_seg_id_ptr != NULL )
0472                     {
0473                         signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
0474                         BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
0475 
0476                         // if one of the last rings of previous single geometry was ommited
0477                         if ( static_cast<std::size_t>(next_ring_index)
0478                                 < geometry::num_interior_rings(
0479                                     single_geometry(geometry2, *prev_seg_id_ptr)) )
0480                         {
0481                             // we can be sure that the exterior overlaps the boundary
0482                             update<exterior, boundary, '1', TransposeResult>(result);
0483                             break;
0484                         }
0485                     }
0486                 }
0487                 // if it's the same single geometry
0488                 else /*if ( previous_multi_index == it->operations[1].seg_id.multi_index )*/
0489                 {
0490                     // and we jumped over one of the rings
0491                     if ( prev_seg_id_ptr != NULL // just in case
0492                       && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index )
0493                     {
0494                         // we can be sure that the exterior overlaps the boundary
0495                         update<exterior, boundary, '1', TransposeResult>(result);
0496                         break;
0497                     }
0498                 }
0499 
0500                 prev_seg_id_ptr = boost::addressof(it->operations[1].seg_id);
0501 
0502                 // find the next ring first iterator and check if the analysis should be performed
0503                 has_boundary_intersection has_boundary_inters;
0504                 auto next = find_next_ring(it, turns.end(), has_boundary_inters);
0505 
0506                 // if there is no 1d overlap with the boundary
0507                 if ( !has_boundary_inters.result )
0508                 {
0509                     // we can be sure that the exterior overlaps the boundary
0510                     update<exterior, boundary, '1', TransposeResult>(result);
0511                     break;
0512                 }
0513                 // else there is 1d overlap with the boundary so we must analyse the boundary
0514                 else
0515                 {
0516                     // u, c
0517                     using less_t = turns::less<1, turns::less_op_areal_linear<1>, Strategy>;
0518                     std::sort(it, next, less_t());
0519 
0520                     // analyse
0521                     areal_boundary_analyser<turn_type> analyser;
0522                     for (auto rit = it ; rit != next ; ++rit)
0523                     {
0524                         // if the analyser requests, break the search
0525                         if ( !analyser.apply(it, rit, next, strategy) )
0526                             break;
0527                     }
0528 
0529                     // if the boundary of Areal goes out of the Linear
0530                     if ( analyser.is_union_detected )
0531                     {
0532                         // we can be sure that the boundary of Areal overlaps the exterior of Linear
0533                         update<exterior, boundary, '1', TransposeResult>(result);
0534                         break;
0535                     }
0536                 }
0537 
0538                 it = next;
0539             }
0540 
0541             // if there was some previous ring
0542             if ( prev_seg_id_ptr != NULL )
0543             {
0544                 signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
0545                 BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
0546 
0547                 // if one of the last rings of previous single geometry was ommited
0548                 if ( static_cast<std::size_t>(next_ring_index)
0549                         < geometry::num_interior_rings(
0550                             single_geometry(geometry2, *prev_seg_id_ptr)) )
0551                 {
0552                     // we can be sure that the exterior overlaps the boundary
0553                     update<exterior, boundary, '1', TransposeResult>(result);
0554                 }
0555             }
0556         }
0557     }
0558 
0559     template <typename It, typename Pred, typename Comp>
0560     static void for_each_equal_range(It first, It last, Pred pred, Comp comp)
0561     {
0562         if ( first == last )
0563         {
0564             return;
0565         }
0566 
0567         It first_equal = first;
0568         It prev = first;
0569         for ( ++first ; ; ++first, ++prev )
0570         {
0571             if ( first == last || !comp(*prev, *first) )
0572             {
0573                 pred(first_equal, first);
0574                 first_equal = first;
0575             }
0576 
0577             if ( first == last )
0578             {
0579                 break;
0580             }
0581         }
0582     }
0583 
0584     struct same_ip
0585     {
0586         template <typename Turn>
0587         bool operator()(Turn const& left, Turn const& right) const
0588         {
0589             return left.operations[0].seg_id == right.operations[0].seg_id
0590                 && left.operations[0].fraction == right.operations[0].fraction;
0591         }
0592     };
0593 
0594     struct same_ip_and_multi_index
0595     {
0596         template <typename Turn>
0597         bool operator()(Turn const& left, Turn const& right) const
0598         {
0599             return same_ip()(left, right)
0600                 && left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index;
0601         }
0602     };
0603 
0604     template <typename OpToPriority>
0605     struct set_turns_group_priority
0606     {
0607         template <typename TurnIt>
0608         void operator()(TurnIt first, TurnIt last) const
0609         {
0610             BOOST_GEOMETRY_ASSERT(first != last);
0611             static OpToPriority op_to_priority;
0612             // find the operation with the least priority
0613             int least_priority = op_to_priority(first->operations[0]);
0614             for ( TurnIt it = first + 1 ; it != last ; ++it )
0615             {
0616                 int priority = op_to_priority(it->operations[0]);
0617                 if ( priority < least_priority )
0618                     least_priority = priority;
0619             }
0620             // set the least priority for all turns of the group
0621             for ( TurnIt it = first ; it != last ; ++it )
0622             {
0623                 it->priority = least_priority;
0624             }
0625         }
0626     };
0627 
0628     template <typename SingleLess>
0629     struct sort_turns_group
0630     {
0631         struct less
0632         {
0633             template <typename Turn>
0634             bool operator()(Turn const& left, Turn const& right) const
0635             {
0636                 return left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index ?
0637                     SingleLess()(left, right) :
0638                     left.priority < right.priority;
0639             }
0640         };
0641 
0642         template <typename TurnIt>
0643         void operator()(TurnIt first, TurnIt last) const
0644         {
0645             std::sort(first, last, less());
0646         }
0647     };
0648 
0649     template <typename TurnIt, typename Strategy>
0650     static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
0651                               std::true_type const& /*is_multi*/)
0652     {
0653         // sort turns by Linear seg_id, then by fraction, then by other multi_index
0654         using less_t = turns::less<0, turns::less_other_multi_index<0>, Strategy>;
0655         std::sort(first, last, less_t());
0656 
0657         // For the same IP and multi_index - the same other's single geometry
0658         // set priorities as the least operation found for the whole single geometry
0659         // so e.g. single geometries containing 'u' will always be before those only containing 'i'
0660         typedef turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic;
0661         for_each_equal_range(first, last,
0662                              set_turns_group_priority<op_to_int_xuic>(), // least operation in xuic order
0663                              same_ip_and_multi_index()); // other's multi_index
0664 
0665         // When priorities for single geometries are set now sort turns for the same IP
0666         // if multi_index is the same sort them according to the single-less
0667         // else use priority of the whole single-geometry set earlier
0668         using single_less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
0669         for_each_equal_range(first, last,
0670                              sort_turns_group<single_less_t>(),
0671                              same_ip());
0672     }
0673 
0674     template <typename TurnIt, typename Strategy>
0675     static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
0676                               std::false_type const& /*is_multi*/)
0677     {
0678         // sort turns by Linear seg_id, then by fraction, then
0679         // for same ring id: x, u, i, c
0680         // for different ring id: c, i, u, x
0681         using less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
0682         std::sort(first, last, less_t());
0683     }
0684 
0685 
0686     // interrupt policy which may be passed to get_turns to interrupt the analysis
0687     // based on the info in the passed result/mask
0688     template <typename Areal, typename Result>
0689     class interrupt_policy_linear_areal
0690     {
0691     public:
0692         static bool const enabled = true;
0693 
0694         interrupt_policy_linear_areal(Areal const& areal, Result & result)
0695             : m_result(result), m_areal(areal)
0696             , is_boundary_found(false)
0697         {}
0698 
0699 // TODO: since we update result for some operations here, we may not do it in the analyser!
0700 
0701         template <typename Range>
0702         inline bool apply(Range const& turns)
0703         {
0704             for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
0705             {
0706                 if ( it->operations[0].operation == overlay::operation_intersection )
0707                 {
0708                     bool const no_interior_rings
0709                         = geometry::num_interior_rings(
0710                                 single_geometry(m_areal, it->operations[1].seg_id)) == 0;
0711 
0712                     // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE!
0713                     // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID)
0714                     if ( no_interior_rings )
0715                         update<interior, interior, '1', TransposeResult>(m_result);
0716                 }
0717                 else if ( it->operations[0].operation == overlay::operation_continue )
0718                 {
0719                     update<interior, boundary, '1', TransposeResult>(m_result);
0720                     is_boundary_found = true;
0721                 }
0722                 else if ( ( it->operations[0].operation == overlay::operation_union
0723                          || it->operations[0].operation == overlay::operation_blocked )
0724                        && it->operations[0].position == overlay::position_middle )
0725                 {
0726 // TODO: here we could also check the boundaries and set BB at this point
0727                     update<interior, boundary, '0', TransposeResult>(m_result);
0728                 }
0729             }
0730 
0731             return m_result.interrupt;
0732         }
0733 
0734     private:
0735         Result & m_result;
0736         Areal const& m_areal;
0737 
0738     public:
0739         bool is_boundary_found;
0740     };
0741 
0742     // This analyser should be used like Input or SinglePass Iterator
0743     // IMPORTANT! It should be called also for the end iterator - last
0744     template <typename TurnInfo>
0745     class turns_analyser
0746     {
0747         typedef typename TurnInfo::point_type turn_point_type;
0748 
0749         static const std::size_t op_id = 0;
0750         static const std::size_t other_op_id = 1;
0751 
0752     public:
0753         turns_analyser()
0754             : m_previous_turn_ptr(NULL)
0755             , m_previous_operation(overlay::operation_none)
0756             , m_boundary_counter(0)
0757             , m_interior_detected(false)
0758             , m_first_interior_other_id_ptr(NULL)
0759             , m_first_from_unknown(false)
0760             , m_first_from_unknown_boundary_detected(false)
0761         {}
0762 
0763         template <typename Result,
0764                   typename TurnIt,
0765                   typename Geometry,
0766                   typename OtherGeometry,
0767                   typename BoundaryChecker,
0768                   typename Strategy>
0769         void apply(Result & res, TurnIt it,
0770                    Geometry const& geometry,
0771                    OtherGeometry const& other_geometry,
0772                    BoundaryChecker const& boundary_checker,
0773                    Strategy const& strategy)
0774         {
0775             overlay::operation_type op = it->operations[op_id].operation;
0776 
0777             if ( op != overlay::operation_union
0778               && op != overlay::operation_intersection
0779               && op != overlay::operation_blocked
0780               && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection
0781             {
0782                 return;
0783             }
0784 
0785             segment_identifier const& seg_id = it->operations[op_id].seg_id;
0786             segment_identifier const& other_id = it->operations[other_op_id].seg_id;
0787 
0788             const bool first_in_range = m_seg_watcher.update(seg_id);
0789 
0790             // TODO: should apply() for the post-last ip be called if first_in_range ?
0791             // this would unify how last points in ranges are handled
0792             // possibly replacing parts of the code below
0793             // e.g. for is_multi and m_interior_detected
0794 
0795             // handle possible exit
0796             bool fake_enter_detected = false;
0797             if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
0798             {
0799                 // real exit point - may be multiple
0800                 // we know that we entered and now we exit
0801                 if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
0802                                                   strategy) )
0803                 {
0804                     m_exit_watcher.reset_detected_exit();
0805 
0806                     update<interior, exterior, '1', TransposeResult>(res);
0807 
0808                     // next single geometry
0809                     if ( first_in_range && m_previous_turn_ptr )
0810                     {
0811                         // NOTE: similar code is in the post-last-ip-apply()
0812                         segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
0813 
0814                         bool const prev_back_b = boundary_checker.is_endpoint_boundary(
0815                                                     range::back(sub_range(geometry, prev_seg_id)));
0816 
0817                         // if there is a boundary on the last point
0818                         if ( prev_back_b )
0819                         {
0820                             update<boundary, exterior, '0', TransposeResult>(res);
0821                         }
0822                     }
0823                 }
0824                 // fake exit point, reset state
0825                 else if ( op == overlay::operation_intersection
0826                         || op == overlay::operation_continue ) // operation_boundary
0827                 {
0828                     m_exit_watcher.reset_detected_exit();
0829                     fake_enter_detected = true;
0830                 }
0831             }
0832             else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
0833             {
0834                 // ignore multiple BLOCKs for this same single geometry1
0835                 if ( op == overlay::operation_blocked
0836                   && seg_id.multi_index == m_previous_turn_ptr->operations[op_id].seg_id.multi_index )
0837                 {
0838                     return;
0839                 }
0840 
0841                 if ( ( op == overlay::operation_intersection
0842                     || op == overlay::operation_continue )
0843                   && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
0844                                                 strategy) )
0845                 {
0846                     fake_enter_detected = true;
0847                 }
0848 
0849                 m_exit_watcher.reset_detected_exit();
0850             }
0851 
0852             if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
0853             {
0854                 if (m_first_from_unknown)
0855                 {
0856                     // For MultiPolygon many x/u operations may be generated as a first IP
0857                     // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
0858                     // then we know that the LineString is outside
0859                     // Similar with the u/u turns, if it was the first one it doesn't mean that the
0860                     // Linestring came from the exterior
0861                     if ((m_previous_operation == overlay::operation_blocked
0862                         && (op != overlay::operation_blocked // operation different than block
0863                             || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index)) // or the next single-geometry
0864                      || (m_previous_operation == overlay::operation_union
0865                         && ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it, strategy)))
0866                     {
0867                         update<interior, exterior, '1', TransposeResult>(res);
0868                         if ( m_first_from_unknown_boundary_detected )
0869                         {
0870                             update<boundary, exterior, '0', TransposeResult>(res);
0871                         }
0872 
0873                         m_first_from_unknown = false;
0874                         m_first_from_unknown_boundary_detected = false;
0875                     }
0876                 }
0877             }
0878 
0879 // NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY)
0880 // BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES
0881 // IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE
0882 // TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED
0883 
0884 // UPDATE: THEY SHOULD BE NORMALIZED NOW
0885 
0886 // TODO: THIS IS POTENTIALLY ERROREOUS!
0887 // THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS
0888 // IT WOULD GIVE WRONG RESULTS E.G.
0889 // IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u'
0890 
0891             // handle the interior overlap
0892             if ( m_interior_detected )
0893             {
0894                 BOOST_GEOMETRY_ASSERT_MSG(m_previous_turn_ptr, "non-NULL ptr expected");
0895 
0896                 // real interior overlap
0897                 if ( ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it,
0898                                                   strategy) )
0899                 {
0900                     update<interior, interior, '1', TransposeResult>(res);
0901                     m_interior_detected = false;
0902 
0903                     // new range detected - reset previous state and check the boundary
0904                     if ( first_in_range )
0905                     {
0906                         segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
0907 
0908                         bool const prev_back_b = boundary_checker.is_endpoint_boundary(
0909                                                     range::back(sub_range(geometry, prev_seg_id)));
0910 
0911                         // if there is a boundary on the last point
0912                         if ( prev_back_b )
0913                         {
0914                             update<boundary, interior, '0', TransposeResult>(res);
0915                         }
0916 
0917                         // The exit_watcher is reset below
0918                         // m_exit_watcher.reset();
0919                     }
0920                 }
0921                 // fake interior overlap
0922                 else if ( op == overlay::operation_continue )
0923                 {
0924                     m_interior_detected = false;
0925                 }
0926                 else if ( op == overlay::operation_union )
0927                 {
0928 // TODO: this probably is not a good way of handling the interiors/enters
0929 //       the solution similar to exit_watcher would be more robust
0930 //       all enters should be kept and handled.
0931 //       maybe integrate it with the exit_watcher -> enter_exit_watcher
0932                     if ( m_first_interior_other_id_ptr
0933                       && m_first_interior_other_id_ptr->multi_index == other_id.multi_index )
0934                     {
0935                         m_interior_detected = false;
0936                     }
0937                 }
0938             }
0939 
0940             // NOTE: If post-last-ip apply() was called this wouldn't be needed
0941             if ( first_in_range )
0942             {
0943                 m_exit_watcher.reset();
0944                 m_boundary_counter = 0;
0945                 m_first_from_unknown = false;
0946                 m_first_from_unknown_boundary_detected = false;
0947             }
0948 
0949             // i/u, c/u
0950             if ( op == overlay::operation_intersection
0951               || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection
0952             {
0953                 bool const first_point = first_in_range || m_first_from_unknown;
0954                 bool no_enters_detected = m_exit_watcher.is_outside();
0955                 m_exit_watcher.enter(*it);
0956 
0957                 if ( op == overlay::operation_intersection )
0958                 {
0959                     if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
0960                         --m_boundary_counter;
0961 
0962                     if ( m_boundary_counter == 0 )
0963                     {
0964                         // interiors overlaps
0965                         //update<interior, interior, '1', TransposeResult>(res);
0966 
0967 // TODO: think about the implementation of the more robust version
0968 //       this way only the first enter will be handled
0969                         if ( !m_interior_detected )
0970                         {
0971                             // don't update now
0972                             // we might enter a boundary of some other ring on the same IP
0973                             m_interior_detected = true;
0974                             m_first_interior_other_id_ptr = boost::addressof(other_id);
0975                         }
0976                     }
0977                 }
0978                 else // operation_boundary
0979                 {
0980                     // don't add to the count for all met boundaries
0981                     // only if this is the "new" boundary
0982                     if ( first_point || !it->operations[op_id].is_collinear )
0983                         ++m_boundary_counter;
0984 
0985                     update<interior, boundary, '1', TransposeResult>(res);
0986                 }
0987 
0988                 bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
0989                                                       boundary_checker);
0990                 // going inside on boundary point
0991                 if ( this_b )
0992                 {
0993                     update<boundary, boundary, '0', TransposeResult>(res);
0994                 }
0995                 // going inside on non-boundary point
0996                 else
0997                 {
0998                     update<interior, boundary, '0', TransposeResult>(res);
0999 
1000                     // if we didn't enter in the past, we were outside
1001                     if ( no_enters_detected
1002                       && ! fake_enter_detected
1003                       && it->operations[op_id].position != overlay::position_front )
1004                     {
1005 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
1006                         bool const from_inside =
1007                             first_point && calculate_from_inside<op_id>(geometry,
1008                                                                         other_geometry,
1009                                                                         *it,
1010                                                                         strategy);
1011 
1012                         if ( from_inside )
1013                             update<interior, interior, '1', TransposeResult>(res);
1014                         else
1015                             update<interior, exterior, '1', TransposeResult>(res);
1016 
1017                         // if it's the first IP then the first point is outside
1018                         if ( first_point )
1019                         {
1020                             bool const front_b = boundary_checker.is_endpoint_boundary(
1021                                                     range::front(sub_range(geometry, seg_id)));
1022 
1023                             // if there is a boundary on the first point
1024                             if ( front_b )
1025                             {
1026                                 if ( from_inside )
1027                                     update<boundary, interior, '0', TransposeResult>(res);
1028                                 else
1029                                     update<boundary, exterior, '0', TransposeResult>(res);
1030                             }
1031                         }
1032                     }
1033                 }
1034 
1035                 if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
1036                 {
1037                     m_first_from_unknown = false;
1038                     m_first_from_unknown_boundary_detected = false;
1039                 }
1040             }
1041             // u/u, x/u
1042             else if ( op == overlay::operation_union || op == overlay::operation_blocked )
1043             {
1044                 bool const op_blocked = op == overlay::operation_blocked;
1045                 bool const no_enters_detected = m_exit_watcher.is_outside()
1046 // TODO: is this condition ok?
1047 // TODO: move it into the exit_watcher?
1048                     && m_exit_watcher.get_exit_operation() == overlay::operation_none;
1049 
1050                 if ( op == overlay::operation_union )
1051                 {
1052                     if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
1053                         --m_boundary_counter;
1054                 }
1055                 else // overlay::operation_blocked
1056                 {
1057                     m_boundary_counter = 0;
1058                 }
1059 
1060                 // we're inside, possibly going out right now
1061                 if ( ! no_enters_detected )
1062                 {
1063                     if ( op_blocked
1064                       && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
1065                     {
1066                         // check if this is indeed the boundary point
1067                         // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
1068                         if (boundary_checker.is_endpoint_boundary(it->point))
1069                         {
1070                             update<boundary, boundary, '0', TransposeResult>(res);
1071                         }
1072                     }
1073                     // union, inside, but no exit -> collinear on self-intersection point
1074                     // not needed since we're already inside the boundary
1075                     /*else if ( !exit_detected )
1076                     {
1077                         update<interior, boundary, '0', TransposeResult>(res);
1078                     }*/
1079                 }
1080                 // we're outside or inside and this is the first turn
1081                 else
1082                 {
1083                     bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
1084                                                           boundary_checker);
1085                     // if current IP is on boundary of the geometry
1086                     if ( this_b )
1087                     {
1088                         update<boundary, boundary, '0', TransposeResult>(res);
1089                     }
1090                     // if current IP is not on boundary of the geometry
1091                     else
1092                     {
1093                         update<interior, boundary, '0', TransposeResult>(res);
1094                     }
1095 
1096                     // TODO: very similar code is used in the handling of intersection
1097                     if ( it->operations[op_id].position != overlay::position_front )
1098                     {
1099 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
1100                         // NOTE: this is not enough for MultiPolygon and operation_blocked
1101                         // For LS/MultiPolygon multiple x/u turns may be generated
1102                         // the first checked Polygon may be the one which LS is outside for.
1103                         bool const first_point = first_in_range || m_first_from_unknown;
1104                         bool const first_from_inside =
1105                             first_point && calculate_from_inside<op_id>(geometry,
1106                                                                         other_geometry,
1107                                                                         *it,
1108                                                                         strategy);
1109                         if ( first_from_inside )
1110                         {
1111                             update<interior, interior, '1', TransposeResult>(res);
1112 
1113                             // notify the exit_watcher that we started inside
1114                             m_exit_watcher.enter(*it);
1115                             // and reset unknown flags since we know that we started inside
1116                             m_first_from_unknown = false;
1117                             m_first_from_unknown_boundary_detected = false;
1118                         }
1119                         else
1120                         {
1121                             if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
1122                               /*&& ( op == overlay::operation_blocked
1123                                 || op == overlay::operation_union )*/ // if we're here it's u or x
1124                             {
1125                                 m_first_from_unknown = true;
1126                             }
1127                             else
1128                             {
1129                                 update<interior, exterior, '1', TransposeResult>(res);
1130                             }
1131                         }
1132 
1133                         // first IP on the last segment point - this means that the first point is outside or inside
1134                         if ( first_point && ( !this_b || op_blocked ) )
1135                         {
1136                             bool const front_b = boundary_checker.is_endpoint_boundary(
1137                                                     range::front(sub_range(geometry, seg_id)));
1138 
1139                             // if there is a boundary on the first point
1140                             if ( front_b )
1141                             {
1142                                 if ( first_from_inside )
1143                                 {
1144                                     update<boundary, interior, '0', TransposeResult>(res);
1145                                 }
1146                                 else
1147                                 {
1148                                     if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
1149                                       /*&& ( op == overlay::operation_blocked
1150                                         || op == overlay::operation_union )*/ // if we're here it's u or x
1151                                     {
1152                                         BOOST_GEOMETRY_ASSERT(m_first_from_unknown);
1153                                         m_first_from_unknown_boundary_detected = true;
1154                                     }
1155                                     else
1156                                     {
1157                                         update<boundary, exterior, '0', TransposeResult>(res);
1158                                     }
1159                                 }
1160                             }
1161                         }
1162                     }
1163                 }
1164 
1165                 // if we're going along a boundary, we exit only if the linestring was collinear
1166                 if ( m_boundary_counter == 0
1167                   || it->operations[op_id].is_collinear )
1168                 {
1169                     // notify the exit watcher about the possible exit
1170                     m_exit_watcher.exit(*it);
1171                 }
1172             }
1173 
1174             // store ref to previously analysed (valid) turn
1175             m_previous_turn_ptr = boost::addressof(*it);
1176             // and previously analysed (valid) operation
1177             m_previous_operation = op;
1178         }
1179 
1180         // it == last
1181         template <typename Result,
1182                   typename TurnIt,
1183                   typename Geometry,
1184                   typename OtherGeometry,
1185                   typename BoundaryChecker>
1186         void apply(Result & res,
1187                    TurnIt first, TurnIt last,
1188                    Geometry const& geometry,
1189                    OtherGeometry const& /*other_geometry*/,
1190                    BoundaryChecker const& boundary_checker)
1191         {
1192             boost::ignore_unused(first, last);
1193             //BOOST_GEOMETRY_ASSERT( first != last );
1194 
1195             // For MultiPolygon many x/u operations may be generated as a first IP
1196             // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
1197             // then we know that the LineString is outside
1198             if BOOST_GEOMETRY_CONSTEXPR (util::is_multi<OtherGeometry>::value)
1199             {
1200                 if (m_first_from_unknown)
1201                 {
1202                     update<interior, exterior, '1', TransposeResult>(res);
1203                     if ( m_first_from_unknown_boundary_detected )
1204                     {
1205                         update<boundary, exterior, '0', TransposeResult>(res);
1206                     }
1207 
1208                     // done below
1209                     //m_first_from_unknown = false;
1210                     //m_first_from_unknown_boundary_detected = false;
1211                 }
1212             }
1213 
1214             // here, the possible exit is the real one
1215             // we know that we entered and now we exit
1216             if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
1217                 ||*/ m_previous_operation == overlay::operation_union
1218                 && !m_interior_detected )
1219             {
1220                 // for sure
1221                 update<interior, exterior, '1', TransposeResult>(res);
1222 
1223                 BOOST_GEOMETRY_ASSERT(first != last);
1224                 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1225 
1226                 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1227 
1228                 bool const prev_back_b = boundary_checker.is_endpoint_boundary(
1229                                             range::back(sub_range(geometry, prev_seg_id)));
1230 
1231                 // if there is a boundary on the last point
1232                 if ( prev_back_b )
1233                 {
1234                     update<boundary, exterior, '0', TransposeResult>(res);
1235                 }
1236             }
1237             // we might enter some Areal and didn't go out,
1238             else if ( m_previous_operation == overlay::operation_intersection
1239                    || m_interior_detected )
1240             {
1241                 // just in case
1242                 update<interior, interior, '1', TransposeResult>(res);
1243                 m_interior_detected = false;
1244 
1245                 BOOST_GEOMETRY_ASSERT(first != last);
1246                 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1247 
1248                 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1249 
1250                 bool const prev_back_b = boundary_checker.is_endpoint_boundary(
1251                                             range::back(sub_range(geometry, prev_seg_id)));
1252 
1253                 // if there is a boundary on the last point
1254                 if ( prev_back_b )
1255                 {
1256                     update<boundary, interior, '0', TransposeResult>(res);
1257                 }
1258             }
1259 
1260             // This condition may be false if the Linestring is lying on the Polygon's collinear spike
1261             // if Polygon's spikes are not handled in get_turns() or relate() (they currently aren't)
1262             //BOOST_GEOMETRY_ASSERT_MSG(m_previous_operation != overlay::operation_continue,
1263             //                    "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)");
1264             // Currently one c/c turn is generated for the exit
1265             //   when a Linestring is going out on a collinear spike
1266             // When a Linestring is going in on a collinear spike
1267             //   the turn is not generated for the entry
1268             // So assume it's the former
1269             if ( m_previous_operation == overlay::operation_continue )
1270             {
1271                 update<interior, exterior, '1', TransposeResult>(res);
1272 
1273                 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1274 
1275                 bool const prev_back_b = boundary_checker.is_endpoint_boundary(
1276                                             range::back(sub_range(geometry, prev_seg_id)));
1277 
1278                 // if there is a boundary on the last point
1279                 if ( prev_back_b )
1280                 {
1281                     update<boundary, exterior, '0', TransposeResult>(res);
1282                 }
1283             }
1284 
1285             // Reset exit watcher before the analysis of the next Linestring
1286             m_exit_watcher.reset();
1287             m_boundary_counter = 0;
1288             m_first_from_unknown = false;
1289             m_first_from_unknown_boundary_detected = false;
1290         }
1291 
1292     private:
1293         exit_watcher<TurnInfo, op_id> m_exit_watcher;
1294         segment_watcher<same_single> m_seg_watcher;
1295         TurnInfo * m_previous_turn_ptr;
1296         overlay::operation_type m_previous_operation;
1297         unsigned m_boundary_counter;
1298         bool m_interior_detected;
1299         const segment_identifier * m_first_interior_other_id_ptr;
1300         bool m_first_from_unknown;
1301         bool m_first_from_unknown_boundary_detected;
1302     };
1303 
1304     // call analyser.apply() for each turn in range
1305     // IMPORTANT! The analyser is also called for the end iterator - last
1306     template <typename Result,
1307               typename TurnIt,
1308               typename Analyser,
1309               typename Geometry,
1310               typename OtherGeometry,
1311               typename BoundaryChecker,
1312               typename Strategy>
1313     static inline void analyse_each_turn(Result & res,
1314                                          Analyser & analyser,
1315                                          TurnIt first, TurnIt last,
1316                                          Geometry const& geometry,
1317                                          OtherGeometry const& other_geometry,
1318                                          BoundaryChecker const& boundary_checker,
1319                                          Strategy const& strategy)
1320     {
1321         if ( first == last )
1322         {
1323             return;
1324         }
1325 
1326         for ( TurnIt it = first ; it != last ; ++it )
1327         {
1328             analyser.apply(res, it,
1329                            geometry, other_geometry,
1330                            boundary_checker,
1331                            strategy);
1332 
1333             if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
1334             {
1335                 return;
1336             }
1337         }
1338 
1339         analyser.apply(res, first, last,
1340                        geometry, other_geometry,
1341                        boundary_checker);
1342     }
1343 
1344     // less comparator comparing multi_index then ring_index
1345     // may be used to sort turns by ring
1346     struct less_ring
1347     {
1348         template <typename Turn>
1349         inline bool operator()(Turn const& left, Turn const& right) const
1350         {
1351             return left.operations[1].seg_id.multi_index < right.operations[1].seg_id.multi_index
1352                 || ( left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index
1353                   && left.operations[1].seg_id.ring_index < right.operations[1].seg_id.ring_index );
1354         }
1355     };
1356 
1357     // policy/functor checking if a turn's operation is operation_continue
1358     struct has_boundary_intersection
1359     {
1360         has_boundary_intersection()
1361             : result(false) {}
1362 
1363         template <typename Turn>
1364         inline void operator()(Turn const& turn)
1365         {
1366             if ( turn.operations[1].operation == overlay::operation_continue )
1367                 result = true;
1368         }
1369 
1370         bool result;
1371     };
1372 
1373     // iterate through the range and search for the different multi_index or ring_index
1374     // also call fun for each turn in the current range
1375     template <typename It, typename Fun>
1376     static inline It find_next_ring(It first, It last, Fun & fun)
1377     {
1378         if ( first == last )
1379             return last;
1380 
1381         signed_size_type const multi_index = first->operations[1].seg_id.multi_index;
1382         signed_size_type const ring_index = first->operations[1].seg_id.ring_index;
1383 
1384         fun(*first);
1385         ++first;
1386 
1387         for ( ; first != last ; ++first )
1388         {
1389             if ( multi_index != first->operations[1].seg_id.multi_index
1390               || ring_index != first->operations[1].seg_id.ring_index )
1391             {
1392                 return first;
1393             }
1394 
1395             fun(*first);
1396         }
1397 
1398         return last;
1399     }
1400 
1401     // analyser which called for turns sorted by seg/distance/operation
1402     // checks if the boundary of Areal geometry really got out
1403     // into the exterior of Linear geometry
1404     template <typename TurnInfo>
1405     class areal_boundary_analyser
1406     {
1407     public:
1408         areal_boundary_analyser()
1409             : is_union_detected(false)
1410             , m_previous_turn_ptr(NULL)
1411         {}
1412 
1413         template <typename TurnIt, typename Strategy>
1414         bool apply(TurnIt /*first*/, TurnIt it, TurnIt last,
1415                    Strategy const& strategy)
1416         {
1417             overlay::operation_type op = it->operations[1].operation;
1418 
1419             if ( it != last )
1420             {
1421                 if ( op != overlay::operation_union
1422                   && op != overlay::operation_continue )
1423                 {
1424                     return true;
1425                 }
1426 
1427                 if ( is_union_detected )
1428                 {
1429                     BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr != NULL);
1430                     if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point, strategy) )
1431                     {
1432                         // break
1433                         return false;
1434                     }
1435                     else if ( op == overlay::operation_continue ) // operation_boundary
1436                     {
1437                         is_union_detected = false;
1438                     }
1439                 }
1440 
1441                 if ( op == overlay::operation_union )
1442                 {
1443                     is_union_detected = true;
1444                     m_previous_turn_ptr = boost::addressof(*it);
1445                 }
1446 
1447                 return true;
1448             }
1449             else
1450             {
1451                 return false;
1452             }
1453         }
1454 
1455         bool is_union_detected;
1456 
1457     private:
1458         const TurnInfo * m_previous_turn_ptr;
1459     };
1460 };
1461 
1462 template <typename Geometry1, typename Geometry2>
1463 struct areal_linear
1464 {
1465     typedef linear_areal<Geometry2, Geometry1, true> linear_areal_type;
1466 
1467     static const bool interruption_enabled = linear_areal_type::interruption_enabled;
1468 
1469     template <typename Result, typename Strategy>
1470     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
1471                              Result & result,
1472                              Strategy const& strategy)
1473     {
1474         linear_areal_type::apply(geometry2, geometry1, result, strategy);
1475     }
1476 
1477     template <typename BoundaryChecker2, typename Result, typename Strategy>
1478     static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
1479                              BoundaryChecker2 const& boundary_checker2,
1480                              Result & result,
1481                              Strategy const& strategy)
1482     {
1483         linear_areal_type::apply(geometry2, geometry1, boundary_checker2, result, strategy);
1484     }
1485 };
1486 
1487 }} // namespace detail::relate
1488 #endif // DOXYGEN_NO_DETAIL
1489 
1490 }} // namespace boost::geometry
1491 
1492 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP