Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:35:14

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