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