Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-21 08:45:50

0001 // Boost.Geometry
0002 
0003 // Copyright (c) 2025 Barend Gehrels, Amsterdam, the Netherlands.
0004 
0005 // Use, modification and distribution is subject to the Boost Software License,
0006 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0007 // http://www.boost.org/LICENSE_1_0.txt)
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     // For extra nodes, also store the original node
0051     signed_size_type original_node_id{0};
0052 };
0053 
0054 struct state_type
0055 {
0056     // Using a flatmap changes behavior and cause errors.
0057     // Using an ordered map gives a just a tiny bit of performance improvement, sometimes.
0058     // Maps from vertex to vertex info
0059     std::map<std::size_t, vertex_info> vertex_map;
0060 
0061     // Reverse mapping. Every node (turn or cluster) has only ONE vertex,
0062     // but there might be so called "extra" vertices, not associated with a node.
0063     std::map<signed_size_type, std::size_t> node_to_vertex_index;
0064 
0065     // For each edge, store the segment identifier
0066     std::map<std::pair<std::size_t, std::size_t>, segment_identifier> edge_to_seg_id;
0067 
0068     // Keeps track of vertex index, which must, for Boost.Graph, be consecutive.
0069     // The turn index is not consecutive (because of discarded, and of clusters).
0070     std::size_t vertex_index{0};
0071 
0072     // For round-trips extra nodes are inserted.
0073     // Round-trips are operations returning to itself, for example in some uu cases.
0074     // They are numbered from turn.size() and up, such that they have a unique positive id.
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     // Insert the source and target node (turn or cluster)
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         // Get the accompanying vertex info (might be a new record)
0094         auto& target_vertex_info = state.vertex_map[it_target->second];
0095         target_vertex_info.node_id = target_node_id;
0096     }
0097 
0098     // Get the accompanying vertex info (might be a new record)
0099     // and store node and the segment id for this edge
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         // The normal case, Not a round trip
0107         vertex_info.target_vertex_indices.insert(it_target->second);
0108         return;
0109     }
0110 
0111     // For a round trip, add an extra vertex.
0112     // It is not necessary to add them to the node_to_vertex_index,
0113     // because they won't be looked up further.
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     // Store the segment id in both of these edges
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         // The node id is negative for clusters
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 // Assigns biconnected components to turns
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         // Assign the component to all the operations
0193         // going from the source node to the target node.
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                         // For cc, always set both operations (only one of them is returned by get_node_id)
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     // Mapping to add turns to vertices, count them, and then build the graph.
0234     // (It is convenient if the vertex index is the same as the turn index.
0235     // Therefore the default mapping is made like that, extra vertices
0236     // are added later)
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     // Build the graph from the vertices
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 }} // namespace detail::overlay
0272 #endif // DOXYGEN_NO_DETAIL
0273 
0274 }} // namespace boost::geometry
0275 
0276 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DETECT_ARTICULATION_POINTS_HPP