1 #ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
2 #define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
3 
4 #include "extractor/travel_mode.hpp"
5 #include "extractor/turn_lane_types.hpp"
6 #include "guidance/turn_instruction.hpp"
7 #include "engine/datafacade/datafacade_base.hpp"
8 #include "engine/guidance/leg_geometry.hpp"
9 #include "engine/guidance/route_step.hpp"
10 #include "engine/guidance/step_maneuver.hpp"
11 #include "engine/internal_route_result.hpp"
12 #include "engine/phantom_node.hpp"
13 #include "util/bearing.hpp"
14 #include "util/coordinate.hpp"
15 #include "util/coordinate_calculation.hpp"
16 #include "util/guidance/entry_class.hpp"
17 #include "util/guidance/turn_lanes.hpp"
18 #include "util/typedefs.hpp"
19 
20 #include <boost/optional.hpp>
21 #include <cstddef>
22 #include <vector>
23 
24 namespace osrm
25 {
26 namespace engine
27 {
28 namespace guidance
29 {
30 namespace detail
31 {
32 std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry,
33                                           const PhantomNode &source_node,
34                                           const bool traversed_in_reverse);
35 std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry,
36                                           const PhantomNode &target_node,
37                                           const bool traversed_in_reverse);
38 } // namespace detail
39 
assembleSteps(const datafacade::BaseDataFacade & facade,const std::vector<PathData> & leg_data,const LegGeometry & leg_geometry,const PhantomNode & source_node,const PhantomNode & target_node,const bool source_traversed_in_reverse,const bool target_traversed_in_reverse)40 inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &facade,
41                                             const std::vector<PathData> &leg_data,
42                                             const LegGeometry &leg_geometry,
43                                             const PhantomNode &source_node,
44                                             const PhantomNode &target_node,
45                                             const bool source_traversed_in_reverse,
46                                             const bool target_traversed_in_reverse)
47 {
48     const double weight_multiplier = facade.GetWeightMultiplier();
49 
50     const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0., ZERO_WEIGHT = 0;
51     const constexpr char *NO_ROTARY_NAME = "";
52     const EdgeWeight source_weight =
53         source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
54     const EdgeWeight source_duration =
55         source_traversed_in_reverse ? source_node.reverse_duration : source_node.forward_duration;
56     const auto source_node_id = source_traversed_in_reverse ? source_node.reverse_segment_id.id
57                                                             : source_node.forward_segment_id.id;
58     const auto source_name_id = facade.GetNameIndex(source_node_id);
59     bool is_segregated = facade.IsSegregated(source_node_id);
60     const auto source_mode = facade.GetTravelMode(source_node_id);
61     auto source_classes = facade.GetClasses(facade.GetClassData(source_node_id));
62 
63     const EdgeWeight target_duration =
64         target_traversed_in_reverse ? target_node.reverse_duration : target_node.forward_duration;
65     const EdgeWeight target_weight =
66         target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight;
67     const auto target_node_id = target_traversed_in_reverse ? target_node.reverse_segment_id.id
68                                                             : target_node.forward_segment_id.id;
69     const auto target_name_id = facade.GetNameIndex(target_node_id);
70     const auto target_mode = facade.GetTravelMode(target_node_id);
71 
72     const auto number_of_segments = leg_geometry.GetNumberOfSegments();
73 
74     std::vector<RouteStep> steps;
75     steps.reserve(number_of_segments);
76 
77     std::size_t segment_index = 0;
78     BOOST_ASSERT(leg_geometry.locations.size() >= 2);
79 
80     auto bearings =
81         detail::getDepartBearings(leg_geometry, source_node, source_traversed_in_reverse);
82 
83     StepManeuver maneuver{source_node.location,
84                           bearings.first,
85                           bearings.second,
86                           osrm::guidance::TurnInstruction::NO_TURN(),
87                           WaypointType::Depart,
88                           0};
89 
90     IntermediateIntersection intersection{source_node.location,
91                                           std::vector<short>({bearings.second}),
92                                           std::vector<bool>({true}),
93                                           IntermediateIntersection::NO_INDEX,
94                                           0,
95                                           util::guidance::LaneTuple(),
96                                           {},
97                                           source_classes};
98 
99     if (leg_data.size() > 0)
100     {
101         // PathData saves the information we need of the segment _before_ the turn,
102         // but a RouteStep is with regard to the segment after the turn.
103         // We need to skip the first segment because it is already covered by the
104         // initial start of a route
105         EdgeWeight segment_duration = 0;
106         EdgeWeight segment_weight = 0;
107 
108         // some name changes are not announced in our processing. For these, we have to keep the
109         // first name on the segment
110         auto step_name_id = source_name_id;
111         for (std::size_t leg_data_index = 0; leg_data_index < leg_data.size(); ++leg_data_index)
112         {
113             const auto &path_point = leg_data[leg_data_index];
114             segment_duration += path_point.duration_until_turn;
115             segment_weight += path_point.weight_until_turn;
116 
117             // all changes to this check have to be matched with assemble_geometry
118             if (path_point.turn_instruction.type != osrm::guidance::TurnType::NoTurn)
119             {
120                 BOOST_ASSERT(segment_weight >= 0);
121                 const auto name = facade.GetNameForID(step_name_id);
122                 const auto ref = facade.GetRefForID(step_name_id);
123                 const auto pronunciation = facade.GetPronunciationForID(step_name_id);
124                 const auto destinations = facade.GetDestinationsForID(step_name_id);
125                 const auto exits = facade.GetExitsForID(step_name_id);
126                 const auto distance = leg_geometry.segment_distances[segment_index];
127                 // intersections contain the classes of exiting road
128                 intersection.classes = facade.GetClasses(path_point.classes);
129 
130                 steps.push_back(RouteStep{path_point.from_edge_based_node,
131                                           step_name_id,
132                                           is_segregated,
133                                           name.to_string(),
134                                           ref.to_string(),
135                                           pronunciation.to_string(),
136                                           destinations.to_string(),
137                                           exits.to_string(),
138                                           NO_ROTARY_NAME,
139                                           NO_ROTARY_NAME,
140                                           segment_duration / 10.,
141                                           distance,
142                                           segment_weight / weight_multiplier,
143                                           path_point.travel_mode,
144                                           maneuver,
145                                           leg_geometry.FrontIndex(segment_index),
146                                           leg_geometry.BackIndex(segment_index) + 1,
147                                           {intersection},
148                                           path_point.is_left_hand_driving});
149 
150                 if (leg_data_index + 1 < leg_data.size())
151                 {
152                     step_name_id = leg_data[leg_data_index + 1].name_id;
153                     is_segregated = leg_data[leg_data_index + 1].is_segregated;
154                 }
155                 else
156                 {
157                     step_name_id = facade.GetNameIndex(target_node_id);
158                     is_segregated = facade.IsSegregated(target_node_id);
159                 }
160 
161                 // extract bearings
162                 bearings = std::make_pair<std::uint16_t, std::uint16_t>(
163                     path_point.pre_turn_bearing.Get(), path_point.post_turn_bearing.Get());
164                 const auto bearing_class = facade.GetBearingClass(path_point.turn_via_node);
165                 auto bearing_data = bearing_class.getAvailableBearings();
166                 intersection.in = bearing_class.findMatchingBearing(bearings.first);
167                 intersection.out = bearing_class.findMatchingBearing(bearings.second);
168                 intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
169                 intersection.bearings.clear();
170                 intersection.bearings.reserve(bearing_data.size());
171                 intersection.lanes = path_point.lane_data.first;
172                 intersection.lane_description =
173                     path_point.lane_data.second != INVALID_LANE_DESCRIPTIONID
174                         ? facade.GetTurnDescription(path_point.lane_data.second)
175                         : extractor::TurnLaneDescription();
176 
177                 // Lanes in turn are bound by total number of lanes at the location
178                 BOOST_ASSERT(intersection.lanes.lanes_in_turn <=
179                              intersection.lane_description.size());
180                 // No lanes at location and no turn lane or lanes at location and lanes in turn
181                 BOOST_ASSERT((intersection.lane_description.empty() &&
182                               intersection.lanes.lanes_in_turn == 0) ||
183                              (!intersection.lane_description.empty() &&
184                               intersection.lanes.lanes_in_turn != 0));
185 
186                 std::copy(bearing_data.begin(),
187                           bearing_data.end(),
188                           std::back_inserter(intersection.bearings));
189                 intersection.entry.clear();
190                 for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size()))
191                 {
192                     intersection.entry.push_back(path_point.entry_class.allowsEntry(idx));
193                 }
194                 std::int16_t bearing_in_driving_direction =
195                     util::bearing::reverse(std::round(bearings.first));
196                 maneuver = {intersection.location,
197                             bearing_in_driving_direction,
198                             bearings.second,
199                             path_point.turn_instruction,
200                             WaypointType::None,
201                             0};
202                 segment_index++;
203                 segment_duration = 0;
204                 segment_weight = 0;
205             }
206         }
207         const auto distance = leg_geometry.segment_distances[segment_index];
208         const EdgeWeight duration = segment_duration + target_duration;
209         const EdgeWeight weight = segment_weight + target_weight;
210         // intersections contain the classes of exiting road
211         intersection.classes = facade.GetClasses(facade.GetClassData(target_node_id));
212         BOOST_ASSERT(duration >= 0);
213         steps.push_back(RouteStep{leg_data[leg_data.size() - 1].from_edge_based_node,
214                                   step_name_id,
215                                   is_segregated,
216                                   facade.GetNameForID(step_name_id).to_string(),
217                                   facade.GetRefForID(step_name_id).to_string(),
218                                   facade.GetPronunciationForID(step_name_id).to_string(),
219                                   facade.GetDestinationsForID(step_name_id).to_string(),
220                                   facade.GetExitsForID(step_name_id).to_string(),
221                                   NO_ROTARY_NAME,
222                                   NO_ROTARY_NAME,
223                                   duration / 10.,
224                                   distance,
225                                   weight / weight_multiplier,
226                                   target_mode,
227                                   maneuver,
228                                   leg_geometry.FrontIndex(segment_index),
229                                   leg_geometry.BackIndex(segment_index) + 1,
230                                   {intersection},
231                                   facade.IsLeftHandDriving(target_node_id)});
232     }
233     // In this case the source + target are on the same edge segment
234     else
235     {
236         BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position);
237         BOOST_ASSERT(source_traversed_in_reverse == target_traversed_in_reverse);
238 
239         // The difference (target-source) should handle
240         // all variants for similar directions u-v and s-t (and opposite)
241         //    s(t)  t(s)   source_traversed_in_reverse = target_traversed_in_reverse = false
242         // u-------------v
243         // |---|           source_weight
244         // |---------|     target_weight
245 
246         //    s(t)  t(s)   source_traversed_in_reverse = target_traversed_in_reverse = true
247         // u-------------v
248         // |   |---------| source_weight
249         // |         |---| target_weight
250         BOOST_ASSERT(target_weight >= source_weight);
251         const EdgeWeight weight = target_weight - source_weight;
252 
253         // use rectified linear unit function to avoid negative duration values
254         // due to flooring errors in phantom snapping
255         BOOST_ASSERT(target_duration >= source_duration || weight == 0);
256         const EdgeWeight duration = std::max(0, target_duration - source_duration);
257 
258         steps.push_back(RouteStep{source_node_id,
259                                   source_name_id,
260                                   is_segregated,
261                                   facade.GetNameForID(source_name_id).to_string(),
262                                   facade.GetRefForID(source_name_id).to_string(),
263                                   facade.GetPronunciationForID(source_name_id).to_string(),
264                                   facade.GetDestinationsForID(source_name_id).to_string(),
265                                   facade.GetExitsForID(source_name_id).to_string(),
266                                   NO_ROTARY_NAME,
267                                   NO_ROTARY_NAME,
268                                   duration / 10.,
269                                   leg_geometry.segment_distances[segment_index],
270                                   weight / weight_multiplier,
271                                   source_mode,
272                                   std::move(maneuver),
273                                   leg_geometry.FrontIndex(segment_index),
274                                   leg_geometry.BackIndex(segment_index) + 1,
275                                   {intersection},
276                                   facade.IsLeftHandDriving(source_node_id)});
277     }
278 
279     BOOST_ASSERT(segment_index == number_of_segments - 1);
280     bearings = detail::getArriveBearings(leg_geometry, target_node, target_traversed_in_reverse);
281 
282     intersection = {
283         target_node.location,
284         std::vector<short>({static_cast<short>(util::bearing::reverse(bearings.first))}),
285         std::vector<bool>({true}),
286         0,
287         IntermediateIntersection::NO_INDEX,
288         util::guidance::LaneTuple(),
289         {},
290         {}};
291 
292     // This step has length zero, the only reason we need it is the target location
293     maneuver = {intersection.location,
294                 bearings.first,
295                 bearings.second,
296                 osrm::guidance::TurnInstruction::NO_TURN(),
297                 WaypointType::Arrive,
298                 0};
299 
300     BOOST_ASSERT(!leg_geometry.locations.empty());
301     steps.push_back(RouteStep{target_node_id,
302                               target_name_id,
303                               facade.IsSegregated(target_node_id),
304                               facade.GetNameForID(target_name_id).to_string(),
305                               facade.GetRefForID(target_name_id).to_string(),
306                               facade.GetPronunciationForID(target_name_id).to_string(),
307                               facade.GetDestinationsForID(target_name_id).to_string(),
308                               facade.GetExitsForID(target_name_id).to_string(),
309                               NO_ROTARY_NAME,
310                               NO_ROTARY_NAME,
311                               ZERO_DURATION,
312                               ZERO_DISTANCE,
313                               ZERO_WEIGHT,
314                               target_mode,
315                               std::move(maneuver),
316                               leg_geometry.locations.size() - 1,
317                               leg_geometry.locations.size(),
318                               {intersection},
319                               facade.IsLeftHandDriving(target_node_id)});
320 
321     BOOST_ASSERT(steps.front().intersections.size() == 1);
322     BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
323     BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
324     BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
325 
326     BOOST_ASSERT(steps.back().intersections.size() == 1);
327     BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
328     BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
329     BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
330     BOOST_ASSERT(steps.back().intersections.front().lanes.lanes_in_turn == 0);
331     BOOST_ASSERT(steps.back().intersections.front().lanes.first_lane_from_the_right ==
332                  INVALID_LANEID);
333     BOOST_ASSERT(steps.back().intersections.front().lane_description.empty());
334     // depart and arrive need to be trivial
335     BOOST_ASSERT(steps.front().maneuver.exit == 0 && steps.back().maneuver.exit == 0);
336     return steps;
337 }
338 
339 } // namespace guidance
340 } // namespace engine
341 } // namespace osrm
342 
343 #endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
344