Currently OSRM only supports turn restrictions with a single via-node or one via-way. OSM allows for multiple via-ways to represent longer and more complex restrictions. This PR extends the use of duplicate nodes for representng via-way turn restrictions to also support multi via-way restrictions. Effectively, this increases the edge-based graph size by the number of edges in multi via-way restrictions. However, given the low number of these restrictions it has little effect on total graph size. In addition, we add a new step in the extraction phase that constructs a restriction graph to support more complex relationships between restrictions, such as nested restrictions and overlapping restrictions.
320 lines
13 KiB
C++
320 lines
13 KiB
C++
#include "extractor/intersection/intersection_analysis.hpp"
|
|
|
|
#include "extractor/graph_compressor.hpp"
|
|
|
|
#include "../common/range_tools.hpp"
|
|
#include "../unit_tests/mocks/mock_scripting_environment.hpp"
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
BOOST_AUTO_TEST_SUITE(intersection_analysis_tests)
|
|
|
|
using namespace osrm;
|
|
using namespace osrm::guidance;
|
|
using namespace osrm::extractor;
|
|
using namespace osrm::extractor::intersection;
|
|
using InputEdge = util::NodeBasedDynamicGraph::InputEdge;
|
|
using Graph = util::NodeBasedDynamicGraph;
|
|
|
|
BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
|
|
{
|
|
std::unordered_set<NodeID> barrier_nodes{6};
|
|
std::unordered_set<NodeID> traffic_lights;
|
|
std::vector<NodeBasedEdgeAnnotation> annotations{
|
|
{EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false},
|
|
{EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}};
|
|
std::vector<TurnRestriction> restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}};
|
|
CompressedEdgeContainer container;
|
|
test::MockScriptingEnvironment scripting_environment;
|
|
std::vector<UnresolvedManeuverOverride> maneuver_overrides;
|
|
|
|
TurnLanesIndexedArray turn_lanes_data{{0, 0, 3},
|
|
{TurnLaneType::uturn | TurnLaneType::left,
|
|
TurnLaneType::straight,
|
|
TurnLaneType::straight | TurnLaneType::right}};
|
|
|
|
// Graph with an additional turn restriction 0→2→1 and bollard at 6
|
|
// 0→5↔6↔7
|
|
// ↕
|
|
// 1↔2←3
|
|
// ↓
|
|
// 4
|
|
const auto unit_edge =
|
|
[](const NodeID from, const NodeID to, bool allowed, AnnotationID annotation) {
|
|
return InputEdge{from,
|
|
to,
|
|
1,
|
|
1,
|
|
1,
|
|
GeometryID{0, false},
|
|
!allowed,
|
|
NodeBasedEdgeClassification(),
|
|
annotation};
|
|
};
|
|
|
|
std::vector<InputEdge> edges = {unit_edge(0, 2, true, 1),
|
|
unit_edge(0, 5, true, 0),
|
|
unit_edge(1, 2, true, 0),
|
|
unit_edge(2, 0, true, 0),
|
|
unit_edge(2, 1, true, 0),
|
|
unit_edge(2, 3, false, 0),
|
|
unit_edge(2, 4, true, 0),
|
|
unit_edge(3, 2, true, 0),
|
|
unit_edge(4, 2, false, 0),
|
|
unit_edge(5, 0, false, 0),
|
|
unit_edge(5, 6, true, 0),
|
|
unit_edge(6, 5, true, 0),
|
|
unit_edge(6, 7, true, 0),
|
|
unit_edge(7, 6, true, 0)};
|
|
IntersectionEdgeGeometries edge_geometries{
|
|
{0, 180, 180, 10.}, // 0→2
|
|
{1, 90, 90, 10.}, // 0→5
|
|
{2, 90, 90, 10.}, // 1→2
|
|
{3, 0, 0, 10.}, // 2→0
|
|
{4, 270, 270, 10.}, // 2→1
|
|
{5, 90, 90, 10.}, // 2→3
|
|
{6, 180, 180, 10.}, // 2→4
|
|
{7, 270, 270, 10.}, // 3→2
|
|
{8, 0, 0, 10.}, // 4→2
|
|
{9, 270, 270, 10.}, // 5→0
|
|
{10, 90, 90, 10.}, // 5→6
|
|
{11, 270, 270, 10.}, // 6→5
|
|
{12, 90, 90, 10.}, // 6→7
|
|
{13, 270, 270, 10.} // 7→6
|
|
};
|
|
|
|
Graph graph(8, edges);
|
|
|
|
GraphCompressor().Compress(barrier_nodes,
|
|
traffic_lights,
|
|
scripting_environment,
|
|
restrictions,
|
|
maneuver_overrides,
|
|
graph,
|
|
annotations,
|
|
container);
|
|
|
|
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3);
|
|
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4);
|
|
|
|
EdgeBasedNodeDataContainer node_data_container(
|
|
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
|
RestrictionGraph restriction_graph = constructRestrictionGraph(restrictions);
|
|
RestrictionMap restriction_map(restriction_graph);
|
|
|
|
const auto connectivity_matrix = [&](NodeID node) {
|
|
std::vector<bool> result;
|
|
const auto incoming_edges = getIncomingEdges(graph, node);
|
|
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
|
for (const auto incoming_edge : incoming_edges)
|
|
{
|
|
for (const auto outgoing_edge : outgoing_edges)
|
|
{
|
|
result.push_back(isTurnAllowed(graph,
|
|
node_data_container,
|
|
restriction_map,
|
|
barrier_nodes,
|
|
edge_geometries,
|
|
turn_lanes_data,
|
|
incoming_edge,
|
|
outgoing_edge));
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(0), 1, 1); // from node 2 allowed U-turn and to node 5
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(1), 1); // from node 2 allowed U-turn
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(2),
|
|
// clang-format off
|
|
1, 0, 0, 1, // from node 0 to node 4 and a U-turn at 2
|
|
1, 0, 0, 1, // from node 1 to nodes 0 and 4
|
|
1, 1, 0, 1 // from node 3 to nodes 0, 1 and 4
|
|
// clang-format on
|
|
);
|
|
REQUIRE_SIZE_RANGE(connectivity_matrix(3), 0); // no incoming edges, empty matrix
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(4), 0); // from node 2 not allowed U-turn
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(5),
|
|
// clang-format off
|
|
0, 1, // from node 0 to node 6
|
|
0, 1, // from node 6 a U-turn to node 6
|
|
// clang-format on
|
|
);
|
|
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(6),
|
|
// clang-format off
|
|
1, 0, // from node 5 a U-turn to node 5
|
|
0, 1, // from node 7 a U-turn to node 7
|
|
// clang-format on
|
|
);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
|
|
{
|
|
std::unordered_set<NodeID> barrier_nodes;
|
|
std::unordered_set<NodeID> traffic_lights;
|
|
std::vector<NodeBasedEdgeAnnotation> annotations;
|
|
std::vector<TurnRestriction> restrictions;
|
|
CompressedEdgeContainer container;
|
|
test::MockScriptingEnvironment scripting_environment;
|
|
std::vector<UnresolvedManeuverOverride> maneuver_overrides;
|
|
|
|
TurnLanesIndexedArray turn_lanes_data;
|
|
|
|
// Graph with roundabout edges 5→0→2
|
|
// 1 2 3
|
|
// ↘ ↑ ↙
|
|
// 0
|
|
// ↙ ↑ ↘
|
|
// 4 5 6
|
|
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed, bool roundabout) {
|
|
return InputEdge{from,
|
|
to,
|
|
1,
|
|
1,
|
|
1,
|
|
GeometryID{0, false},
|
|
!allowed,
|
|
NodeBasedEdgeClassification{
|
|
true, false, false, roundabout, false, false, false, {}, 0, 0},
|
|
0};
|
|
};
|
|
std::vector<InputEdge> edges = {unit_edge(0, 1, false, false),
|
|
unit_edge(0, 2, true, true),
|
|
unit_edge(0, 3, false, false),
|
|
unit_edge(0, 4, true, false),
|
|
unit_edge(0, 5, false, true),
|
|
unit_edge(0, 6, true, false),
|
|
unit_edge(1, 0, true, false),
|
|
unit_edge(2, 0, false, true),
|
|
unit_edge(3, 0, true, false),
|
|
unit_edge(4, 0, false, false),
|
|
unit_edge(5, 0, true, true),
|
|
unit_edge(6, 0, false, false)};
|
|
IntersectionEdgeGeometries edge_geometries{
|
|
{0, 315, 315, 10}, // 0→1
|
|
{1, 0, 0, 10}, // 0→2
|
|
{2, 45, 45, 10}, // 0→3
|
|
{3, 225, 225, 10}, // 0→4
|
|
{4, 180, 180, 10}, // 0→5
|
|
{5, 135, 135, 10}, // 0→6
|
|
{6, 135, 135, 10}, // 1→0
|
|
{7, 180, 180, 10}, // 2→0
|
|
{8, 225, 225, 10}, // 3→0
|
|
{9, 45, 45, 10}, // 4→0
|
|
{10, 0, 0, 10}, // 5→0
|
|
{11, 315, 315, 10} // 6→0
|
|
};
|
|
|
|
Graph graph(7, edges);
|
|
|
|
GraphCompressor().Compress(barrier_nodes,
|
|
traffic_lights,
|
|
scripting_environment,
|
|
restrictions,
|
|
maneuver_overrides,
|
|
graph,
|
|
annotations,
|
|
container);
|
|
|
|
REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3);
|
|
REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6);
|
|
|
|
EdgeBasedNodeDataContainer node_data_container(
|
|
std::vector<EdgeBasedNode>(graph.GetNumberOfEdges()), annotations);
|
|
|
|
RestrictionGraph restriction_graph = constructRestrictionGraph(restrictions);
|
|
RestrictionMap restriction_map(restriction_graph);
|
|
|
|
const auto connectivity_matrix = [&](NodeID node) {
|
|
std::vector<bool> result;
|
|
const auto incoming_edges = getIncomingEdges(graph, node);
|
|
const auto outgoing_edges = getOutgoingEdges(graph, node);
|
|
for (const auto incoming_edge : incoming_edges)
|
|
{
|
|
for (const auto outgoing_edge : outgoing_edges)
|
|
{
|
|
result.push_back(isTurnAllowed(graph,
|
|
node_data_container,
|
|
restriction_map,
|
|
barrier_nodes,
|
|
edge_geometries,
|
|
turn_lanes_data,
|
|
incoming_edge,
|
|
outgoing_edge));
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
CHECK_EQUAL_RANGE(connectivity_matrix(0),
|
|
// clang-format off
|
|
0, 1, 0, 0, 0, 1, // from node 1 to nodes 2 and 6
|
|
0, 1, 0, 1, 0, 0, // from node 3 to nodes 2 and 4
|
|
0, 1, 0, 1, 0, 1 // from node 5 to nodes 2, 4 and 6
|
|
// clang-format on
|
|
);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(skip_degree_two_nodes)
|
|
{
|
|
std::unordered_set<NodeID> barrier_nodes{1};
|
|
std::unordered_set<NodeID> traffic_lights{2};
|
|
std::vector<NodeBasedEdgeAnnotation> annotations(1);
|
|
std::vector<TurnRestriction> restrictions;
|
|
CompressedEdgeContainer container;
|
|
test::MockScriptingEnvironment scripting_environment;
|
|
std::vector<UnresolvedManeuverOverride> maneuver_overrides;
|
|
|
|
TurnLanesIndexedArray turn_lanes_data;
|
|
|
|
// Graph
|
|
//
|
|
// 0↔1→2↔3↔4→5 7
|
|
// ↑ ↕ ↕
|
|
// 6 8 ↔ 9
|
|
//
|
|
const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed) {
|
|
return InputEdge{
|
|
from, to, 1, 1, 1, GeometryID{0, false}, !allowed, NodeBasedEdgeClassification{}, 0};
|
|
};
|
|
std::vector<InputEdge> edges = {unit_edge(0, 1, true), // 0
|
|
unit_edge(1, 0, true),
|
|
unit_edge(1, 2, true),
|
|
unit_edge(2, 1, false),
|
|
unit_edge(2, 3, true),
|
|
unit_edge(3, 2, true), // 5
|
|
unit_edge(3, 4, true),
|
|
unit_edge(4, 3, true),
|
|
unit_edge(4, 5, true),
|
|
unit_edge(4, 6, false),
|
|
unit_edge(5, 4, false), // 10
|
|
unit_edge(6, 4, true),
|
|
// Circle
|
|
unit_edge(7, 8, true), // 12
|
|
unit_edge(7, 9, true),
|
|
unit_edge(8, 7, true),
|
|
unit_edge(8, 9, true),
|
|
unit_edge(9, 7, true),
|
|
unit_edge(9, 8, true)};
|
|
|
|
Graph graph(10, edges);
|
|
|
|
GraphCompressor().Compress(barrier_nodes,
|
|
traffic_lights,
|
|
scripting_environment,
|
|
restrictions,
|
|
maneuver_overrides,
|
|
graph,
|
|
annotations,
|
|
container);
|
|
|
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {0, 0}).edge), 4);
|
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {4, 7}).edge), 0);
|
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {5, 10}).edge), 4);
|
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {6, 11}).edge), 4);
|
|
BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {7, 12}).edge), 7);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|