1 #include "engine/routing_algorithms/tile_turns.hpp"
2
3 namespace osrm
4 {
5 namespace engine
6 {
7 namespace routing_algorithms
8 {
9
10 namespace
11 {
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 };
20
21 struct SegmentData
22 {
23 NodeID target_node;
24 EdgeID edge_based_node_id;
25 };
26
27 template <typename edge_extractor, typename datafacade>
generateTurns(const datafacade & facade,const std::vector<RTreeLeaf> & edges,const std::vector<std::size_t> & sorted_edge_indexes,edge_extractor const & find_edge)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 {
33 // Lookup table for edge-based-nodes
34 std::unordered_map<NodeID, EdgeBasedNodeInfo> edge_based_node_info;
35 std::unordered_map<NodeID, std::vector<SegmentData>> directed_graph;
36
37 // Reserve enough space for unique edge-based-nodes on every edge.
38 // Only a tile with all unique edges will use this much, but
39 // it saves us a bunch of re-allocations during iteration.
40 directed_graph.reserve(edges.size() * 2);
41
42 const auto get_geometry_id = [&facade](auto edge) {
43 return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
44 };
45
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.
50 for (const auto &edge_index : sorted_edge_indexes)
51 {
52 const auto &edge = edges[edge_index];
53 if (edge.forward_segment_id.enabled)
54 {
55 // operator[] will construct an empty vector at [edge.u] if there is no value.
56 directed_graph[edge.u].push_back({edge.v, edge.forward_segment_id.id});
57 if (edge_based_node_info.count(edge.forward_segment_id.id) == 0)
58 {
59 edge_based_node_info[edge.forward_segment_id.id] = {true, get_geometry_id(edge)};
60 }
61 else
62 {
63 BOOST_ASSERT(edge_based_node_info[edge.forward_segment_id.id].is_geometry_forward ==
64 true);
65 BOOST_ASSERT(edge_based_node_info[edge.forward_segment_id.id].packed_geometry_id ==
66 get_geometry_id(edge));
67 }
68 }
69 if (edge.reverse_segment_id.enabled)
70 {
71 directed_graph[edge.v].push_back({edge.u, edge.reverse_segment_id.id});
72 if (edge_based_node_info.count(edge.reverse_segment_id.id) == 0)
73 {
74 edge_based_node_info[edge.reverse_segment_id.id] = {false, get_geometry_id(edge)};
75 }
76 else
77 {
78 BOOST_ASSERT(edge_based_node_info[edge.reverse_segment_id.id].is_geometry_forward ==
79 false);
80 BOOST_ASSERT(edge_based_node_info[edge.reverse_segment_id.id].packed_geometry_id ==
81 get_geometry_id(edge));
82 }
83 }
84 }
85
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
98 // Given a turn:
99 // u---v
100 // |
101 // w
102 // uv is the "approach"
103 // vw is the "exit"
104 // Look at every node in the directed graph we created
105 for (const auto &startnode : sorted_startnodes)
106 {
107 BOOST_ASSERT(directed_graph.find(startnode) != directed_graph.end());
108 const auto &nodedata = directed_graph.find(startnode)->second;
109 // For all the outgoing edges from the node
110 for (const auto &approachedge : nodedata)
111 {
112 // If the target of this edge doesn't exist in our directed
113 // graph, it's probably outside the tile, so we can skip it
114 if (directed_graph.count(approachedge.target_node) == 0)
115 continue;
116
117 // For each of the outgoing edges from our target coordinate
118 for (const auto &exit_edge : directed_graph.find(approachedge.target_node)->second)
119 {
120 // If the next edge has the same edge_based_node_id, then it's
121 // not a turn, so skip it
122 if (approachedge.edge_based_node_id == exit_edge.edge_based_node_id)
123 continue;
124
125 // Skip u-turns
126 if (startnode == exit_edge.target_node)
127 continue;
128
129 // Find the connection between our source road and the target node
130 // Since we only want to find direct edges, we cannot check shortcut edges here.
131 // Otherwise we might find a forward edge even though a shorter backward edge
132 // exists (due to oneways).
133 //
134 // a > - > - > - b
135 // | |
136 // |------ c ----|
137 //
138 // would offer a backward edge at `b` to `a` (due to the oneway from a to b)
139 // but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
140 EdgeID edge_based_edge_id =
141 find_edge(approachedge.edge_based_node_id, exit_edge.edge_based_node_id);
142
143 if (edge_based_edge_id != SPECIAL_EDGEID)
144 {
145 const auto &data = facade.GetEdgeData(edge_based_edge_id);
146
147 // Now, calculate the sum of the weight of all the segments.
148 const auto turn_weight = facade.GetWeightPenaltyForEdgeID(data.turn_id);
149 const auto turn_duration = facade.GetDurationPenaltyForEdgeID(data.turn_id);
150 const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id);
151
152 // Find the three nodes that make up the turn movement)
153 const auto node_from = startnode;
154 const auto node_via = approachedge.target_node;
155 const auto node_to = exit_edge.target_node;
156
157 const auto coord_from = facade.GetCoordinateOfNode(node_from);
158 const auto coord_via = facade.GetCoordinateOfNode(node_via);
159 const auto coord_to = facade.GetCoordinateOfNode(node_to);
160
161 // Calculate the bearing that we approach the intersection at
162 const auto angle_in = static_cast<int>(
163 util::coordinate_calculation::bearing(coord_from, coord_via));
164
165 const auto exit_bearing = static_cast<int>(
166 util::coordinate_calculation::bearing(coord_via, coord_to));
167
168 // Figure out the angle of the turn
169 auto turn_angle = exit_bearing - angle_in;
170 while (turn_angle > 180)
171 {
172 turn_angle -= 360;
173 }
174 while (turn_angle < -180)
175 {
176 turn_angle += 360;
177 }
178
179 // Save everything we need to later add all the points to the tile.
180 // We need the coordinate of the intersection, the angle in, the turn
181 // angle and the turn cost.
182 all_turn_data.push_back(TurnData{coord_via,
183 angle_in,
184 turn_angle,
185 turn_weight,
186 turn_duration,
187 turn_instruction});
188 }
189 }
190 }
191 }
192
193 return all_turn_data;
194 }
195
196 } // namespace
197
198 // CH Version of finding all turn penalties. Here is where the actual work is happening
getTileTurns(const DataFacade<ch::Algorithm> & facade,const std::vector<RTreeLeaf> & edges,const std::vector<std::size_t> & sorted_edge_indexes)199 std::vector<TurnData> getTileTurns(const DataFacade<ch::Algorithm> &facade,
200 const std::vector<RTreeLeaf> &edges,
201 const std::vector<std::size_t> &sorted_edge_indexes)
202 {
203 // Define how to find the representative edge between two edge based nodes for a CH
204 struct EdgeFinderCH
205 {
206 EdgeFinderCH(const DataFacade<ch::Algorithm> &facade) : facade(facade) {}
207 const DataFacade<ch::Algorithm> &facade;
208
209 EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
210 {
211 // Find the connection between our source road and the target node
212 // Since we only want to find direct edges, we cannot check shortcut edges here.
213 // Otherwise we might find a forward edge even though a shorter backward edge
214 // exists (due to oneways).
215 //
216 // a > - > - > - b
217 // | |
218 // |------ c ----|
219 //
220 // would offer a backward edge at `b` to `a` (due to the oneway from a to b)
221 // but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
222 EdgeID edge_id = facade.FindSmallestEdge(
223 approach_node, exit_node, [](const contractor::QueryEdge::EdgeData &data) {
224 return data.forward && !data.shortcut;
225 });
226
227 // Depending on how the graph is constructed, we might have to look for
228 // a backwards edge instead. They're equivalent, just one is available for
229 // a forward routing search, and one is used for the backwards dijkstra
230 // steps. Their weight should be the same, we can use either one.
231 // If we didn't find a forward edge, try for a backward one
232 if (SPECIAL_EDGEID == edge_id)
233 {
234 edge_id = facade.FindSmallestEdge(
235 exit_node, approach_node, [](const contractor::QueryEdge::EdgeData &data) {
236 return data.backward && !data.shortcut;
237 });
238 }
239
240 BOOST_ASSERT_MSG(edge_id == SPECIAL_EDGEID || !facade.GetEdgeData(edge_id).shortcut,
241 "Connecting edge must not be a shortcut");
242 return edge_id;
243 }
244 };
245
246 EdgeFinderCH edge_finder(facade);
247 return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
248 }
249
250 // MLD version to find all turns
getTileTurns(const DataFacade<mld::Algorithm> & facade,const std::vector<RTreeLeaf> & edges,const std::vector<std::size_t> & sorted_edge_indexes)251 std::vector<TurnData> getTileTurns(const DataFacade<mld::Algorithm> &facade,
252 const std::vector<RTreeLeaf> &edges,
253 const std::vector<std::size_t> &sorted_edge_indexes)
254 {
255 // Define how to find the representative edge between two edge-based-nodes for a MLD
256 struct EdgeFinderMLD
257 {
258 EdgeFinderMLD(const DataFacade<mld::Algorithm> &facade) : facade(facade) {}
259 const DataFacade<mld::Algorithm> &facade;
260
261 EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
262 {
263 return facade.FindEdge(approach_node, exit_node);
264 }
265 };
266
267 EdgeFinderMLD edge_finder(facade);
268
269 return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
270 }
271
272 } // namespace routing_algorithms
273 } // namespace engine
274 } // namespace osrm
275