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