File indexing completed on 2025-09-17 08:30:39
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
0016 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_SWITCH_DETECTOR_HPP
0017
0018 #include <boost/range/value_type.hpp>
0019
0020 #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
0021 #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
0022 #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
0023 #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
0024 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
0025
0026 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
0027 #include <boost/geometry/core/access.hpp>
0028 #endif
0029
0030 #include <boost/geometry/util/constexpr.hpp>
0031
0032 #include <cstddef>
0033 #include <map>
0034
0035 namespace boost { namespace geometry
0036 {
0037
0038 #ifndef DOXYGEN_NO_DETAIL
0039 namespace detail { namespace overlay
0040 {
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078 template
0079 <
0080 bool Reverse1,
0081 bool Reverse2,
0082 overlay_type OverlayType,
0083 typename Geometry1,
0084 typename Geometry2,
0085 typename Turns,
0086 typename Clusters,
0087 typename Visitor
0088 >
0089 struct traversal_switch_detector
0090 {
0091 static const operation_type target_operation
0092 = operation_from_overlay<OverlayType>::value;
0093
0094 enum isolation_type
0095 {
0096 isolation_no = 0,
0097 isolation_yes = 1,
0098 isolation_multiple = 2
0099 };
0100
0101 using turn_type = typename boost::range_value<Turns>::type;
0102 using set_type= std::set<signed_size_type>;
0103
0104
0105
0106 struct merged_ring_properties
0107 {
0108 signed_size_type region_id = -1;
0109 set_type turn_indices;
0110 };
0111
0112 struct connection_properties
0113 {
0114 std::size_t count = 0;
0115
0116 set_type unique_turn_ids;
0117 };
0118
0119
0120 using connection_map = std::map<signed_size_type, connection_properties>;
0121
0122
0123
0124 struct region_properties
0125 {
0126 signed_size_type region_id = -1;
0127 isolation_type isolated = isolation_no;
0128 set_type unique_turn_ids;
0129 connection_map connected_region_counts;
0130 };
0131
0132
0133 using merge_map = std::map<ring_identifier, merged_ring_properties>;
0134
0135
0136 using region_connection_map = std::map<signed_size_type, region_properties>;
0137
0138 inline traversal_switch_detector(Geometry1 const& geometry1,
0139 Geometry2 const& geometry2,
0140 Turns& turns, Clusters const& clusters,
0141 Visitor& visitor)
0142 : m_geometry1(geometry1)
0143 , m_geometry2(geometry2)
0144 , m_turns(turns)
0145 , m_clusters(clusters)
0146 , m_visitor(visitor)
0147 {
0148 }
0149
0150 bool one_connection_to_another_region(region_properties const& region) const
0151 {
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 if (region.connected_region_counts.size() == 1)
0162 {
0163 auto const& cprop = region.connected_region_counts.begin()->second;
0164 return cprop.count <= 1;
0165 }
0166 return region.connected_region_counts.empty();
0167 }
0168
0169
0170 bool multiple_connections_to_one_region(region_properties const& region) const
0171 {
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184 if (region.connected_region_counts.size() == 1)
0185 {
0186 auto const& cprop = region.connected_region_counts.begin()->second;
0187 return cprop.count > 1;
0188 }
0189 return false;
0190 }
0191
0192 bool one_connection_to_multiple_regions(region_properties const& region) const
0193 {
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203 bool first = true;
0204 signed_size_type first_turn_id = 0;
0205 for (auto const& key_val : region.connected_region_counts)
0206 {
0207 auto const& cprop = key_val.second;
0208
0209 if (cprop.count != 1)
0210 {
0211 return false;
0212 }
0213 auto const unique_turn_id = *cprop.unique_turn_ids.begin();
0214 if (first)
0215 {
0216 first_turn_id = unique_turn_id;
0217 first = false;
0218 }
0219 else if (first_turn_id != unique_turn_id)
0220 {
0221 return false;
0222 }
0223 }
0224 return true;
0225 }
0226
0227 bool ii_turn_connects_two_regions(region_properties const& region,
0228 region_properties const& connected_region,
0229 signed_size_type turn_index) const
0230 {
0231 turn_type const& turn = m_turns[turn_index];
0232 if (! turn.both(operation_intersection))
0233 {
0234 return false;
0235 }
0236
0237 signed_size_type const id0 = turn.operations[0].enriched.region_id;
0238 signed_size_type const id1 = turn.operations[1].enriched.region_id;
0239
0240 return (id0 == region.region_id && id1 == connected_region.region_id)
0241 || (id1 == region.region_id && id0 == connected_region.region_id);
0242 }
0243
0244
0245 bool isolated_multiple_connection(region_properties const& region,
0246 region_properties const& connected_region) const
0247 {
0248 if (connected_region.isolated != isolation_multiple)
0249 {
0250 return false;
0251 }
0252
0253
0254 set_type turn_ids = region.unique_turn_ids;
0255 for (auto turn_id : connected_region.unique_turn_ids)
0256 {
0257 turn_ids.erase(turn_id);
0258 }
0259
0260
0261 if (turn_ids.size() != 1)
0262 {
0263 return false;
0264 }
0265
0266 for (auto id_or_index : connected_region.unique_turn_ids)
0267 {
0268 if (id_or_index >= 0)
0269 {
0270 if (! ii_turn_connects_two_regions(region, connected_region, id_or_index))
0271 {
0272 return false;
0273 }
0274 }
0275 else
0276 {
0277 signed_size_type const cluster_id = -id_or_index;
0278 auto it = m_clusters.find(cluster_id);
0279 if (it != m_clusters.end())
0280 {
0281 cluster_info const& cinfo = it->second;
0282 for (auto turn_index : cinfo.turn_indices)
0283 {
0284 if (! ii_turn_connects_two_regions(region, connected_region, turn_index))
0285 {
0286 return false;
0287 }
0288 }
0289 }
0290 }
0291 }
0292
0293 return true;
0294 }
0295
0296 bool has_only_isolated_children(region_properties const& region) const
0297 {
0298 bool first_with_turn = true;
0299 signed_size_type first_turn_id = 0;
0300
0301 for (auto const& key_val : region.connected_region_counts)
0302 {
0303 signed_size_type const region_id = key_val.first;
0304 connection_properties const& cprop = key_val.second;
0305
0306 auto mit = m_connected_regions.find(region_id);
0307 if (mit == m_connected_regions.end())
0308 {
0309
0310 return false;
0311 }
0312
0313 region_properties const& connected_region = mit->second;
0314
0315 if (cprop.count != 1)
0316 {
0317
0318 if (! isolated_multiple_connection(region, connected_region))
0319 {
0320 return false;
0321 }
0322 }
0323
0324 if (connected_region.isolated != isolation_yes
0325 && connected_region.isolated != isolation_multiple)
0326 {
0327 signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin();
0328 if (first_with_turn)
0329 {
0330 first_turn_id = unique_turn_id;
0331 first_with_turn = false;
0332 }
0333 else if (first_turn_id != unique_turn_id)
0334 {
0335 return false;
0336 }
0337 }
0338 }
0339
0340
0341
0342 return true;
0343 }
0344
0345 void get_isolated_regions()
0346 {
0347
0348
0349
0350
0351 for (auto& key_val : m_connected_regions)
0352 {
0353 region_properties& properties = key_val.second;
0354 if (one_connection_to_another_region(properties))
0355 {
0356 properties.isolated = isolation_yes;
0357 }
0358 else if (multiple_connections_to_one_region(properties))
0359 {
0360 properties.isolated = isolation_multiple;
0361 }
0362 else if (one_connection_to_multiple_regions(properties))
0363 {
0364 properties.isolated = isolation_yes;
0365 }
0366 }
0367
0368
0369
0370 std::size_t defensive_check = 0;
0371 bool changed = true;
0372 while (changed && defensive_check++ < m_connected_regions.size())
0373 {
0374 changed = false;
0375 for (auto& key_val : m_connected_regions)
0376 {
0377 region_properties& properties = key_val.second;
0378
0379 if (properties.isolated == isolation_no
0380 && has_only_isolated_children(properties))
0381 {
0382 properties.isolated = isolation_yes;
0383 changed = true;
0384 }
0385 }
0386 }
0387 }
0388
0389 void assign_isolation_to_enriched()
0390 {
0391 for (turn_type& turn : m_turns)
0392 {
0393 constexpr auto order1 = geometry::point_order<Geometry1>::value;
0394 constexpr bool reverse1 = (order1 == boost::geometry::counterclockwise)
0395 ? ! Reverse1 : Reverse1;
0396
0397 constexpr auto order2 = geometry::point_order<Geometry2>::value;
0398 constexpr bool reverse2 = (order2 == boost::geometry::counterclockwise)
0399 ? ! Reverse2 : Reverse2;
0400
0401
0402
0403
0404 bool const reverseMeaningInTurn
0405 = (reverse1 || reverse2)
0406 && ! turn.is_self()
0407 && ! turn.is_clustered()
0408 && uu_or_ii(turn)
0409 && turn.operations[0].enriched.region_id
0410 != turn.operations[1].enriched.region_id;
0411
0412 for (auto& op : turn.operations)
0413 {
0414 auto mit = m_connected_regions.find(op.enriched.region_id);
0415 if (mit != m_connected_regions.end())
0416 {
0417 bool const reverseMeaningInOp
0418 = reverseMeaningInTurn
0419 && ((op.seg_id.source_index == 0 && reverse1)
0420 || (op.seg_id.source_index == 1 && reverse2));
0421
0422
0423
0424
0425
0426 region_properties const& prop = mit->second;
0427 op.enriched.isolated
0428 = reverseMeaningInOp
0429 ? false
0430 : prop.isolated == isolation_yes;
0431 }
0432 }
0433 }
0434 }
0435
0436 void assign_region_ids_to_enriched()
0437 {
0438 for (auto const& key_val : m_turns_per_ring)
0439 {
0440 ring_identifier const& ring_id = key_val.first;
0441 merged_ring_properties const& properties = key_val.second;
0442
0443 for (auto turn_index : properties.turn_indices)
0444 {
0445 turn_type& turn = m_turns[turn_index];
0446
0447 if (! acceptable(turn))
0448 {
0449
0450 continue;
0451 }
0452
0453 for (auto& op : turn.operations)
0454 {
0455 if (ring_id_by_seg_id(op.seg_id) == ring_id)
0456 {
0457 op.enriched.region_id = properties.region_id;
0458 }
0459 }
0460 }
0461 }
0462 }
0463
0464 void assign_connected_regions()
0465 {
0466 for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
0467 {
0468 turn_type const& turn = m_turns[turn_index];
0469
0470 signed_size_type const unique_turn_id
0471 = turn.is_clustered() ? -turn.cluster_id : turn_index;
0472
0473 signed_size_type const& id0 = turn.operations[0].enriched.region_id;
0474 signed_size_type const& id1 = turn.operations[1].enriched.region_id;
0475
0476
0477 if (id0 != -1)
0478 {
0479 m_connected_regions[id0].region_id = id0;
0480 m_connected_regions[id0].unique_turn_ids.insert(unique_turn_id);
0481 }
0482 if (id1 != -1 && id0 != id1)
0483 {
0484 m_connected_regions[id1].region_id = id1;
0485 m_connected_regions[id1].unique_turn_ids.insert(unique_turn_id);
0486 }
0487
0488 if (id0 != id1 && id0 != -1 && id1 != -1)
0489 {
0490
0491 connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1];
0492 connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0];
0493
0494
0495 if (prop0.unique_turn_ids.count(unique_turn_id) == 0)
0496 {
0497 prop0.count++;
0498 prop0.unique_turn_ids.insert(unique_turn_id);
0499 }
0500 if (prop1.unique_turn_ids.count(unique_turn_id) == 0)
0501 {
0502 prop1.count++;
0503 prop1.unique_turn_ids.insert(unique_turn_id);
0504 }
0505 }
0506 }
0507 }
0508
0509 inline bool acceptable(turn_type const& turn) const
0510 {
0511
0512
0513
0514 return ! turn.discarded && ! turn.both(operation_blocked);
0515 }
0516
0517 inline bool uu_or_ii(turn_type const& turn) const
0518 {
0519 return turn.both(operation_union) || turn.both(operation_intersection);
0520 }
0521
0522 inline bool connects_same_region(turn_type const& turn) const
0523 {
0524 if (! acceptable(turn))
0525 {
0526 return false;
0527 }
0528
0529 if (! turn.is_clustered())
0530 {
0531
0532 return ! uu_or_ii(turn);
0533 }
0534
0535 if BOOST_GEOMETRY_CONSTEXPR (target_operation == operation_union)
0536 {
0537
0538
0539 return turn.operations[0].enriched.zone
0540 == turn.operations[1].enriched.zone;
0541 }
0542 else
0543 {
0544
0545
0546
0547 return ! (turn.both(operation_intersection)
0548 || turn.combination(operation_intersection, operation_union));
0549 }
0550 }
0551
0552 void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id,
0553 merged_ring_properties& properties, signed_size_type region_id = -1)
0554 {
0555 if (properties.region_id > 0)
0556 {
0557
0558 return;
0559 }
0560
0561
0562 if (region_id == -1)
0563 {
0564 region_id = new_region_id++;
0565 }
0566
0567
0568 properties.region_id = region_id;
0569
0570 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
0571 std::cout << " ADD " << ring_id << " TO REGION " << region_id << std::endl;
0572 #endif
0573
0574
0575 for (auto turn_index : properties.turn_indices)
0576 {
0577 turn_type const& turn = m_turns[turn_index];
0578 if (! connects_same_region(turn))
0579 {
0580
0581 continue;
0582 }
0583
0584
0585
0586 for (auto const& op : turn.operations)
0587 {
0588 ring_identifier connected_ring_id = ring_id_by_seg_id(op.seg_id);
0589 if (connected_ring_id != ring_id)
0590 {
0591 propagate_region(new_region_id, connected_ring_id, region_id);
0592 }
0593 }
0594 }
0595 }
0596
0597 void propagate_region(signed_size_type& new_region_id,
0598 ring_identifier const& ring_id, signed_size_type region_id)
0599 {
0600 auto it = m_turns_per_ring.find(ring_id);
0601 if (it != m_turns_per_ring.end())
0602 {
0603 create_region(new_region_id, ring_id, it->second, region_id);
0604 }
0605 }
0606
0607 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
0608 void debug_show_results()
0609 {
0610 auto isolation_to_string = [](isolation_type const& iso) -> std::string
0611 {
0612 switch(iso)
0613 {
0614 case isolation_no : return "no";
0615 case isolation_yes : return "yes";
0616 case isolation_multiple : return "multiple";
0617 }
0618 return "error";
0619 };
0620 auto set_to_string = [](auto const& s) -> std::string
0621 {
0622 std::ostringstream result;
0623 for (auto item : s) { result << " " << item; }
0624 return result.str();
0625 };
0626
0627 for (auto const& kv : m_connected_regions)
0628 {
0629 auto const& prop = kv.second;
0630
0631 std::ostringstream sub;
0632 sub << "[turns" << set_to_string(prop.unique_turn_ids)
0633 << "] regions";
0634 for (auto const& kvs : prop.connected_region_counts)
0635 {
0636 sub << " { " << kvs.first
0637 << " : via [" << set_to_string(kvs.second.unique_turn_ids)
0638 << " ] }";
0639 }
0640
0641 std::cout << "REGION " << prop.region_id
0642 << " " << isolation_to_string(prop.isolated)
0643 << " " << sub.str()
0644 << std::endl;
0645 }
0646
0647 for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
0648 {
0649 turn_type const& turn = m_turns[turn_index];
0650
0651 if (uu_or_ii(turn) && ! turn.is_clustered())
0652 {
0653 std::cout << (turn.both(operation_union) ? "UU" : "II")
0654 << " " << turn_index
0655 << " (" << geometry::get<0>(turn.point)
0656 << ", " << geometry::get<1>(turn.point) << ")"
0657 << " -> " << std::boolalpha
0658 << " [" << turn.operations[0].seg_id.source_index
0659 << "/" << turn.operations[1].seg_id.source_index << "] "
0660 << "(" << turn.operations[0].enriched.region_id
0661 << " " << turn.operations[0].enriched.isolated
0662 << ") / (" << turn.operations[1].enriched.region_id
0663 << " " << turn.operations[1].enriched.isolated << ")"
0664 << std::endl;
0665 }
0666 }
0667
0668 for (auto const& key_val : m_clusters)
0669 {
0670 cluster_info const& cinfo = key_val.second;
0671 std::cout << "CL RESULT " << key_val.first
0672 << " -> " << cinfo.open_count << std::endl;
0673 }
0674 }
0675 #endif
0676
0677 void iterate()
0678 {
0679 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
0680 std::cout << "BEGIN SWITCH DETECTOR (region_ids and isolation)"
0681 << (Reverse1 ? " REVERSE_1" : "")
0682 << (Reverse2 ? " REVERSE_2" : "")
0683 << std::endl;
0684 #endif
0685
0686
0687 m_turns_per_ring.clear();
0688 m_connected_regions.clear();
0689
0690 for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
0691 {
0692 turn_type const& turn = m_turns[turn_index];
0693
0694 if BOOST_GEOMETRY_CONSTEXPR (target_operation == operation_intersection)
0695 {
0696 if (turn.discarded)
0697 {
0698
0699 continue;
0700 }
0701 }
0702
0703 for (auto const& op : turn.operations)
0704 {
0705 m_turns_per_ring[ring_id_by_seg_id(op.seg_id)].turn_indices.insert(turn_index);
0706 }
0707 }
0708
0709
0710 {
0711 signed_size_type new_region_id = 1;
0712 for (auto& key_val : m_turns_per_ring)
0713 {
0714 create_region(new_region_id, key_val.first, key_val.second);
0715 }
0716
0717 assign_region_ids_to_enriched();
0718 assign_connected_regions();
0719 get_isolated_regions();
0720 assign_isolation_to_enriched();
0721 }
0722
0723 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
0724 std::cout << "END SWITCH DETECTOR" << std::endl;
0725 debug_show_results();
0726 #endif
0727
0728 }
0729
0730 private:
0731
0732 Geometry1 const& m_geometry1;
0733 Geometry2 const& m_geometry2;
0734 Turns& m_turns;
0735 Clusters const& m_clusters;
0736 merge_map m_turns_per_ring;
0737 region_connection_map m_connected_regions;
0738 Visitor& m_visitor;
0739 };
0740
0741 }}
0742 #endif
0743
0744 }}
0745
0746 #endif