Skip to content

Commit f80e5db

Browse files
Moritz Kobitzschdanpat
authored andcommitted
add support for visualising turn penalties in MLD Debug tiles (#4157)
- template function for tile functionality with edge finder operator - refactors unit tests into single function (reduce code duplication) - adds unit tests for core-ch
1 parent 35550d8 commit f80e5db

File tree

6 files changed

+223
-86
lines changed

6 files changed

+223
-86
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- .osrm.nodes file was renamed to .nbg_nodes and .ebg_nodes was added
1919
- Guidance
2020
- #4075 Changed counting of exits on service roundabouts
21+
- Debug Tiles
22+
- added support for visualising turn penalties to the MLD plugin
2123
- Bugfixes
2224
- Fixed a copy/paste issue assigning wrong directions in similar turns (left over right)
2325
- #4074: fixed a bug that would announce entering highway ramps as u-turns

include/engine/algorithm.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ template <> struct HasShortestPathSearch<mld::Algorithm> final : std::true_type
102102
template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
103103
{
104104
};
105+
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
106+
{
107+
};
105108
}
106109
}
107110
}

include/engine/routing_algorithms.hpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,6 @@ RoutingAlgorithms<routing_algorithms::mld::Algorithm>::ManyToManySearch(
221221
{
222222
throw util::exception("ManyToManySearch is not implemented");
223223
}
224-
225-
template <>
226-
inline std::vector<routing_algorithms::TurnData>
227-
RoutingAlgorithms<routing_algorithms::mld::Algorithm>::GetTileTurns(
228-
const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &,
229-
const std::vector<std::size_t> &) const
230-
{
231-
throw util::exception("GetTileTurns is not implemented");
232-
}
233224
}
234225
}
235226

include/engine/routing_algorithms/tile_turns.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
3333
const std::vector<RTreeLeaf> &edges,
3434
const std::vector<std::size_t> &sorted_edge_indexes);
3535

36+
std::vector<TurnData>
37+
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
38+
const std::vector<RTreeLeaf> &edges,
39+
const std::vector<std::size_t> &sorted_edge_indexes);
40+
3641
} // namespace routing_algorithms
3742
} // namespace engine
3843
} // namespace osrm

src/engine/routing_algorithms/tile_turns.cpp

Lines changed: 139 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,33 @@ namespace engine
77
namespace routing_algorithms
88
{
99

10-
std::vector<TurnData>
11-
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
12-
const std::vector<RTreeLeaf> &edges,
13-
const std::vector<std::size_t> &sorted_edge_indexes)
10+
namespace
1411
{
15-
std::vector<TurnData> all_turn_data;
12+
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
13+
// When we create these, we insure that (source, target) and packed_geometry_id
14+
// are all pointed in the same direction.
15+
struct EdgeBasedNodeInfo
16+
{
17+
bool is_geometry_forward; // Is the geometry forward or reverse?
18+
unsigned packed_geometry_id;
19+
};
1620

17-
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
18-
// When we create these, we insure that (source, target) and packed_geometry_id
19-
// are all pointed in the same direction.
20-
struct EdgeBasedNodeInfo
21-
{
22-
bool is_geometry_forward; // Is the geometry forward or reverse?
23-
unsigned packed_geometry_id;
24-
};
21+
struct SegmentData
22+
{
23+
NodeID target_node;
24+
EdgeID edge_based_node_id;
25+
};
26+
27+
template <typename edge_extractor, typename datafacade>
28+
std::vector<TurnData> generateTurns(const datafacade &facade,
29+
const std::vector<RTreeLeaf> &edges,
30+
const std::vector<std::size_t> &sorted_edge_indexes,
31+
edge_extractor const &find_edge)
32+
{
2533
// Lookup table for edge-based-nodes
2634
std::unordered_map<NodeID, EdgeBasedNodeInfo> edge_based_node_info;
27-
28-
struct SegmentData
29-
{
30-
NodeID target_node;
31-
EdgeID edge_based_node_id;
32-
};
33-
3435
std::unordered_map<NodeID, std::vector<SegmentData>> directed_graph;
36+
3537
// Reserve enough space for unique edge-based-nodes on every edge.
3638
// Only a tile with all unique edges will use this much, but
3739
// it saves us a bunch of re-allocations during iteration.
@@ -41,8 +43,10 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
4143
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
4244
};
4345

44-
// Build an adjacency list for all the road segments visible in
45-
// the tile
46+
// To build a tile, we can only rely on the r-tree to quickly find all data visible within the
47+
// tile itself. The Rtree returns a series of segments that may or may not offer turns
48+
// associated with them. To be able to extract turn penalties, we extract a node based graph
49+
// from our edge based representation.
4650
for (const auto &edge_index : sorted_edge_indexes)
4751
{
4852
const auto &edge = edges[edge_index];
@@ -79,28 +83,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
7983
}
8084
}
8185

86+
// Make sure we traverse the startnodes in a consistent order
87+
// to ensure identical PBF encoding on all platforms.
88+
std::vector<NodeID> sorted_startnodes;
89+
sorted_startnodes.reserve(directed_graph.size());
90+
std::transform(directed_graph.begin(),
91+
directed_graph.end(),
92+
std::back_inserter(sorted_startnodes),
93+
[](auto const &node) { return node.first; });
94+
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
95+
96+
std::vector<TurnData> all_turn_data;
97+
8298
// Given a turn:
8399
// u---v
84100
// |
85101
// w
86102
// uv is the "approach"
87103
// vw is the "exit"
88-
std::vector<contractor::QueryEdge::EdgeData> unpacked_shortcut;
89104
std::vector<EdgeWeight> approach_weight_vector;
90105
std::vector<EdgeWeight> approach_duration_vector;
91106

92-
// Make sure we traverse the startnodes in a consistent order
93-
// to ensure identical PBF encoding on all platforms.
94-
std::vector<NodeID> sorted_startnodes;
95-
sorted_startnodes.reserve(directed_graph.size());
96-
for (const auto &startnode : directed_graph)
97-
sorted_startnodes.push_back(startnode.first);
98-
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
99-
100107
// Look at every node in the directed graph we created
101108
for (const auto &startnode : sorted_startnodes)
102109
{
103-
const auto &nodedata = directed_graph[startnode];
110+
BOOST_ASSERT(directed_graph.find(startnode) != directed_graph.end());
111+
const auto &nodedata = directed_graph.find(startnode)->second;
104112
// For all the outgoing edges from the node
105113
for (const auto &approachedge : nodedata)
106114
{
@@ -110,7 +118,7 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
110118
continue;
111119

112120
// For each of the outgoing edges from our target coordinate
113-
for (const auto &exit_edge : directed_graph[approachedge.target_node])
121+
for (const auto &exit_edge : directed_graph.find(approachedge.target_node)->second)
114122
{
115123
// If the next edge has the same edge_based_node_id, then it's
116124
// not a turn, so skip it
@@ -132,55 +140,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
132140
//
133141
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
134142
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
135-
EdgeID smaller_edge_id =
136-
facade.FindSmallestEdge(approachedge.edge_based_node_id,
137-
exit_edge.edge_based_node_id,
138-
[](const contractor::QueryEdge::EdgeData &data) {
139-
return data.forward && !data.shortcut;
140-
});
141-
142-
// Depending on how the graph is constructed, we might have to look for
143-
// a backwards edge instead. They're equivalent, just one is available for
144-
// a forward routing search, and one is used for the backwards dijkstra
145-
// steps. Their weight should be the same, we can use either one.
146-
// If we didn't find a forward edge, try for a backward one
147-
if (SPECIAL_EDGEID == smaller_edge_id)
148-
{
149-
smaller_edge_id =
150-
facade.FindSmallestEdge(exit_edge.edge_based_node_id,
151-
approachedge.edge_based_node_id,
152-
[](const contractor::QueryEdge::EdgeData &data) {
153-
return data.backward && !data.shortcut;
154-
});
155-
}
143+
EdgeID edge_based_edge_id =
144+
find_edge(approachedge.edge_based_node_id, exit_edge.edge_based_node_id);
156145

157-
// If no edge was found, it means that there's no connection between these
158-
// nodes, due to oneways or turn restrictions. Given the edge-based-nodes
159-
// that we're examining here, we *should* only find directly-connected
160-
// edges, not shortcuts
161-
if (smaller_edge_id != SPECIAL_EDGEID)
146+
if (edge_based_edge_id != SPECIAL_EDGEID)
162147
{
163-
const auto &data = facade.GetEdgeData(smaller_edge_id);
164-
BOOST_ASSERT_MSG(!data.shortcut, "Connecting edge must not be a shortcut");
148+
const auto &data = facade.GetEdgeData(edge_based_edge_id);
165149

166150
// Now, calculate the sum of the weight of all the segments.
167-
if (edge_based_node_info[approachedge.edge_based_node_id].is_geometry_forward)
151+
if (edge_based_node_info.find(approachedge.edge_based_node_id)
152+
->second.is_geometry_forward)
168153
{
169154
approach_weight_vector = facade.GetUncompressedForwardWeights(
170-
edge_based_node_info[approachedge.edge_based_node_id]
171-
.packed_geometry_id);
155+
edge_based_node_info.find(approachedge.edge_based_node_id)
156+
->second.packed_geometry_id);
172157
approach_duration_vector = facade.GetUncompressedForwardDurations(
173-
edge_based_node_info[approachedge.edge_based_node_id]
174-
.packed_geometry_id);
158+
edge_based_node_info.find(approachedge.edge_based_node_id)
159+
->second.packed_geometry_id);
175160
}
176161
else
177162
{
178163
approach_weight_vector = facade.GetUncompressedReverseWeights(
179-
edge_based_node_info[approachedge.edge_based_node_id]
180-
.packed_geometry_id);
164+
edge_based_node_info.find(approachedge.edge_based_node_id)
165+
->second.packed_geometry_id);
181166
approach_duration_vector = facade.GetUncompressedReverseDurations(
182-
edge_based_node_info[approachedge.edge_based_node_id]
183-
.packed_geometry_id);
167+
edge_based_node_info.find(approachedge.edge_based_node_id)
168+
->second.packed_geometry_id);
184169
}
185170
const auto sum_node_weight = std::accumulate(approach_weight_vector.begin(),
186171
approach_weight_vector.end(),
@@ -239,6 +224,90 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
239224
return all_turn_data;
240225
}
241226

227+
} // namespace
228+
229+
// CH Version of finding all turn penalties. Here is where the actual work is happening
230+
std::vector<TurnData>
231+
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
232+
const std::vector<RTreeLeaf> &edges,
233+
const std::vector<std::size_t> &sorted_edge_indexes)
234+
{
235+
// Define how to find the representative edge between two edge based nodes for a CH
236+
struct EdgeFinderCH
237+
{
238+
EdgeFinderCH(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade)
239+
: facade(facade)
240+
{
241+
}
242+
const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade;
243+
244+
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
245+
{
246+
// Find the connection between our source road and the target node
247+
// Since we only want to find direct edges, we cannot check shortcut edges here.
248+
// Otherwise we might find a forward edge even though a shorter backward edge
249+
// exists (due to oneways).
250+
//
251+
// a > - > - > - b
252+
// | |
253+
// |------ c ----|
254+
//
255+
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
256+
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
257+
EdgeID edge_id = facade.FindSmallestEdge(
258+
approach_node, exit_node, [](const contractor::QueryEdge::EdgeData &data) {
259+
return data.forward && !data.shortcut;
260+
});
261+
262+
// Depending on how the graph is constructed, we might have to look for
263+
// a backwards edge instead. They're equivalent, just one is available for
264+
// a forward routing search, and one is used for the backwards dijkstra
265+
// steps. Their weight should be the same, we can use either one.
266+
// If we didn't find a forward edge, try for a backward one
267+
if (SPECIAL_EDGEID == edge_id)
268+
{
269+
edge_id = facade.FindSmallestEdge(
270+
exit_node, approach_node, [](const contractor::QueryEdge::EdgeData &data) {
271+
return data.backward && !data.shortcut;
272+
});
273+
}
274+
275+
BOOST_ASSERT_MSG(edge_id == SPECIAL_EDGEID || !facade.GetEdgeData(edge_id).shortcut,
276+
"Connecting edge must not be a shortcut");
277+
return edge_id;
278+
}
279+
};
280+
281+
EdgeFinderCH edge_finder(facade);
282+
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
283+
}
284+
285+
// MLD version to find all turns
286+
std::vector<TurnData>
287+
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
288+
const std::vector<RTreeLeaf> &edges,
289+
const std::vector<std::size_t> &sorted_edge_indexes)
290+
{
291+
// Define how to find the representative edge between two edge-based-nodes for a MLD
292+
struct EdgeFinderMLD
293+
{
294+
EdgeFinderMLD(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade)
295+
: facade(facade)
296+
{
297+
}
298+
const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade;
299+
300+
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
301+
{
302+
return facade.FindEdge(approach_node, exit_node);
303+
}
304+
};
305+
306+
EdgeFinderMLD edge_finder(facade);
307+
308+
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
309+
}
310+
242311
} // namespace routing_algorithms
243312
} // namespace engine
244313
} // namespace osrm

0 commit comments

Comments
 (0)