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