Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/use biconnected components #1357

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ target_link_libraries(boost_geometry
Boost::type_traits
Boost::utility
Boost::variant

Boost::graph
)

# Required for Boost.Geometry Index
Expand Down Expand Up @@ -112,7 +114,10 @@ if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
serialization
tokenizer
variant
test)
test

graph
)

if (BOOST_SRC_DIR_IS_VALID)
set(BOOST_EXCLUDE_LIBRARIES ${PROJECT_NAME})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// Boost.Geometry

// Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DETECT_ARTICULATION_POINTS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DETECT_ARTICULATION_POINTS_HPP

#include <map>
#include <set>

#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/node_util.hpp>
#include <boost/geometry/algorithms/detail/overlay/graph_util.hpp>

#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/adjacency_list.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{


struct vertex_info
{
signed_size_type node_id{0};
std::set<std::size_t> target_vertex_indices;

bool is_extra{false};

// For extra nodes, also store the original node
signed_size_type original_node_id{0};
};

struct state_type
{
// Maps from vertex to vertex info
std::map<std::size_t, vertex_info> vertex_map;

// Reverse mapping. Every node (turn or cluster) has only ONE vertex,
// but there might be additional vertices, not associated with a node.
std::map<signed_size_type, std::size_t> node_to_vertex_index;

// Keeps track of vertex index, which must, for Boost.Graph,
// be consecutive. The turn index is not (because of discarded and clusters).
std::size_t vertex_index{0};

// For some cases (returning to itself) extra nodes are inserted.
// They are numbered from turn.size() and up.
std::size_t extra_node_id{0};
};

void add_target_node(signed_size_type source_node_id, signed_size_type target_node_id,
state_type& state, bool allow_returning)
{
// Insert the source and target node (turn or cluster)
auto it_source = state.node_to_vertex_index.find(source_node_id);
if (it_source == state.node_to_vertex_index.end())
{
it_source = state.node_to_vertex_index.insert({source_node_id, state.vertex_index++}).first;
}

auto it_target = state.node_to_vertex_index.find(target_node_id);
if (it_target == state.node_to_vertex_index.end())
{
it_target = state.node_to_vertex_index.insert({target_node_id, state.vertex_index++}).first;
}

// Get the accompanying vertex into (might be a new record)
auto& vertex_info = state.vertex_map[it_source->second];
vertex_info.node_id = source_node_id;

if (target_node_id == source_node_id && allow_returning)
{
std::size_t const extra_node_id = state.extra_node_id++;

// Add a brand new vertex, and add the target to it
// To keep the index right, first this brand new vertex as the target
vertex_info.target_vertex_indices.insert(state.vertex_index);
state.node_to_vertex_index.insert({extra_node_id, state.vertex_index});
auto& extra_vertex_info = state.vertex_map[state.vertex_index++];
extra_vertex_info.node_id = extra_node_id;
extra_vertex_info.is_extra = true;
extra_vertex_info.original_node_id = source_node_id;
extra_vertex_info.target_vertex_indices.insert(it_target->second);
}
else
{
vertex_info.target_vertex_indices.insert(it_target->second);
}
}


template <operation_type TargetOperation, typename Turns, typename Clusters>
void fill_vertex_map(Turns const& turns, Clusters const& clusters, state_type& state)
{
std::set<std::size_t> visited_turns;
for (std::size_t i = 0; i < turns.size(); i++)
{
auto const & turn = turns[i];
if (turn.discarded)
{
continue;
}
if (visited_turns.count(i) > 0)
{
continue;
}

auto const source_node_id = get_node_id(turns, i);
auto const turn_indices = get_turn_indices_by_node_id(turns, clusters, source_node_id);

visited_turns.insert(turn_indices.begin(), turn_indices.end());

auto const target_nodes = get_target_nodes<TargetOperation>(turns, clusters, turn_indices);

for (auto const& target_node_id : target_nodes)
{
add_target_node(source_node_id, target_node_id, state, true);
}
}
}

// Assigns biconnected components to turns
template <typename Turns, typename Clusters, typename Graph, typename Components>
void assign_biconnected_component_ids(Turns& turns, Clusters const& clusters,
Graph const& graph, Components const& component, state_type const& state)
{
auto node_id_from_it = [&state](auto const& it)
{
return it->second.is_extra
? it->second.original_node_id
: it->second.node_id;
};

typename graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(graph); ei != ei_end; ++ei)
{
auto it_source = state.vertex_map.find(source(*ei, graph));
auto it_target = state.vertex_map.find(target(*ei, graph));
if (it_source == state.vertex_map.end() || it_target == state.vertex_map.end())
{
continue;
}

auto const source_node_id = node_id_from_it(it_source);
auto const target_node_id = node_id_from_it(it_target);

auto const source_turn_indices = get_turn_indices_by_node_id(turns, clusters, source_node_id);

// Assign the component to all the operations
// going from the source node to the target node.
for (auto const& turn_index : source_turn_indices)
{
auto& source_turn = turns[turn_index];
for (std::size_t j = 0; j < 2; j++)
{
auto& op = source_turn.operations[j];
if (op.enriched.travels_to_ip_index < 0)
{
continue;
}
auto const travels_to_node_id = get_node_id(turns, op.enriched.travels_to_ip_index);
if (travels_to_node_id == target_node_id)
{
op.enriched.component_id = static_cast<int>(component[*ei]);
}
}
}
}
}

template <operation_type TargetOperation, typename Turns, typename Clusters>
void detect_biconnected_components(Turns& turns, Clusters const& clusters)
{
using graph_t = boost::adjacency_list
<
boost::vecS,
boost::vecS,
boost::undirectedS,
boost::no_property,
boost::property<edge_component, std::size_t>
>;
using vertex_t = boost::graph_traits <graph_t>::vertex_descriptor;

// Mapping to add turns to vertices, count them, and then build the graph.
// (It is convenient if the vertex index is the same as the turn index.
// Therefore the default mapping is made like that, extra vertices
// are added later)

state_type state;
state.extra_node_id = static_cast<std::size_t>(turns.size());

fill_vertex_map<TargetOperation>(turns, clusters, state);

// Build the graph from the vertices
graph_t graph(state.vertex_map.size());
for (auto const& key_value : state.vertex_map)
{
auto const vertex_index = key_value.first;
for (auto const& target_vertex_index : key_value.second.target_vertex_indices)
{
add_edge(vertex_index, target_vertex_index, graph);
}
}

edge_component ec;
auto component = get(ec, graph);
biconnected_components(graph, component);
fix_components(component, graph);

assign_biconnected_component_ids(turns, clusters, graph, component, state);
}


}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DETECT_ARTICULATION_POINTS_HPP
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ struct enrichment_info
, rank(-1)
, zone(-1)
, region_id(-1)
, isolated(false)
{}

inline signed_size_type get_next_turn_index() const
Expand Down Expand Up @@ -69,7 +68,8 @@ struct enrichment_info
signed_size_type rank; // in cluster
signed_size_type zone; // open zone, in cluster
signed_size_type region_id;
bool isolated;

signed_size_type component_id{-1};
};


Expand Down
61 changes: 61 additions & 0 deletions include/boost/geometry/algorithms/detail/overlay/graph_util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Boost.Geometry

// Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GRAPH_UTIL_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GRAPH_UTIL_HPP

#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/adjacency_list.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{

struct edge_component
{
using kind = edge_property_tag;
};

// It appears that in an undirected graph, the components for two edges are sometimes different.
// Fix that. To be found out why this is.
template <typename Graph, typename Components>
void fix_components(Components& components, Graph const& g)
{
typename graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
{
auto& component = components[*ei];

auto const source_vertex = source(*ei, g);
auto const target_vertex = target(*ei, g);

// Get the reverse edge and its component
auto const reverse_edge_pair = edge(target_vertex, source_vertex, g);
if (! reverse_edge_pair.second)
{
continue;
}

auto& reverse_component = components[reverse_edge_pair.first];

if (component != reverse_component)
{
component = reverse_component;
}
}
}

}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GRAPH_UTIL_HPP
Loading
Loading