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