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_DETECT_ARTICULATION_POINTS_HPP
0010 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DETECT_ARTICULATION_POINTS_HPP
0011
0012 #include <map>
0013 #include <set>
0014
0015 #include <boost/geometry/algorithms/detail/signed_size_type.hpp>
0016 #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
0017 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
0018 #include <boost/geometry/algorithms/detail/overlay/graph/node_util.hpp>
0019 #include <boost/geometry/algorithms/detail/overlay/graph/graph_util.hpp>
0020
0021 #include <boost/geometry/algorithms/detail/overlay/graph/debug_graph.hpp>
0022
0023 #include <boost/graph/biconnected_components.hpp>
0024 #include <boost/graph/adjacency_list.hpp>
0025
0026 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
0027 #include <boost/geometry/core/exception.hpp>
0028 #include <boost/throw_exception.hpp>
0029 #endif
0030
0031 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0032 #include <fstream>
0033 #endif
0034
0035 namespace boost { namespace geometry
0036 {
0037
0038 #ifndef DOXYGEN_NO_DETAIL
0039 namespace detail { namespace overlay
0040 {
0041
0042 struct vertex_info
0043 {
0044 signed_size_type node_id{0};
0045
0046 set_of_size_t target_vertex_indices;
0047
0048 bool is_extra{false};
0049
0050
0051 signed_size_type original_node_id{0};
0052 };
0053
0054 struct state_type
0055 {
0056
0057
0058
0059 std::map<std::size_t, vertex_info> vertex_map;
0060
0061
0062
0063 std::map<signed_size_type, std::size_t> node_to_vertex_index;
0064
0065
0066 std::map<std::pair<std::size_t, std::size_t>, segment_identifier> edge_to_seg_id;
0067
0068
0069
0070 std::size_t vertex_index{0};
0071
0072
0073
0074
0075 std::size_t extra_node_id{0};
0076 };
0077
0078 inline void add_edge(signed_size_type source_node_id, signed_size_type target_node_id,
0079 segment_identifier const& seg_id, state_type& state)
0080 {
0081
0082 auto it_source = state.node_to_vertex_index.find(source_node_id);
0083 if (it_source == state.node_to_vertex_index.end())
0084 {
0085 it_source = state.node_to_vertex_index.insert({source_node_id, state.vertex_index++}).first;
0086 }
0087
0088 auto it_target = state.node_to_vertex_index.find(target_node_id);
0089 if (it_target == state.node_to_vertex_index.end())
0090 {
0091 it_target = state.node_to_vertex_index.insert({target_node_id, state.vertex_index++}).first;
0092
0093
0094 auto& target_vertex_info = state.vertex_map[it_target->second];
0095 target_vertex_info.node_id = target_node_id;
0096 }
0097
0098
0099
0100 auto& vertex_info = state.vertex_map[it_source->second];
0101 vertex_info.node_id = source_node_id;
0102 state.edge_to_seg_id[{it_source->second, it_target->second}] = seg_id;
0103
0104 if (target_node_id != source_node_id)
0105 {
0106
0107 vertex_info.target_vertex_indices.insert(it_target->second);
0108 return;
0109 }
0110
0111
0112
0113
0114 std::size_t const extra_node_id = state.extra_node_id++;
0115 std::size_t const extra_vertex_index = state.vertex_index++;
0116
0117
0118 auto& extra_vertex_info = state.vertex_map[extra_vertex_index];
0119 extra_vertex_info.node_id = extra_node_id;
0120 state.edge_to_seg_id[{it_source->second, extra_vertex_index}] = seg_id;
0121 state.edge_to_seg_id[{extra_vertex_index, it_target->second}] = seg_id;
0122
0123 extra_vertex_info.is_extra = true;
0124 extra_vertex_info.original_node_id = source_node_id;
0125 extra_vertex_info.target_vertex_indices.insert(it_target->second);
0126
0127 vertex_info.target_vertex_indices.insert(extra_vertex_index);
0128 }
0129
0130 template <operation_type TargetOperation, typename Turns, typename Clusters>
0131 void fill_vertex_map(Turns const& turns, Clusters const& clusters, state_type& state)
0132 {
0133 std::set<edge_info> edges;
0134 for (auto const& key_value : clusters)
0135 {
0136
0137 auto const cluster_node_id = -key_value.first;
0138 auto const& cluster = key_value.second;
0139 for (std::size_t turn_index : cluster.turn_indices)
0140 {
0141 auto const& turn = turns[turn_index];
0142 get_target_operations<TargetOperation>(turns, turn, turn_index, cluster_node_id, edges);
0143 }
0144 }
0145 for (std::size_t i = 0; i < turns.size(); i++)
0146 {
0147 auto const& turn = turns[i];
0148 if (turn.discarded || turn.is_clustered())
0149 {
0150 continue;
0151 }
0152 get_target_operations<TargetOperation>(turns, turn, i, i, edges);
0153 }
0154 for (auto const& edge : edges)
0155 {
0156 add_edge(edge.source_node_id, edge.target_node_id, edge.seg_id, state);
0157 }
0158 }
0159
0160
0161 template <typename Turns, typename Clusters, typename Graph, typename Components>
0162 void assign_biconnected_component_ids(Turns& turns, Clusters const& clusters, bool allow_closed,
0163 Graph const& graph, Components const& component, state_type const& state)
0164 {
0165 auto node_id_from_it = [](auto const& it)
0166 {
0167 return it->second.is_extra
0168 ? it->second.original_node_id
0169 : it->second.node_id;
0170 };
0171
0172 typename graph_traits<Graph>::edge_iterator ei, ei_end;
0173 for (boost::tie(ei, ei_end) = edges(graph); ei != ei_end; ++ei)
0174 {
0175 auto it_source = state.vertex_map.find(source(*ei, graph));
0176 auto it_target = state.vertex_map.find(target(*ei, graph));
0177 if (it_source == state.vertex_map.end() || it_target == state.vertex_map.end())
0178 {
0179 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
0180 BOOST_THROW_EXCEPTION(logic_exception("Edge not found in vertex map"));
0181 #endif
0182 continue;
0183 }
0184
0185 auto const source_node_id = node_id_from_it(it_source);
0186 auto const target_node_id = node_id_from_it(it_target);
0187 auto const edge_seg_id = state.edge_to_seg_id.at({source(*ei, graph), target(*ei, graph)});
0188
0189 auto const turn_indices = get_turn_indices_by_node_id(turns, clusters, source_node_id,
0190 allow_closed);
0191
0192
0193
0194 for (auto const& turn_index : turn_indices)
0195 {
0196 auto& turn = turns[turn_index];
0197 for (std::size_t j = 0; j < 2; j++)
0198 {
0199 auto& op = turn.operations[j];
0200 if (op.enriched.travels_to_ip_index < 0)
0201 {
0202 continue;
0203 }
0204
0205 auto const travels_to_node_id = get_node_id(turns, op.enriched.travels_to_ip_index);
0206 if (travels_to_node_id == target_node_id && op.seg_id == edge_seg_id)
0207 {
0208 op.enriched.component_id = static_cast<int>(component[*ei]);
0209 if (turn.both(operation_continue))
0210 {
0211
0212 auto& other_op = turn.operations[1 - j];
0213 other_op.enriched.component_id = op.enriched.component_id;
0214 }
0215 }
0216 }
0217 }
0218 }
0219 }
0220
0221 template <operation_type TargetOperation, typename Turns, typename Clusters>
0222 void detect_biconnected_components(Turns& turns, Clusters const& clusters)
0223 {
0224 using graph_t = boost::adjacency_list
0225 <
0226 boost::vecS,
0227 boost::vecS,
0228 boost::undirectedS,
0229 boost::no_property,
0230 boost::property<edge_component, std::size_t>
0231 >;
0232
0233
0234
0235
0236
0237
0238 state_type state;
0239 state.extra_node_id = static_cast<std::size_t>(turns.size());
0240
0241 fill_vertex_map<TargetOperation>(turns, clusters, state);
0242
0243
0244 graph_t graph(state.vertex_map.size());
0245 for (auto const& key_value : state.vertex_map)
0246 {
0247 auto const vertex_index = key_value.first;
0248 for (auto const target_vertex_index : key_value.second.target_vertex_indices)
0249 {
0250 boost::add_edge(vertex_index, target_vertex_index, graph);
0251 }
0252 }
0253
0254 edge_component ec;
0255 auto component = boost::get(ec, graph);
0256 biconnected_components(graph, component);
0257 fix_components(component, graph);
0258
0259 assign_biconnected_component_ids(turns, clusters,
0260 TargetOperation == operation_intersection,
0261 graph, component, state);
0262
0263 #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE_GRAPH)
0264 {
0265 std::ofstream out("/tmp/graph_viz.dot");
0266 write_graph_viz(out, turns, clusters, graph, component, state.vertex_map);
0267 }
0268 #endif
0269 }
0270
0271 }}
0272 #endif
0273
0274 }}
0275
0276 #endif