File indexing completed on 2025-01-18 09:35:15
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
0015 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP
0016
0017 #include <algorithm>
0018
0019 #include <boost/core/ignore_unused.hpp>
0020 #include <boost/range/size.hpp>
0021
0022 #include <boost/geometry/core/assert.hpp>
0023
0024 #include <boost/geometry/util/condition.hpp>
0025 #include <boost/geometry/util/range.hpp>
0026
0027 #include <boost/geometry/algorithms/detail/sub_range.hpp>
0028 #include <boost/geometry/algorithms/detail/single_geometry.hpp>
0029
0030 #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
0031 #include <boost/geometry/algorithms/detail/relate/result.hpp>
0032 #include <boost/geometry/algorithms/detail/relate/turns.hpp>
0033 #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
0034 #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
0035
0036 #include <boost/geometry/geometries/helper_geometry.hpp>
0037
0038 namespace boost { namespace geometry
0039 {
0040
0041 #ifndef DOXYGEN_NO_DETAIL
0042 namespace detail { namespace relate {
0043
0044 template <typename Result, typename BoundaryChecker, bool TransposeResult>
0045 class disjoint_linestring_pred
0046 {
0047 public:
0048 disjoint_linestring_pred(Result & res,
0049 BoundaryChecker const& boundary_checker)
0050 : m_result(res)
0051 , m_boundary_checker(boundary_checker)
0052 , m_flags(0)
0053 {
0054 if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
0055 {
0056 m_flags |= 1;
0057 }
0058 if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
0059 {
0060 m_flags |= 2;
0061 }
0062 }
0063
0064 template <typename Linestring>
0065 bool operator()(Linestring const& linestring)
0066 {
0067 if ( m_flags == 3 )
0068 {
0069 return false;
0070 }
0071
0072 std::size_t const count = boost::size(linestring);
0073
0074
0075 if ( count < 2 )
0076 {
0077
0078
0079 return true;
0080 }
0081
0082
0083 if ( count == 2
0084 && equals::equals_point_point(range::front(linestring),
0085 range::back(linestring),
0086 m_boundary_checker.strategy()) )
0087 {
0088 update<interior, exterior, '0', TransposeResult>(m_result);
0089 }
0090 else
0091 {
0092 update<interior, exterior, '1', TransposeResult>(m_result);
0093 m_flags |= 1;
0094
0095
0096 if (m_flags < 2
0097 && (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
0098 || m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
0099 {
0100 update<boundary, exterior, '0', TransposeResult>(m_result);
0101 m_flags |= 2;
0102 }
0103 }
0104
0105 return m_flags != 3
0106 && ! m_result.interrupt;
0107 }
0108
0109 private:
0110 Result & m_result;
0111 BoundaryChecker const& m_boundary_checker;
0112 unsigned m_flags;
0113 };
0114
0115 template <typename Geometry1, typename Geometry2>
0116 struct linear_linear
0117 {
0118 static const bool interruption_enabled = true;
0119
0120 template <typename Result, typename Strategy>
0121 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0122 Result & result,
0123 Strategy const& strategy)
0124 {
0125 boundary_checker<Geometry1, Strategy> boundary_checker1(geometry1, strategy);
0126 boundary_checker<Geometry2, Strategy> boundary_checker2(geometry2, strategy);
0127 apply(geometry1, geometry2, boundary_checker1, boundary_checker2, result, strategy);
0128 }
0129
0130 template <typename BoundaryChecker1, typename BoundaryChecker2, typename Result, typename Strategy>
0131 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
0132 BoundaryChecker1 const& boundary_checker1,
0133 BoundaryChecker2 const& boundary_checker2,
0134 Result & result,
0135 Strategy const& strategy)
0136 {
0137
0138 update<exterior, exterior, result_dimension<Geometry1>::value>(result);
0139 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0140 return;
0141
0142
0143 using turn_type = typename turns::get_turns
0144 <
0145 Geometry1, Geometry2
0146 >::template turn_info_type<Strategy>::type;
0147 std::vector<turn_type> turns;
0148
0149 interrupt_policy_linear_linear<Result> interrupt_policy(result);
0150
0151 turns::get_turns
0152 <
0153 Geometry1,
0154 Geometry2,
0155 detail::get_turns::get_turn_info_type<Geometry1, Geometry2, turns::assign_policy<true> >
0156 >::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
0157
0158 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0159 return;
0160
0161 disjoint_linestring_pred<Result, BoundaryChecker1, false> pred1(result, boundary_checker1);
0162 for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
0163 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0164 return;
0165
0166 disjoint_linestring_pred<Result, BoundaryChecker2, true> pred2(result, boundary_checker2);
0167 for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
0168 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0169 return;
0170
0171 if ( turns.empty() )
0172 return;
0173
0174
0175
0176
0177 if ( may_update<interior, interior, '1'>(result)
0178 || may_update<interior, boundary, '0'>(result)
0179 || may_update<interior, exterior, '1'>(result)
0180 || may_update<boundary, interior, '0'>(result)
0181 || may_update<boundary, boundary, '0'>(result)
0182 || may_update<boundary, exterior, '0'>(result) )
0183 {
0184 using less_t = turns::less<0, turns::less_op_linear_linear<0>, Strategy>;
0185 std::sort(turns.begin(), turns.end(), less_t());
0186
0187 turns_analyser<turn_type, 0> analyser;
0188 analyse_each_turn(result, analyser,
0189 turns.begin(), turns.end(),
0190 geometry1, geometry2,
0191 boundary_checker1, boundary_checker2);
0192 }
0193
0194 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
0195 return;
0196
0197 if ( may_update<interior, interior, '1', true>(result)
0198 || may_update<interior, boundary, '0', true>(result)
0199 || may_update<interior, exterior, '1', true>(result)
0200 || may_update<boundary, interior, '0', true>(result)
0201 || may_update<boundary, boundary, '0', true>(result)
0202 || may_update<boundary, exterior, '0', true>(result) )
0203 {
0204 using less_t = turns::less<1, turns::less_op_linear_linear<1>, Strategy>;
0205 std::sort(turns.begin(), turns.end(), less_t());
0206
0207 turns_analyser<turn_type, 1> analyser;
0208 analyse_each_turn(result, analyser,
0209 turns.begin(), turns.end(),
0210 geometry2, geometry1,
0211 boundary_checker2, boundary_checker1);
0212 }
0213 }
0214
0215 template <typename Result>
0216 class interrupt_policy_linear_linear
0217 {
0218 public:
0219 static bool const enabled = true;
0220
0221 explicit interrupt_policy_linear_linear(Result & result)
0222 : m_result(result)
0223 {}
0224
0225
0226
0227 template <typename Range>
0228 inline bool apply(Range const& turns)
0229 {
0230 for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
0231 {
0232 if ( it->operations[0].operation == overlay::operation_intersection
0233 || it->operations[1].operation == overlay::operation_intersection )
0234 {
0235 update<interior, interior, '1'>(m_result);
0236 }
0237 else if ( ( it->operations[0].operation == overlay::operation_union
0238 || it->operations[0].operation == overlay::operation_blocked
0239 || it->operations[1].operation == overlay::operation_union
0240 || it->operations[1].operation == overlay::operation_blocked )
0241 && it->operations[0].position == overlay::position_middle
0242 && it->operations[1].position == overlay::position_middle )
0243 {
0244
0245 update<interior, interior, '0'>(m_result);
0246 }
0247 }
0248
0249 return m_result.interrupt;
0250 }
0251
0252 private:
0253 Result & m_result;
0254 };
0255
0256
0257 template <typename TurnInfo, std::size_t OpId>
0258 class turns_analyser
0259 {
0260 typedef typename TurnInfo::point_type turn_point_type;
0261
0262 static const std::size_t op_id = OpId;
0263 static const std::size_t other_op_id = (OpId + 1) % 2;
0264 static const bool transpose_result = OpId != 0;
0265
0266 public:
0267 turns_analyser()
0268 : m_previous_turn_ptr(NULL)
0269 , m_previous_operation(overlay::operation_none)
0270 , m_degenerated_turn_ptr(NULL)
0271 , m_collinear_spike_exit(false)
0272 {}
0273
0274 template <typename Result,
0275 typename TurnIt,
0276 typename Geometry,
0277 typename OtherGeometry,
0278 typename BoundaryChecker,
0279 typename OtherBoundaryChecker>
0280 void apply(Result & res, TurnIt it,
0281 Geometry const& geometry,
0282 OtherGeometry const& other_geometry,
0283 BoundaryChecker const& boundary_checker,
0284 OtherBoundaryChecker const& other_boundary_checker)
0285 {
0286 overlay::operation_type const op = it->operations[op_id].operation;
0287
0288 segment_identifier const& seg_id = it->operations[op_id].seg_id;
0289
0290 bool const first_in_range = m_seg_watcher.update(seg_id);
0291
0292 if ( op != overlay::operation_union
0293 && op != overlay::operation_intersection
0294 && op != overlay::operation_blocked )
0295 {
0296
0297 if ( op == overlay::operation_continue
0298 && it->method == overlay::method_none
0299 && m_exit_watcher.is_outside(*it)
0300
0301 )
0302 {
0303
0304
0305
0306
0307
0308
0309 handle_degenerated(res, *it,
0310 geometry, other_geometry,
0311 boundary_checker, other_boundary_checker,
0312 first_in_range);
0313
0314
0315 if ( it->operations[op_id].position == overlay::position_back )
0316 {
0317 m_previous_operation = overlay::operation_blocked;
0318 m_exit_watcher.reset_detected_exit();
0319 }
0320 }
0321
0322 return;
0323 }
0324
0325
0326 m_degenerated_turn_ptr = NULL;
0327
0328
0329 bool fake_enter_detected = false;
0330 if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
0331 {
0332
0333
0334 if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
0335 *it,
0336 boundary_checker.strategy()) )
0337 {
0338 m_exit_watcher.reset_detected_exit();
0339
0340
0341 update<interior, exterior, '1', transpose_result>(res);
0342 }
0343
0344 else if ( op == overlay::operation_intersection )
0345 {
0346 m_exit_watcher.reset_detected_exit();
0347 fake_enter_detected = true;
0348 }
0349 }
0350 else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
0351 {
0352
0353 if ( op == overlay::operation_blocked )
0354 return;
0355
0356 if ( op == overlay::operation_intersection
0357 && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(),
0358 *it,
0359 boundary_checker.strategy()) )
0360 {
0361 fake_enter_detected = true;
0362 }
0363
0364 m_exit_watcher.reset_detected_exit();
0365 }
0366
0367
0368 if ( op == overlay::operation_intersection )
0369 {
0370 bool const was_outside = m_exit_watcher.is_outside();
0371 m_exit_watcher.enter(*it);
0372
0373
0374 update<interior, interior, '1', transpose_result>(res);
0375
0376 bool const this_b = it->operations[op_id].position == overlay::position_front
0377 && is_ip_on_boundary(it->point, it->operations[op_id],
0378 boundary_checker);
0379
0380
0381
0382 if ( this_b )
0383 {
0384
0385 bool const other_b = is_ip_on_boundary(it->point, it->operations[other_op_id],
0386 other_boundary_checker);
0387
0388
0389 if ( other_b )
0390 {
0391 update<boundary, boundary, '0', transpose_result>(res);
0392 }
0393 else
0394 {
0395 update<boundary, interior, '0', transpose_result>(res);
0396 }
0397 }
0398
0399 else
0400 {
0401
0402 if ( was_outside
0403 && ! fake_enter_detected
0404 && it->operations[op_id].position != overlay::position_front
0405 && ! m_collinear_spike_exit )
0406 {
0407 update<interior, exterior, '1', transpose_result>(res);
0408
0409
0410 if ( first_in_range )
0411 {
0412 bool const front_b = boundary_checker.is_endpoint_boundary(
0413 range::front(sub_range(geometry, seg_id)));
0414
0415
0416 if ( front_b )
0417 {
0418 update<boundary, exterior, '0', transpose_result>(res);
0419 }
0420 }
0421 }
0422 }
0423
0424 m_collinear_spike_exit = false;
0425 }
0426
0427 else if ( op == overlay::operation_union || op == overlay::operation_blocked )
0428 {
0429
0430
0431
0432 bool const is_collinear = it->operations[op_id].is_collinear;
0433 bool const was_outside = m_exit_watcher.is_outside()
0434 && m_exit_watcher.get_exit_operation() == overlay::operation_none;
0435
0436
0437
0438 if ( !was_outside && is_collinear )
0439 {
0440 m_exit_watcher.exit(*it, false);
0441
0442 if ( it->operations[op_id].position != overlay::position_back )
0443 {
0444 m_collinear_spike_exit = true;
0445 }
0446 }
0447
0448 bool const op_blocked = op == overlay::operation_blocked;
0449
0450
0451
0452 if ( ! was_outside && is_collinear )
0453 {
0454 if ( op_blocked
0455 && it->operations[op_id].position == overlay::position_back )
0456 {
0457
0458
0459 if (boundary_checker.is_endpoint_boundary(it->point))
0460 {
0461
0462 bool const other_b = is_ip_on_boundary(it->point,
0463 it->operations[other_op_id],
0464 other_boundary_checker);
0465
0466 if ( other_b )
0467 {
0468 update<boundary, boundary, '0', transpose_result>(res);
0469 }
0470 else
0471 {
0472 update<boundary, interior, '0', transpose_result>(res);
0473 }
0474 }
0475 }
0476 }
0477
0478 else
0479 {
0480
0481 if ( was_outside
0482 && it->operations[op_id].position != overlay::position_front
0483 && ! m_collinear_spike_exit
0484 )
0485 {
0486 update<interior, exterior, '1', transpose_result>(res);
0487 }
0488
0489
0490 if ( it->method == overlay::method_crosses )
0491 {
0492
0493 update<interior, interior, '0', transpose_result>(res);
0494
0495
0496 if ( first_in_range )
0497 {
0498 bool const front_b = boundary_checker.is_endpoint_boundary(
0499 range::front(sub_range(geometry, seg_id)));
0500
0501
0502 if ( front_b )
0503 {
0504 update<boundary, exterior, '0', transpose_result>(res);
0505 }
0506 }
0507 }
0508
0509 else
0510 {
0511 bool const this_b = is_ip_on_boundary(it->point,
0512 it->operations[op_id],
0513 boundary_checker);
0514
0515 bool const other_b = is_ip_on_boundary(it->point,
0516 it->operations[other_op_id],
0517 other_boundary_checker);
0518
0519
0520 if ( this_b )
0521 {
0522
0523 if ( other_b )
0524 {
0525 update<boundary, boundary, '0', transpose_result>(res);
0526 }
0527 else
0528 {
0529 update<boundary, interior, '0', transpose_result>(res);
0530 }
0531 }
0532
0533 else
0534 {
0535
0536 if ( other_b )
0537 {
0538 update<interior, boundary, '0', transpose_result>(res);
0539 }
0540 else
0541 {
0542 update<interior, interior, '0', transpose_result>(res);
0543 }
0544 }
0545
0546
0547 if ( first_in_range
0548 && ( !this_b || op_blocked )
0549 && was_outside
0550 && it->operations[op_id].position != overlay::position_front
0551 && ! m_collinear_spike_exit
0552 )
0553 {
0554 bool const front_b = boundary_checker.is_endpoint_boundary(
0555 range::front(sub_range(geometry, seg_id)));
0556
0557
0558 if ( front_b )
0559 {
0560 update<boundary, exterior, '0', transpose_result>(res);
0561 }
0562 }
0563
0564 }
0565 }
0566 }
0567
0568
0569 m_previous_turn_ptr = boost::addressof(*it);
0570
0571 m_previous_operation = op;
0572 }
0573
0574
0575 template <typename Result,
0576 typename TurnIt,
0577 typename Geometry,
0578 typename OtherGeometry,
0579 typename BoundaryChecker,
0580 typename OtherBoundaryChecker>
0581 void apply(Result & res,
0582 TurnIt first, TurnIt last,
0583 Geometry const& geometry,
0584 OtherGeometry const& ,
0585 BoundaryChecker const& boundary_checker,
0586 OtherBoundaryChecker const& )
0587 {
0588 boost::ignore_unused(first, last);
0589
0590
0591
0592
0593 if (
0594 m_previous_operation == overlay::operation_union
0595 || m_degenerated_turn_ptr )
0596 {
0597 update<interior, exterior, '1', transpose_result>(res);
0598
0599 BOOST_GEOMETRY_ASSERT(first != last);
0600
0601 const TurnInfo * turn_ptr = NULL;
0602 if ( m_degenerated_turn_ptr )
0603 turn_ptr = m_degenerated_turn_ptr;
0604 else if ( m_previous_turn_ptr )
0605 turn_ptr = m_previous_turn_ptr;
0606
0607 if ( turn_ptr )
0608 {
0609 segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id;
0610
0611
0612 bool const prev_back_b = boundary_checker.is_endpoint_boundary(
0613 range::back(sub_range(geometry, prev_seg_id)));
0614
0615
0616 if ( prev_back_b )
0617 {
0618 update<boundary, exterior, '0', transpose_result>(res);
0619 }
0620 }
0621 }
0622
0623
0624
0625
0626 m_exit_watcher.reset();
0627
0628 m_previous_turn_ptr = NULL;
0629 m_previous_operation = overlay::operation_none;
0630 m_degenerated_turn_ptr = NULL;
0631
0632
0633
0634 m_collinear_spike_exit = false;
0635 }
0636
0637 template <typename Result,
0638 typename Turn,
0639 typename Geometry,
0640 typename OtherGeometry,
0641 typename BoundaryChecker,
0642 typename OtherBoundaryChecker>
0643 void handle_degenerated(Result & res,
0644 Turn const& turn,
0645 Geometry const& geometry,
0646 OtherGeometry const& other_geometry,
0647 BoundaryChecker const& boundary_checker,
0648 OtherBoundaryChecker const& other_boundary_checker,
0649 bool first_in_range)
0650 {
0651 auto const& ls1 = detail::single_geometry(geometry, turn.operations[op_id].seg_id);
0652 auto const& ls2 = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id);
0653
0654
0655
0656 if ( turn.operations[op_id].position == overlay::position_front )
0657 {
0658
0659 if ( boost::size(ls2) == 2 )
0660 {
0661 bool const front_b = boundary_checker.is_endpoint_boundary(turn.point);
0662
0663 if ( front_b )
0664 {
0665 update<boundary, interior, '0', transpose_result>(res);
0666 }
0667 else
0668 {
0669 update<interior, interior, '0', transpose_result>(res);
0670 }
0671
0672
0673 update<interior, exterior, '1', transpose_result>(res);
0674
0675 m_degenerated_turn_ptr = boost::addressof(turn);
0676 }
0677 }
0678 else if ( turn.operations[op_id].position == overlay::position_back )
0679 {
0680
0681 if ( boost::size(ls2) == 2 )
0682 {
0683 update<interior, exterior, '1', transpose_result>(res);
0684
0685 bool const back_b = boundary_checker.is_endpoint_boundary(turn.point);
0686
0687 if ( back_b )
0688 {
0689 update<boundary, interior, '0', transpose_result>(res);
0690 }
0691 else
0692 {
0693 update<interior, interior, '0', transpose_result>(res);
0694 }
0695
0696 if ( first_in_range )
0697 {
0698
0699 bool const front_b = boundary_checker.is_endpoint_boundary(
0700 range::front(ls1));
0701 if ( front_b )
0702 {
0703 update<boundary, exterior, '0', transpose_result>(res);
0704 }
0705 }
0706 }
0707 }
0708 else if ( turn.operations[op_id].position == overlay::position_middle
0709 && turn.operations[other_op_id].position == overlay::position_middle )
0710 {
0711 update<interior, interior, '0', transpose_result>(res);
0712
0713
0714
0715 bool const is_point1 = boost::size(ls1) == 2
0716 && equals::equals_point_point(range::front(ls1),
0717 range::back(ls1),
0718 boundary_checker.strategy());
0719 bool const is_point2 = boost::size(ls2) == 2
0720 && equals::equals_point_point(range::front(ls2),
0721 range::back(ls2),
0722 other_boundary_checker.strategy());
0723
0724
0725 if ( !is_point1 && is_point2 )
0726 {
0727 update<interior, exterior, '1', transpose_result>(res);
0728
0729 if ( first_in_range )
0730 {
0731
0732 bool const front_b = boundary_checker.is_endpoint_boundary(
0733 range::front(ls1));
0734 if ( front_b )
0735 {
0736 update<boundary, exterior, '0', transpose_result>(res);
0737 }
0738 }
0739
0740 m_degenerated_turn_ptr = boost::addressof(turn);
0741 }
0742 }
0743
0744
0745
0746 }
0747
0748 private:
0749 exit_watcher<TurnInfo, OpId> m_exit_watcher;
0750 segment_watcher<same_single> m_seg_watcher;
0751 const TurnInfo * m_previous_turn_ptr;
0752 overlay::operation_type m_previous_operation;
0753 const TurnInfo * m_degenerated_turn_ptr;
0754 bool m_collinear_spike_exit;
0755 };
0756
0757 template <typename Result,
0758 typename TurnIt,
0759 typename Analyser,
0760 typename Geometry,
0761 typename OtherGeometry,
0762 typename BoundaryChecker,
0763 typename OtherBoundaryChecker>
0764 static inline void analyse_each_turn(Result & res,
0765 Analyser & analyser,
0766 TurnIt first, TurnIt last,
0767 Geometry const& geometry,
0768 OtherGeometry const& other_geometry,
0769 BoundaryChecker const& boundary_checker,
0770 OtherBoundaryChecker const& other_boundary_checker)
0771 {
0772 if ( first == last )
0773 return;
0774
0775 for ( TurnIt it = first ; it != last ; ++it )
0776 {
0777 analyser.apply(res, it,
0778 geometry, other_geometry,
0779 boundary_checker, other_boundary_checker);
0780
0781 if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
0782 return;
0783 }
0784
0785 analyser.apply(res, first, last,
0786 geometry, other_geometry,
0787 boundary_checker, other_boundary_checker);
0788 }
0789 };
0790
0791 }}
0792 #endif
0793
0794 }}
0795
0796 #endif