File indexing completed on 2026-08-21 08:45:50
0001
0002
0003
0004
0005
0006
0007
0008
0009 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_CLUSTERED_COUNTS_HPP
0010 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_CLUSTERED_COUNTS_HPP
0011
0012 #include <boost/geometry/algorithms/detail/position_code.hpp>
0013 #include <boost/geometry/algorithms/detail/overlay/approximately_equals.hpp>
0014 #include <boost/geometry/algorithms/detail/signed_size_type.hpp>
0015 #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
0016 #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
0017 #include <boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp>
0018 #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
0019 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
0020
0021 #include <set>
0022 #include <map>
0023
0024 namespace boost { namespace geometry
0025 {
0026
0027 #ifndef DOXYGEN_NO_DETAIL
0028 namespace detail { namespace overlay
0029 {
0030
0031
0032 enum class connection_type { unknown = -1, incoming = 0, outgoing = 1 };
0033
0034
0035
0036
0037 struct connection_key
0038 {
0039 segment_identifier seg_id;
0040 connection_type connection{connection_type::unknown};
0041
0042 bool operator<(connection_key const& rhs) const
0043 {
0044 return std::tie(seg_id, connection) < std::tie(rhs.seg_id, rhs.connection);
0045 }
0046 };
0047
0048
0049 template <typename Point>
0050 struct connection_properties
0051 {
0052
0053 int position_code{0};
0054 Point point{};
0055 Point opposite_point{};
0056 bool is_shifted{false};
0057
0058
0059 std::size_t zone_count_left{0};
0060 std::size_t zone_count_right{0};
0061 std::size_t rank{0};
0062 };
0063
0064
0065 template <typename Point>
0066 struct connection_item
0067 {
0068 connection_key key{};
0069 connection_properties<Point> properties{};
0070 };
0071
0072 template <overlay_type OverlayType>
0073 struct is_corresponding_connection
0074 {
0075 static inline bool apply(connection_key const& left, connection_key const& right)
0076 {
0077 return left.seg_id.source_index == right.seg_id.source_index;
0078 }
0079 };
0080
0081 template <>
0082 struct is_corresponding_connection<overlay_buffer>
0083 {
0084 static inline bool apply(connection_key const& left, connection_key const& right)
0085 {
0086
0087
0088 return left.seg_id == right.seg_id;
0089 }
0090 };
0091
0092 template
0093 <
0094 bool Reverse1,
0095 bool Reverse2,
0096 overlay_type OverlayType,
0097 typename Geometry1,
0098 typename Geometry2,
0099 typename Turns,
0100 typename Clusters,
0101 typename Strategy
0102 >
0103 struct clustered_count_handler
0104 {
0105 using point_type = typename Turns::value_type::point_type;
0106 using connection_map_type = std::map<connection_key, connection_properties<point_type>>;
0107 using ct_type = typename geometry::select_most_precise
0108 <
0109 geometry::coordinate_type_t<point_type>,
0110 double
0111 >::type;
0112
0113 clustered_count_handler(Geometry1 const& m_geometry1, Geometry2 const& m_geometry2,
0114 Turns& m_turns, Clusters& clusters,
0115 Strategy const& strategy)
0116 : m_geometry1(m_geometry1)
0117 , m_geometry2(m_geometry2)
0118 , m_turns(m_turns)
0119 , m_clusters(clusters)
0120 , m_intersection_strategy(strategy)
0121 , m_side_strategy(m_intersection_strategy.side())
0122 {}
0123
0124
0125
0126
0127 template <typename Operation>
0128 bool get_segment_points(Operation const& op, point_type const& point_turn,
0129 point_type& point_from, point_type& point_to)
0130 {
0131
0132 static const ct_type tolerance
0133 = common_approximately_equals_epsilon_multiplier<ct_type>::value();
0134
0135
0136 constexpr int max_iterations = 10;
0137
0138 int from_offset = 0;
0139 do
0140 {
0141 geometry::copy_segment_point<Reverse1, Reverse2>(m_geometry1, m_geometry2,
0142 op.seg_id, from_offset--, point_from);
0143 } while (approximately_equals(point_from, point_turn, tolerance) && from_offset > -max_iterations);
0144
0145 int to_offset = 1;
0146 do
0147 {
0148 geometry::copy_segment_point<Reverse1, Reverse2>(m_geometry1, m_geometry2,
0149 op.seg_id, to_offset++, point_to);
0150 } while (approximately_equals(point_to, point_turn, tolerance) && to_offset < max_iterations);
0151
0152 return from_offset < -1 || to_offset > 2;
0153 }
0154
0155 void get_connection_map(cluster_info const& cluster, point_type const& point_turn,
0156 connection_map_type& connection_map, point_type& point_origin)
0157 {
0158 auto const get_position_code = [&](point_type const& point)
0159 {
0160 return detail::get_position_code(point_origin, point_turn, point, m_side_strategy);
0161 };
0162
0163 auto insert = [&connection_map](auto const& op, connection_type conn,
0164 auto const& point, int position_code, auto const& opposite_point, bool is_shifted)
0165 {
0166 connection_key const key{op.seg_id, conn};
0167 connection_properties<point_type> properties{position_code, point, opposite_point, is_shifted};
0168 connection_map.insert({key, properties});
0169 };
0170
0171
0172 bool first = true;
0173 for (std::size_t index : cluster.turn_indices)
0174 {
0175 auto const& turn = m_turns[index];
0176 for (auto const& op : turn.operations)
0177 {
0178 point_type point_from, point_to;
0179 bool const is_shifted = get_segment_points(op, point_turn, point_from, point_to);
0180
0181 if (first)
0182 {
0183
0184
0185 first = false;
0186 point_origin = point_from;
0187 }
0188
0189
0190 insert(op, connection_type::incoming, point_from, get_position_code(point_from), point_to, is_shifted);
0191 insert(op, connection_type::outgoing, point_to, get_position_code(point_to), point_from, is_shifted);
0192 }
0193 }
0194 }
0195
0196 void sort(point_type const& point_turn, std::vector<connection_item<point_type>>& item_vector)
0197 {
0198 auto compare_by_connection = [](auto const& left, auto const& right)
0199 {
0200
0201 return left.key.connection > right.key.connection;
0202 };
0203
0204
0205
0206
0207 auto compare_by_side = [&](auto const& left, auto const& right)
0208 {
0209 int const side_left = m_side_strategy.apply(point_turn, right.properties.point, left.properties.point);
0210 int const side_right = m_side_strategy.apply(point_turn, left.properties.point, right.properties.point);
0211
0212 if (side_right == side_left)
0213 {
0214 return compare_by_connection(left, right);
0215 }
0216 return side_left < side_right;
0217 };
0218
0219 std::sort(item_vector.begin(), item_vector.end(),
0220 [&](auto const& left, auto const& right)
0221 {
0222 if (left.properties.position_code == right.properties.position_code)
0223 {
0224 if (left.properties.position_code == 1 || left.properties.position_code == 3)
0225 {
0226
0227 return compare_by_connection(left, right);
0228 }
0229 return compare_by_side(left, right);
0230 }
0231 return left.properties.position_code < right.properties.position_code;
0232 });
0233 }
0234
0235
0236 void assign_ranks(point_type const& point_turn,
0237 std::vector<connection_item<point_type>>& item_vector)
0238 {
0239 std::size_t rank = 0;
0240 item_vector.front().properties.rank = 0;
0241 for (std::size_t i = 0; i + 1 < item_vector.size(); i++)
0242 {
0243 auto const& previous = item_vector[i];
0244 auto& item = item_vector[i + 1];
0245 if (item.properties.position_code != previous.properties.position_code)
0246 {
0247 item.properties.rank = ++rank;
0248 continue;
0249 }
0250
0251 if (item.properties.position_code == 1 || item.properties.position_code == 3)
0252 {
0253
0254 item.properties.rank = rank;
0255 continue;
0256 }
0257
0258
0259
0260
0261 int const side = m_side_strategy.apply(point_turn, previous.properties.point,
0262 item.properties.point);
0263 item.properties.rank = side == 0 ? rank : ++rank;
0264 }
0265 }
0266
0267 auto get_zone_counts(std::vector<connection_item<point_type>> const& item_vector,
0268 std::size_t rank_size)
0269 {
0270 std::size_t const vector_size = item_vector.size();
0271 auto get_next_item = [&vector_size](std::size_t counter)
0272 {
0273 return (counter + 1) % vector_size;
0274 };
0275
0276 auto get_next_zone = [&rank_size](std::size_t counter)
0277 {
0278 return (counter + 1) % rank_size;
0279 };
0280
0281
0282
0283 std::vector<std::size_t> zone_counts(rank_size, 0);
0284 for (std::size_t i = 0; i < item_vector.size(); i++)
0285 {
0286 auto const& item = item_vector[i];
0287 if (item.key.connection != connection_type::incoming)
0288 {
0289 continue;
0290 }
0291
0292
0293
0294 std::size_t end_rank = item.properties.rank;
0295 for (std::size_t j = get_next_item(i), iteration = 0; ; j = get_next_item(j), iteration++)
0296 {
0297 if (iteration > vector_size)
0298 {
0299 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0300 std::cerr << " *** ERROR: infinite loop in cluster" << std::endl;
0301 #endif
0302 return zone_counts;
0303 }
0304 auto const& next = item_vector[j];
0305 end_rank = next.properties.rank;
0306
0307 if (next.key.connection == connection_type::outgoing
0308 && is_corresponding_connection<OverlayType>::apply(item.key, next.key))
0309 {
0310
0311 break;
0312 }
0313 }
0314
0315
0316 for (std::size_t r = item.properties.rank; r != end_rank; r = get_next_zone(r))
0317 {
0318 zone_counts[r]++;
0319 }
0320 }
0321
0322 return zone_counts;
0323 }
0324
0325 void assign_zone_counts(std::vector<connection_item<point_type>>& item_vector,
0326 std::vector<std::size_t> const& zone_counts, std::size_t rank_size)
0327 {
0328
0329
0330 for (auto& item : item_vector)
0331 {
0332 std::size_t const zone_right =
0333 item.key.connection == connection_type::incoming
0334 ? item.properties.rank
0335 : (item.properties.rank + rank_size - 1) % rank_size;
0336
0337 std::size_t const zone_left =
0338 item.key.connection == connection_type::incoming
0339 ? (item.properties.rank + rank_size - 1) % rank_size
0340 : item.properties.rank;
0341
0342 item.properties.zone_count_left = zone_counts[zone_left];
0343 item.properties.zone_count_right = zone_counts[zone_right];
0344 }
0345 }
0346
0347 std::size_t get_open_count(std::vector<std::size_t> const& zone_counts, std::size_t rank_size)
0348 {
0349 std::size_t result = 0;
0350 for (std::size_t i = 0; i < rank_size; i++)
0351 {
0352 if (zone_counts[i] == 0)
0353 {
0354 result++;
0355 }
0356 }
0357 return result;
0358 }
0359
0360
0361 void handle_spikes(cluster_info& cluster, std::vector<connection_item<point_type>>& item_vector)
0362 {
0363 for (std::size_t i = 0; i < item_vector.size(); i++)
0364 {
0365 auto const next_i = (i + 1) % item_vector.size();
0366 if (item_vector[i].key.connection == item_vector[next_i].key.connection)
0367 {
0368
0369 continue;
0370 }
0371 auto& current = item_vector[i].properties;
0372 auto& next = item_vector[next_i].properties;
0373 if (current.rank != next.rank
0374 || current.zone_count_left != 1 || current.zone_count_right != 1
0375 || next.zone_count_left != 1 || next.zone_count_right != 1)
0376 {
0377
0378
0379 continue;
0380 }
0381
0382 if (current.is_shifted || next.is_shifted) {
0383
0384
0385 continue;
0386 }
0387
0388
0389
0390 auto const dm = get_distance_measure(current.opposite_point, current.point, next.point,
0391 m_intersection_strategy);
0392 if (dm.measure <= 0)
0393 {
0394 continue;
0395 }
0396
0397
0398
0399 cluster.open_count++;
0400 current.zone_count_left = 0;
0401 next.zone_count_right = 0;
0402 }
0403 }
0404
0405 void assign_turn_operations(cluster_info const& cluster,
0406 connection_map_type const& connection_map)
0407 {
0408
0409 for (std::size_t index : cluster.turn_indices)
0410 {
0411 auto& turn = m_turns[index];
0412 for (int i = 0; i < 2; i++)
0413 {
0414 auto& op = turn.operations[i];
0415 connection_key const key{op.seg_id, connection_type::outgoing};
0416 auto const it = connection_map.find(key);
0417 if (it != connection_map.end())
0418 {
0419 op.enriched.count_left = it->second.zone_count_left;
0420 op.enriched.count_right = it->second.zone_count_right;
0421 op.enriched.rank = it->second.rank;
0422 }
0423 }
0424 }
0425 }
0426
0427
0428
0429
0430
0431
0432
0433
0434 void change_reversed_operations(signed_size_type const cluster_id, cluster_info const& cluster,
0435 point_type const& point_turn, point_type const& point_origin)
0436 {
0437 std::set<std::size_t> reversed_indices;
0438 for (std::size_t index : cluster.turn_indices)
0439 {
0440 auto const& turn = m_turns[index];
0441 if (! turn.combination(operation_union, operation_intersection))
0442 {
0443 continue;
0444 }
0445 int const union_index = turn.operations[0].operation == operation_union ? 0 : 1;
0446 auto const& op_u = turn.operations[union_index];
0447 auto const& op_i = turn.operations[1 - union_index];
0448 if (op_u.enriched.count_left > 0 && op_i.enriched.count_left == 0)
0449 {
0450 reversed_indices.insert(index);
0451 }
0452 }
0453
0454 if (reversed_indices.empty())
0455 {
0456 return;
0457 }
0458
0459 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0460 std::cout << " *** REVERSED OPERATIONS in cluster: " << cluster_id
0461 << " cluster size: " << cluster.turn_indices.size()
0462 << " reversed: " << reversed_indices.size()
0463 << std::endl;
0464 #endif
0465 for (std::size_t index : cluster.turn_indices)
0466 {
0467 auto& turn = m_turns[index];
0468 auto& op0 = turn.operations[0];
0469 auto& op1 = turn.operations[1];
0470
0471 bool const is_same_target = op0.enriched.travels_to_ip_index == op1.enriched.travels_to_ip_index;
0472 if (is_same_target && reversed_indices.find(index) != reversed_indices.end())
0473 {
0474
0475 op0.operation = operation_continue;
0476 op1.operation = operation_continue;
0477
0478
0479 op0.enriched.count_left = (std::min)(op0.enriched.count_left, op1.enriched.count_left);
0480 op1.enriched.count_left = op0.enriched.count_left;
0481 op0.enriched.count_right = (std::min)(op0.enriched.count_right, op1.enriched.count_right);
0482 op1.enriched.count_right = op0.enriched.count_right;
0483 }
0484 }
0485 }
0486
0487 template <typename Visitor>
0488 void apply(signed_size_type const cluster_id, cluster_info& cluster, Visitor& visitor)
0489 {
0490 if (cluster.turn_indices.empty())
0491 {
0492
0493 return;
0494 }
0495
0496 point_type const& point_turn = m_turns[*cluster.turn_indices.begin()].point;
0497 point_type point_origin;
0498 connection_map_type connection_map;
0499 get_connection_map(cluster, point_turn, connection_map, point_origin);
0500
0501
0502
0503 std::vector<connection_item<point_type>> item_vector;
0504 for (auto const& key_value : connection_map)
0505 {
0506 connection_item<point_type> item;
0507 item.key = key_value.first;
0508 item.properties = key_value.second;
0509 item_vector.push_back(std::move(item));
0510 }
0511
0512 sort(point_turn, item_vector);
0513 assign_ranks(point_turn, item_vector);
0514
0515 auto const rank_size = item_vector.back().properties.rank + 1;
0516 auto const zone_counts = get_zone_counts(item_vector, rank_size);
0517
0518 assign_zone_counts(item_vector, zone_counts, rank_size);
0519
0520 cluster.open_count = get_open_count(zone_counts, rank_size);
0521 if (cluster.open_count == 0)
0522 {
0523 handle_spikes(cluster, item_vector);
0524 }
0525
0526
0527 for (auto const& item : item_vector)
0528 {
0529 connection_map[item.key] = item.properties;
0530 }
0531
0532 assign_turn_operations(cluster, connection_map);
0533 change_reversed_operations(cluster_id, cluster, point_turn, point_origin);
0534
0535 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0536
0537 std::cout << "Cluster " << cluster_id << " size: " << cluster.turn_indices.size() << std::endl;
0538 for (auto const& item : item_vector)
0539 {
0540 std::cout << " " << item.key.seg_id
0541 << " " << (item.key.connection == connection_type::incoming ? " in" : "out")
0542 << " " << item.properties.position_code
0543 << " " << item.properties.rank
0544 << " " << item.properties.zone_count_left
0545 << " " << item.properties.zone_count_right
0546 << std::endl;
0547 }
0548 #endif
0549
0550 visitor.visit_cluster_connections(cluster_id, m_turns, cluster, item_vector);
0551 }
0552
0553 template <typename Visitor>
0554 void apply(Visitor& visitor)
0555 {
0556 for (auto& key_value : m_clusters)
0557 {
0558 auto& cluster = key_value.second;
0559 if (cluster.turn_indices.empty())
0560 {
0561 continue;
0562 }
0563
0564 apply(key_value.first, cluster, visitor);
0565 }
0566 }
0567
0568 private:
0569 Geometry1 const& m_geometry1;
0570 Geometry2 const& m_geometry2;
0571 Turns& m_turns;
0572 Clusters& m_clusters;
0573 Strategy const& m_intersection_strategy;
0574 decltype(m_intersection_strategy.side()) m_side_strategy;
0575 };
0576
0577 }}
0578 #endif
0579
0580 }}
0581
0582 #endif