1 #ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
2 #define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
3
4 #include <algorithm>
5 #include <cstdint>
6
7 #include "guidance/roundabout_type.hpp"
8 #include "util/attributes.hpp"
9 #include "util/typedefs.hpp"
10
11 namespace osrm
12 {
13 namespace guidance
14 {
15
16 // direction modifiers based on angle
17 namespace DirectionModifier
18 {
19 typedef std::uint8_t Enum;
20 const constexpr Enum UTurn = 0;
21 const constexpr Enum SharpRight = 1;
22 const constexpr Enum Right = 2;
23 const constexpr Enum SlightRight = 3;
24 const constexpr Enum Straight = 4;
25 const constexpr Enum SlightLeft = 5;
26 const constexpr Enum Left = 6;
27 const constexpr Enum SharpLeft = 7;
28 const constexpr Enum MaxDirectionModifier = 8;
29 } // namespace DirectionModifier
30
31 namespace TurnType
32 {
33 typedef std::uint8_t Enum;
34 const constexpr Enum Invalid = 0; // no valid turn instruction
35 const constexpr Enum NewName = 1; // no turn, but name changes
36 const constexpr Enum Continue = 2; // remain on a street
37 const constexpr Enum Turn = 3; // basic turn
38 const constexpr Enum Merge = 4; // merge onto a street
39 const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps)
40 const constexpr Enum OffRamp = 6; // special turn, highway exit
41 const constexpr Enum Fork = 7; // fork road splitting up
42 const constexpr Enum EndOfRoad = 8; // T intersection
43 const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply...
44 const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout
45 const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout
46 const constexpr Enum EnterRotary = 12; // Enter a rotary
47 const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
48 const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
49 const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
50 // depreacted: const constexpr Enum UseLane = 16; // No Turn, but you need to stay on a given lane!
51
52 // Values below here are silent instructions
53 const constexpr Enum NoTurn = 17; // end of segment without turn/middle of a segment
54 const constexpr Enum Suppressed = 18; // location that suppresses a turn
55 const constexpr Enum EnterRoundaboutAtExit = 19; // Entering a small Roundabout at a countable exit
56 const constexpr Enum ExitRoundabout = 20; // Exiting a small Roundabout
57 const constexpr Enum EnterRotaryAtExit = 21; // Enter A Rotary at a countable exit
58 const constexpr Enum ExitRotary = 22; // Exit a rotary
59 const constexpr Enum EnterRoundaboutIntersectionAtExit =
60 23; // Entering a small Roundabout at a countable exit
61 const constexpr Enum ExitRoundaboutIntersection = 24; // Exiting a small Roundabout
62 const constexpr Enum StayOnRoundabout = 25; // Continue on Either a small or a large Roundabout
63 const constexpr Enum Sliproad =
64 26; // Something that looks like a ramp, but is actually just a small sliproad
65 const constexpr Enum MaxTurnType = 27; // Special value for static asserts
66 } // namespace TurnType
67
68 struct TurnInstruction
69 {
TurnInstructionosrm::guidance::TurnInstruction70 TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
71 const DirectionModifier::Enum direction_modifier = DirectionModifier::UTurn)
72 : type(type), direction_modifier(direction_modifier)
73 {
74 }
75
76 TurnType::Enum type : 5;
77 DirectionModifier::Enum direction_modifier : 3;
78
IsUTurnosrm::guidance::TurnInstruction79 bool IsUTurn() const
80 {
81 return type != TurnType::NoTurn && direction_modifier == DirectionModifier::UTurn;
82 }
83
INVALIDosrm::guidance::TurnInstruction84 static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
85
NO_TURNosrm::guidance::TurnInstruction86 static TurnInstruction NO_TURN() { return {TurnType::NoTurn, DirectionModifier::UTurn}; }
87
REMAIN_ROUNDABOUTosrm::guidance::TurnInstruction88 static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
89 const DirectionModifier::Enum modifier)
90 {
91 return {TurnType::StayOnRoundabout, modifier};
92 }
93
ENTER_ROUNDABOUTosrm::guidance::TurnInstruction94 static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
95 const DirectionModifier::Enum modifier)
96 {
97 const constexpr TurnType::Enum enter_instruction[] = {
98 TurnType::Invalid,
99 TurnType::EnterRoundabout,
100 TurnType::EnterRotary,
101 TurnType::EnterRoundaboutIntersection};
102 return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
103 }
104
EXIT_ROUNDABOUTosrm::guidance::TurnInstruction105 static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
106 const DirectionModifier::Enum modifier)
107 {
108 const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid,
109 TurnType::ExitRoundabout,
110 TurnType::ExitRotary,
111 TurnType::ExitRoundaboutIntersection};
112 return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
113 }
114
ENTER_AND_EXIT_ROUNDABOUTosrm::guidance::TurnInstruction115 static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
116 const DirectionModifier::Enum modifier)
117 {
118 const constexpr TurnType::Enum exit_instruction[] = {
119 TurnType::Invalid,
120 TurnType::EnterAndExitRoundabout,
121 TurnType::EnterAndExitRotary,
122 TurnType::EnterAndExitRoundaboutIntersection};
123 return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
124 }
125
ENTER_ROUNDABOUT_AT_EXITosrm::guidance::TurnInstruction126 static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
127 const DirectionModifier::Enum modifier)
128 {
129 const constexpr TurnType::Enum enter_instruction[] = {
130 TurnType::Invalid,
131 TurnType::EnterRoundaboutAtExit,
132 TurnType::EnterRotaryAtExit,
133 TurnType::EnterRoundaboutIntersectionAtExit};
134 return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
135 }
136
SUPPRESSEDosrm::guidance::TurnInstruction137 static TurnInstruction SUPPRESSED(const DirectionModifier::Enum modifier)
138 {
139 return {TurnType::Suppressed, modifier};
140 }
141 };
142
143 static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit a byte");
144
operator !=(const TurnInstruction lhs,const TurnInstruction rhs)145 inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
146 {
147 return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
148 }
149
operator ==(const TurnInstruction lhs,const TurnInstruction rhs)150 inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
151 {
152 return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
153 }
154
155 // check if a instruction is associated in any form with a roundabout
hasRoundaboutType(const TurnInstruction instruction)156 inline bool hasRoundaboutType(const TurnInstruction instruction)
157 {
158 using namespace guidance::TurnType;
159 const constexpr TurnType::Enum valid_types[] = {TurnType::EnterRoundabout,
160 TurnType::EnterAndExitRoundabout,
161 TurnType::EnterRotary,
162 TurnType::EnterAndExitRotary,
163 TurnType::EnterRoundaboutIntersection,
164 TurnType::EnterAndExitRoundaboutIntersection,
165 TurnType::EnterRoundaboutAtExit,
166 TurnType::ExitRoundabout,
167 TurnType::EnterRotaryAtExit,
168 TurnType::ExitRotary,
169 TurnType::EnterRoundaboutIntersectionAtExit,
170 TurnType::ExitRoundaboutIntersection,
171 TurnType::StayOnRoundabout};
172
173 const auto *first = valid_types;
174 const auto *last = first + sizeof(valid_types) / sizeof(valid_types[0]);
175
176 return std::find(first, last, instruction.type) != last;
177 }
178
entersRoundabout(const guidance::TurnInstruction instruction)179 inline bool entersRoundabout(const guidance::TurnInstruction instruction)
180 {
181 return (instruction.type == guidance::TurnType::EnterRoundabout ||
182 instruction.type == guidance::TurnType::EnterRotary ||
183 instruction.type == guidance::TurnType::EnterRoundaboutIntersection ||
184 instruction.type == guidance::TurnType::EnterRoundaboutAtExit ||
185 instruction.type == guidance::TurnType::EnterRotaryAtExit ||
186 instruction.type == guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
187 instruction.type == guidance::TurnType::EnterAndExitRoundabout ||
188 instruction.type == guidance::TurnType::EnterAndExitRotary ||
189 instruction.type == guidance::TurnType::EnterAndExitRoundaboutIntersection);
190 }
191
leavesRoundabout(const guidance::TurnInstruction instruction)192 inline bool leavesRoundabout(const guidance::TurnInstruction instruction)
193 {
194 return (instruction.type == guidance::TurnType::ExitRoundabout ||
195 instruction.type == guidance::TurnType::ExitRotary ||
196 instruction.type == guidance::TurnType::ExitRoundaboutIntersection ||
197 instruction.type == guidance::TurnType::EnterAndExitRoundabout ||
198 instruction.type == guidance::TurnType::EnterAndExitRotary ||
199 instruction.type == guidance::TurnType::EnterAndExitRoundaboutIntersection);
200 }
201
staysOnRoundabout(const guidance::TurnInstruction instruction)202 inline bool staysOnRoundabout(const guidance::TurnInstruction instruction)
203 {
204 return instruction.type == guidance::TurnType::StayOnRoundabout ||
205 instruction.type == guidance::TurnType::EnterRoundaboutAtExit ||
206 instruction.type == guidance::TurnType::EnterRotaryAtExit ||
207 instruction.type == guidance::TurnType::EnterRoundaboutIntersectionAtExit;
208 }
209
210 // Silent Turn Instructions are not to be mentioned to the outside world but
isSilent(const guidance::TurnInstruction instruction)211 inline bool isSilent(const guidance::TurnInstruction instruction)
212 {
213 return instruction.type == guidance::TurnType::NoTurn ||
214 instruction.type == guidance::TurnType::Suppressed ||
215 instruction.type == guidance::TurnType::StayOnRoundabout;
216 }
217
hasRampType(const guidance::TurnInstruction instruction)218 inline bool hasRampType(const guidance::TurnInstruction instruction)
219 {
220 return instruction.type == guidance::TurnType::OffRamp ||
221 instruction.type == guidance::TurnType::OnRamp;
222 }
223
getTurnDirection(const double angle)224 inline guidance::DirectionModifier::Enum getTurnDirection(const double angle)
225 {
226 // An angle of zero is a u-turn
227 // 180 goes perfectly straight
228 // 0-180 are right turns
229 // 180-360 are left turns
230 if (angle > 0 && angle < 60)
231 return guidance::DirectionModifier::SharpRight;
232 if (angle >= 60 && angle < 140)
233 return guidance::DirectionModifier::Right;
234 if (angle >= 140 && angle < 160)
235 return guidance::DirectionModifier::SlightRight;
236 if (angle >= 160 && angle <= 200)
237 return guidance::DirectionModifier::Straight;
238 if (angle > 200 && angle <= 220)
239 return guidance::DirectionModifier::SlightLeft;
240 if (angle > 220 && angle <= 300)
241 return guidance::DirectionModifier::Left;
242 if (angle > 300 && angle < 360)
243 return guidance::DirectionModifier::SharpLeft;
244 return guidance::DirectionModifier::UTurn;
245 }
246
247 // swaps left <-> right modifier types
248 OSRM_ATTR_WARN_UNUSED
249 inline guidance::DirectionModifier::Enum
mirrorDirectionModifier(const guidance::DirectionModifier::Enum modifier)250 mirrorDirectionModifier(const guidance::DirectionModifier::Enum modifier)
251 {
252 const constexpr guidance::DirectionModifier::Enum results[] = {
253 guidance::DirectionModifier::UTurn,
254 guidance::DirectionModifier::SharpLeft,
255 guidance::DirectionModifier::Left,
256 guidance::DirectionModifier::SlightLeft,
257 guidance::DirectionModifier::Straight,
258 guidance::DirectionModifier::SlightRight,
259 guidance::DirectionModifier::Right,
260 guidance::DirectionModifier::SharpRight};
261 return results[modifier];
262 }
263
hasLeftModifier(const guidance::TurnInstruction instruction)264 inline bool hasLeftModifier(const guidance::TurnInstruction instruction)
265 {
266 return instruction.direction_modifier == guidance::DirectionModifier::SharpLeft ||
267 instruction.direction_modifier == guidance::DirectionModifier::Left ||
268 instruction.direction_modifier == guidance::DirectionModifier::SlightLeft;
269 }
270
hasRightModifier(const guidance::TurnInstruction instruction)271 inline bool hasRightModifier(const guidance::TurnInstruction instruction)
272 {
273 return instruction.direction_modifier == guidance::DirectionModifier::SharpRight ||
274 instruction.direction_modifier == guidance::DirectionModifier::Right ||
275 instruction.direction_modifier == guidance::DirectionModifier::SlightRight;
276 }
277
isLeftTurn(const guidance::TurnInstruction instruction)278 inline bool isLeftTurn(const guidance::TurnInstruction instruction)
279 {
280 switch (instruction.type)
281 {
282 case TurnType::Merge:
283 return hasRightModifier(instruction);
284 default:
285 return hasLeftModifier(instruction);
286 }
287 }
288
isRightTurn(const guidance::TurnInstruction instruction)289 inline bool isRightTurn(const guidance::TurnInstruction instruction)
290 {
291 switch (instruction.type)
292 {
293 case TurnType::Merge:
294 return hasLeftModifier(instruction);
295 default:
296 return hasRightModifier(instruction);
297 }
298 }
299
bearingToDirectionModifier(const double bearing)300 inline DirectionModifier::Enum bearingToDirectionModifier(const double bearing)
301 {
302 if (bearing < 135)
303 {
304 return guidance::DirectionModifier::Right;
305 }
306
307 if (bearing <= 225)
308 {
309 return guidance::DirectionModifier::Straight;
310 }
311 return guidance::DirectionModifier::Left;
312 }
313
314 namespace detail
315 {
316
317 const constexpr char *modifier_names[] = {"uturn",
318 "sharp right",
319 "right",
320 "slight right",
321 "straight",
322 "slight left",
323 "left",
324 "sharp left",
325 "UNDEFINED"};
326
327 /**
328 * Human readable values for TurnType enum values
329 */
330 struct TurnTypeName
331 {
332 // String value we return with our API
333 const char *external_name;
334 // Internal only string name for the turn type - useful for debugging
335 // and used by debug tiles for visualizing hidden turn types
336 const char *internal_name;
337 };
338
339 // Indexes in this list correspond to the Enum values of osrm::guidance::TurnType
340 const constexpr TurnTypeName turn_type_names[] = {
341 {"invalid", "(not set)"},
342 {"new name", "new name"},
343 {"continue", "continue"},
344 {"turn", "turn"},
345 {"merge", "merge"},
346 {"on ramp", "on ramp"},
347 {"off ramp", "off ramp"},
348 {"fork", "fork"},
349 {"end of road", "end of road"},
350 {"notification", "notification"},
351 {"roundabout", "enter roundabout"},
352 {"exit roundabout", "enter and exit roundabout"},
353 {"rotary", "enter rotary"},
354 {"exit rotary", "enter and exit rotary"},
355 {"roundabout turn", "enter roundabout turn"},
356 {"roundabout turn", "enter and exit roundabout turn"},
357 {"use lane", "use lane"},
358 {"invalid", "(noturn)"},
359 {"invalid", "(suppressed)"},
360 {"roundabout", "roundabout"},
361 {"exit roundabout", "exit roundabout"},
362 {"rotary", "rotary"},
363 {"exit rotary", "exit rotary"},
364 {"roundabout turn", "roundabout turn"},
365 {"exit roundabout", "exit roundabout turn"},
366 {"invalid", "(stay on roundabout)"},
367 {"invalid", "(sliproad)"},
368 {"MAXVALUE", "MAXVALUE"}};
369
370 } // namespace detail
371
instructionTypeToString(const TurnType::Enum type)372 inline std::string instructionTypeToString(const TurnType::Enum type)
373 {
374 static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >=
375 TurnType::MaxTurnType,
376 "Some turn types have no string representation.");
377 return detail::turn_type_names[static_cast<std::size_t>(type)].external_name;
378 }
379
internalInstructionTypeToString(const TurnType::Enum type)380 inline std::string internalInstructionTypeToString(const TurnType::Enum type)
381 {
382 static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >=
383 TurnType::MaxTurnType,
384 "Some turn types have no string representation.");
385 return detail::turn_type_names[static_cast<std::size_t>(type)].internal_name;
386 }
387
instructionModifierToString(const DirectionModifier::Enum modifier)388 inline std::string instructionModifierToString(const DirectionModifier::Enum modifier)
389 {
390 static_assert((sizeof(detail::modifier_names) + 1) / sizeof(detail::modifier_names[0]) >=
391 DirectionModifier::MaxDirectionModifier,
392 "Some direction modifiers have no string representation.");
393 return detail::modifier_names[static_cast<std::size_t>(modifier)];
394 }
395
396 } // namespace guidance
397 } // namespace osrm
398
399 #endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
400