File indexing completed on 2025-12-16 09:50:01
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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
0043
0044
0045
0046
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
0095 if ( count < 2 )
0096 {
0097
0098
0099 return true;
0100 }
0101
0102
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
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
0125 if ((m_interrupt_flags & 0xC) != 0xC
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
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
0173
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
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
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
0224
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
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
0252
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
0294
0295
0296
0297 if (is_ip_qj)
0298 {
0299 std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
0300
0301
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
0308
0309 return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy);
0310 }
0311 else
0312 {
0313
0314 return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy);
0315 }
0316 }
0317
0318
0319
0320 template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
0321 struct linear_areal
0322 {
0323
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;
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
0363
0364 update<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);
0365
0366 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0367 {
0368 return;
0369 }
0370
0371
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
0413
0414 update<exterior, interior, '2', TransposeResult>(result);
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
0437 if ( !interrupt_policy.is_boundary_found )
0438 {
0439 update<exterior, boundary, '1', TransposeResult>(result);
0440 }
0441
0442 else if ( may_update<exterior, boundary, '1', TransposeResult>(result) )
0443 {
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453 std::sort(turns.begin(), turns.end(), less_ring());
0454
0455 segment_identifier * prev_seg_id_ptr = NULL;
0456
0457 for (auto it = turns.begin() ; it != turns.end() ; )
0458 {
0459
0460 if ( prev_seg_id_ptr == NULL
0461 || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index )
0462 {
0463
0464 if ( it->operations[1].seg_id.ring_index > -1 )
0465 {
0466
0467 update<exterior, boundary, '1', TransposeResult>(result);
0468 break;
0469 }
0470
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
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
0482 update<exterior, boundary, '1', TransposeResult>(result);
0483 break;
0484 }
0485 }
0486 }
0487
0488 else
0489 {
0490
0491 if ( prev_seg_id_ptr != NULL
0492 && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index )
0493 {
0494
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
0503 has_boundary_intersection has_boundary_inters;
0504 auto next = find_next_ring(it, turns.end(), has_boundary_inters);
0505
0506
0507 if ( !has_boundary_inters.result )
0508 {
0509
0510 update<exterior, boundary, '1', TransposeResult>(result);
0511 break;
0512 }
0513
0514 else
0515 {
0516
0517 using less_t = turns::less<1, turns::less_op_areal_linear<1>, Strategy>;
0518 std::sort(it, next, less_t());
0519
0520
0521 areal_boundary_analyser<turn_type> analyser;
0522 for (auto rit = it ; rit != next ; ++rit)
0523 {
0524
0525 if ( !analyser.apply(it, rit, next, strategy) )
0526 break;
0527 }
0528
0529
0530 if ( analyser.is_union_detected )
0531 {
0532
0533 update<exterior, boundary, '1', TransposeResult>(result);
0534 break;
0535 }
0536 }
0537
0538 it = next;
0539 }
0540
0541
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
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
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
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
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& )
0652 {
0653
0654 using less_t = turns::less<0, turns::less_other_multi_index<0>, Strategy>;
0655 std::sort(first, last, less_t());
0656
0657
0658
0659
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>(),
0663 same_ip_and_multi_index());
0664
0665
0666
0667
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& )
0677 {
0678
0679
0680
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
0687
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
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
0713
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
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
0743
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 )
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
0791
0792
0793
0794
0795
0796 bool fake_enter_detected = false;
0797 if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
0798 {
0799
0800
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
0809 if ( first_in_range && m_previous_turn_ptr )
0810 {
0811
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
0818 if ( prev_back_b )
0819 {
0820 update<boundary, exterior, '0', TransposeResult>(res);
0821 }
0822 }
0823 }
0824
0825 else if ( op == overlay::operation_intersection
0826 || op == overlay::operation_continue )
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
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
0857
0858
0859
0860
0861 if ((m_previous_operation == overlay::operation_blocked
0862 && (op != overlay::operation_blocked
0863 || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index))
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
0880
0881
0882
0883
0884
0885
0886
0887
0888
0889
0890
0891
0892 if ( m_interior_detected )
0893 {
0894 BOOST_GEOMETRY_ASSERT_MSG(m_previous_turn_ptr, "non-NULL ptr expected");
0895
0896
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
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
0912 if ( prev_back_b )
0913 {
0914 update<boundary, interior, '0', TransposeResult>(res);
0915 }
0916
0917
0918
0919 }
0920 }
0921
0922 else if ( op == overlay::operation_continue )
0923 {
0924 m_interior_detected = false;
0925 }
0926 else if ( op == overlay::operation_union )
0927 {
0928
0929
0930
0931
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
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
0950 if ( op == overlay::operation_intersection
0951 || op == overlay::operation_continue )
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
0965
0966
0967
0968
0969 if ( !m_interior_detected )
0970 {
0971
0972
0973 m_interior_detected = true;
0974 m_first_interior_other_id_ptr = boost::addressof(other_id);
0975 }
0976 }
0977 }
0978 else
0979 {
0980
0981
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
0991 if ( this_b )
0992 {
0993 update<boundary, boundary, '0', TransposeResult>(res);
0994 }
0995
0996 else
0997 {
0998 update<interior, boundary, '0', TransposeResult>(res);
0999
1000
1001 if ( no_enters_detected
1002 && ! fake_enter_detected
1003 && it->operations[op_id].position != overlay::position_front )
1004 {
1005
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
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
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
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
1047
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
1056 {
1057 m_boundary_counter = 0;
1058 }
1059
1060
1061 if ( ! no_enters_detected )
1062 {
1063 if ( op_blocked
1064 && it->operations[op_id].position == overlay::position_back )
1065 {
1066
1067
1068 if (boundary_checker.is_endpoint_boundary(it->point))
1069 {
1070 update<boundary, boundary, '0', TransposeResult>(res);
1071 }
1072 }
1073
1074
1075
1076
1077
1078
1079 }
1080
1081 else
1082 {
1083 bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
1084 boundary_checker);
1085
1086 if ( this_b )
1087 {
1088 update<boundary, boundary, '0', TransposeResult>(res);
1089 }
1090
1091 else
1092 {
1093 update<interior, boundary, '0', TransposeResult>(res);
1094 }
1095
1096
1097 if ( it->operations[op_id].position != overlay::position_front )
1098 {
1099
1100
1101
1102
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
1114 m_exit_watcher.enter(*it);
1115
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
1123
1124 {
1125 m_first_from_unknown = true;
1126 }
1127 else
1128 {
1129 update<interior, exterior, '1', TransposeResult>(res);
1130 }
1131 }
1132
1133
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
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
1150
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
1166 if ( m_boundary_counter == 0
1167 || it->operations[op_id].is_collinear )
1168 {
1169
1170 m_exit_watcher.exit(*it);
1171 }
1172 }
1173
1174
1175 m_previous_turn_ptr = boost::addressof(*it);
1176
1177 m_previous_operation = op;
1178 }
1179
1180
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& ,
1190 BoundaryChecker const& boundary_checker)
1191 {
1192 boost::ignore_unused(first, last);
1193
1194
1195
1196
1197
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
1209
1210
1211 }
1212 }
1213
1214
1215
1216 if (
1217 m_previous_operation == overlay::operation_union
1218 && !m_interior_detected )
1219 {
1220
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
1232 if ( prev_back_b )
1233 {
1234 update<boundary, exterior, '0', TransposeResult>(res);
1235 }
1236 }
1237
1238 else if ( m_previous_operation == overlay::operation_intersection
1239 || m_interior_detected )
1240 {
1241
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
1254 if ( prev_back_b )
1255 {
1256 update<boundary, interior, '0', TransposeResult>(res);
1257 }
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
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
1279 if ( prev_back_b )
1280 {
1281 update<boundary, exterior, '0', TransposeResult>(res);
1282 }
1283 }
1284
1285
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
1305
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
1345
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
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
1374
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
1402
1403
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 , 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
1433 return false;
1434 }
1435 else if ( op == overlay::operation_continue )
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 }}
1488 #endif
1489
1490 }}
1491
1492 #endif