395 lines
16 KiB
C++
395 lines
16 KiB
C++
#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|
|
#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|
|
|
|
#include "guidance/roundabout_type.hpp"
|
|
#include "util/typedefs.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdint>
|
|
|
|
namespace osrm::guidance
|
|
{
|
|
|
|
// direction modifiers based on angle
|
|
namespace DirectionModifier
|
|
{
|
|
using Enum = std::uint8_t;
|
|
const constexpr Enum UTurn = 0;
|
|
const constexpr Enum SharpRight = 1;
|
|
const constexpr Enum Right = 2;
|
|
const constexpr Enum SlightRight = 3;
|
|
const constexpr Enum Straight = 4;
|
|
const constexpr Enum SlightLeft = 5;
|
|
const constexpr Enum Left = 6;
|
|
const constexpr Enum SharpLeft = 7;
|
|
const constexpr Enum MaxDirectionModifier = 8;
|
|
} // namespace DirectionModifier
|
|
|
|
namespace TurnType
|
|
{
|
|
using Enum = std::uint8_t;
|
|
const constexpr Enum Invalid = 0; // no valid turn instruction
|
|
const constexpr Enum NewName = 1; // no turn, but name changes
|
|
const constexpr Enum Continue = 2; // remain on a street
|
|
const constexpr Enum Turn = 3; // basic turn
|
|
const constexpr Enum Merge = 4; // merge onto a street
|
|
const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps)
|
|
const constexpr Enum OffRamp = 6; // special turn, highway exit
|
|
const constexpr Enum Fork = 7; // fork road splitting up
|
|
const constexpr Enum EndOfRoad = 8; // T intersection
|
|
const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply...
|
|
const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout
|
|
const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout
|
|
const constexpr Enum EnterRotary = 12; // Enter a rotary
|
|
const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
|
|
const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
|
|
const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
|
|
// depreacted: const constexpr Enum UseLane = 16; // No Turn, but you need to stay on a given lane!
|
|
|
|
// Values below here are silent instructions
|
|
const constexpr Enum NoTurn = 17; // end of segment without turn/middle of a segment
|
|
const constexpr Enum Suppressed = 18; // location that suppresses a turn
|
|
const constexpr Enum EnterRoundaboutAtExit = 19; // Entering a small Roundabout at a countable exit
|
|
const constexpr Enum ExitRoundabout = 20; // Exiting a small Roundabout
|
|
const constexpr Enum EnterRotaryAtExit = 21; // Enter A Rotary at a countable exit
|
|
const constexpr Enum ExitRotary = 22; // Exit a rotary
|
|
const constexpr Enum EnterRoundaboutIntersectionAtExit =
|
|
23; // Entering a small Roundabout at a countable exit
|
|
const constexpr Enum ExitRoundaboutIntersection = 24; // Exiting a small Roundabout
|
|
const constexpr Enum StayOnRoundabout = 25; // Continue on Either a small or a large Roundabout
|
|
const constexpr Enum Sliproad =
|
|
26; // Something that looks like a ramp, but is actually just a small sliproad
|
|
const constexpr Enum MaxTurnType = 27; // Special value for static asserts
|
|
} // namespace TurnType
|
|
|
|
struct TurnInstruction
|
|
{
|
|
TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
|
|
const DirectionModifier::Enum direction_modifier = DirectionModifier::UTurn)
|
|
: type(type), direction_modifier(direction_modifier)
|
|
{
|
|
}
|
|
|
|
TurnType::Enum type : 5;
|
|
DirectionModifier::Enum direction_modifier : 3;
|
|
|
|
bool IsUTurn() const
|
|
{
|
|
return type != TurnType::NoTurn && direction_modifier == DirectionModifier::UTurn;
|
|
}
|
|
|
|
static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
|
|
|
|
static TurnInstruction NO_TURN() { return {TurnType::NoTurn, DirectionModifier::UTurn}; }
|
|
|
|
static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
|
|
const DirectionModifier::Enum modifier)
|
|
{
|
|
return {TurnType::StayOnRoundabout, modifier};
|
|
}
|
|
|
|
static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
|
|
const DirectionModifier::Enum modifier)
|
|
{
|
|
const constexpr TurnType::Enum enter_instruction[] = {
|
|
TurnType::Invalid,
|
|
TurnType::EnterRoundabout,
|
|
TurnType::EnterRotary,
|
|
TurnType::EnterRoundaboutIntersection};
|
|
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
|
|
}
|
|
|
|
static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
|
|
const DirectionModifier::Enum modifier)
|
|
{
|
|
const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid,
|
|
TurnType::ExitRoundabout,
|
|
TurnType::ExitRotary,
|
|
TurnType::ExitRoundaboutIntersection};
|
|
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
|
|
}
|
|
|
|
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
|
|
const DirectionModifier::Enum modifier)
|
|
{
|
|
const constexpr TurnType::Enum exit_instruction[] = {
|
|
TurnType::Invalid,
|
|
TurnType::EnterAndExitRoundabout,
|
|
TurnType::EnterAndExitRotary,
|
|
TurnType::EnterAndExitRoundaboutIntersection};
|
|
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
|
|
}
|
|
|
|
static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
|
|
const DirectionModifier::Enum modifier)
|
|
{
|
|
const constexpr TurnType::Enum enter_instruction[] = {
|
|
TurnType::Invalid,
|
|
TurnType::EnterRoundaboutAtExit,
|
|
TurnType::EnterRotaryAtExit,
|
|
TurnType::EnterRoundaboutIntersectionAtExit};
|
|
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
|
|
}
|
|
|
|
static TurnInstruction SUPPRESSED(const DirectionModifier::Enum modifier)
|
|
{
|
|
return {TurnType::Suppressed, modifier};
|
|
}
|
|
};
|
|
|
|
static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit a byte");
|
|
|
|
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
|
|
{
|
|
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
|
|
}
|
|
|
|
inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
|
|
{
|
|
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
|
|
}
|
|
|
|
// check if a instruction is associated in any form with a roundabout
|
|
inline bool hasRoundaboutType(const TurnInstruction instruction)
|
|
{
|
|
using namespace guidance::TurnType;
|
|
const constexpr std::array<TurnType::Enum, 13> valid_types = {
|
|
TurnType::EnterRoundabout,
|
|
TurnType::EnterAndExitRoundabout,
|
|
TurnType::EnterRotary,
|
|
TurnType::EnterAndExitRotary,
|
|
TurnType::EnterRoundaboutIntersection,
|
|
TurnType::EnterAndExitRoundaboutIntersection,
|
|
TurnType::EnterRoundaboutAtExit,
|
|
TurnType::ExitRoundabout,
|
|
TurnType::EnterRotaryAtExit,
|
|
TurnType::ExitRotary,
|
|
TurnType::EnterRoundaboutIntersectionAtExit,
|
|
TurnType::ExitRoundaboutIntersection,
|
|
TurnType::StayOnRoundabout};
|
|
|
|
return std::find(valid_types.cbegin(), valid_types.cend(), instruction.type) !=
|
|
valid_types.cend();
|
|
}
|
|
|
|
inline bool entersRoundabout(const guidance::TurnInstruction instruction)
|
|
{
|
|
return (instruction.type == guidance::TurnType::EnterRoundabout ||
|
|
instruction.type == guidance::TurnType::EnterRotary ||
|
|
instruction.type == guidance::TurnType::EnterRoundaboutIntersection ||
|
|
instruction.type == guidance::TurnType::EnterRoundaboutAtExit ||
|
|
instruction.type == guidance::TurnType::EnterRotaryAtExit ||
|
|
instruction.type == guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRoundabout ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRotary ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRoundaboutIntersection);
|
|
}
|
|
|
|
inline bool leavesRoundabout(const guidance::TurnInstruction instruction)
|
|
{
|
|
return (instruction.type == guidance::TurnType::ExitRoundabout ||
|
|
instruction.type == guidance::TurnType::ExitRotary ||
|
|
instruction.type == guidance::TurnType::ExitRoundaboutIntersection ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRoundabout ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRotary ||
|
|
instruction.type == guidance::TurnType::EnterAndExitRoundaboutIntersection);
|
|
}
|
|
|
|
inline bool staysOnRoundabout(const guidance::TurnInstruction instruction)
|
|
{
|
|
return instruction.type == guidance::TurnType::StayOnRoundabout ||
|
|
instruction.type == guidance::TurnType::EnterRoundaboutAtExit ||
|
|
instruction.type == guidance::TurnType::EnterRotaryAtExit ||
|
|
instruction.type == guidance::TurnType::EnterRoundaboutIntersectionAtExit;
|
|
}
|
|
|
|
// Silent Turn Instructions are not to be mentioned to the outside world but
|
|
inline bool isSilent(const guidance::TurnInstruction instruction)
|
|
{
|
|
return instruction.type == guidance::TurnType::NoTurn ||
|
|
instruction.type == guidance::TurnType::Suppressed ||
|
|
instruction.type == guidance::TurnType::StayOnRoundabout;
|
|
}
|
|
|
|
inline bool hasRampType(const guidance::TurnInstruction instruction)
|
|
{
|
|
return instruction.type == guidance::TurnType::OffRamp ||
|
|
instruction.type == guidance::TurnType::OnRamp;
|
|
}
|
|
|
|
inline guidance::DirectionModifier::Enum getTurnDirection(const double angle)
|
|
{
|
|
// An angle of zero is a u-turn
|
|
// 180 goes perfectly straight
|
|
// 0-180 are right turns
|
|
// 180-360 are left turns
|
|
if (angle > 0 && angle < 60)
|
|
return guidance::DirectionModifier::SharpRight;
|
|
if (angle >= 60 && angle < 140)
|
|
return guidance::DirectionModifier::Right;
|
|
if (angle >= 140 && angle < 160)
|
|
return guidance::DirectionModifier::SlightRight;
|
|
if (angle >= 160 && angle <= 200)
|
|
return guidance::DirectionModifier::Straight;
|
|
if (angle > 200 && angle <= 220)
|
|
return guidance::DirectionModifier::SlightLeft;
|
|
if (angle > 220 && angle <= 300)
|
|
return guidance::DirectionModifier::Left;
|
|
if (angle > 300 && angle < 360)
|
|
return guidance::DirectionModifier::SharpLeft;
|
|
return guidance::DirectionModifier::UTurn;
|
|
}
|
|
|
|
// swaps left <-> right modifier types
|
|
[[nodiscard]] inline guidance::DirectionModifier::Enum
|
|
mirrorDirectionModifier(const guidance::DirectionModifier::Enum modifier)
|
|
{
|
|
const constexpr guidance::DirectionModifier::Enum results[] = {
|
|
guidance::DirectionModifier::UTurn,
|
|
guidance::DirectionModifier::SharpLeft,
|
|
guidance::DirectionModifier::Left,
|
|
guidance::DirectionModifier::SlightLeft,
|
|
guidance::DirectionModifier::Straight,
|
|
guidance::DirectionModifier::SlightRight,
|
|
guidance::DirectionModifier::Right,
|
|
guidance::DirectionModifier::SharpRight};
|
|
return results[modifier];
|
|
}
|
|
|
|
inline bool hasLeftModifier(const guidance::TurnInstruction instruction)
|
|
{
|
|
return instruction.direction_modifier == guidance::DirectionModifier::SharpLeft ||
|
|
instruction.direction_modifier == guidance::DirectionModifier::Left ||
|
|
instruction.direction_modifier == guidance::DirectionModifier::SlightLeft;
|
|
}
|
|
|
|
inline bool hasRightModifier(const guidance::TurnInstruction instruction)
|
|
{
|
|
return instruction.direction_modifier == guidance::DirectionModifier::SharpRight ||
|
|
instruction.direction_modifier == guidance::DirectionModifier::Right ||
|
|
instruction.direction_modifier == guidance::DirectionModifier::SlightRight;
|
|
}
|
|
|
|
inline bool isLeftTurn(const guidance::TurnInstruction instruction)
|
|
{
|
|
switch (instruction.type)
|
|
{
|
|
case TurnType::Merge:
|
|
return hasRightModifier(instruction);
|
|
default:
|
|
return hasLeftModifier(instruction);
|
|
}
|
|
}
|
|
|
|
inline bool isRightTurn(const guidance::TurnInstruction instruction)
|
|
{
|
|
switch (instruction.type)
|
|
{
|
|
case TurnType::Merge:
|
|
return hasLeftModifier(instruction);
|
|
default:
|
|
return hasRightModifier(instruction);
|
|
}
|
|
}
|
|
|
|
inline DirectionModifier::Enum bearingToDirectionModifier(const double bearing)
|
|
{
|
|
if (bearing < 135)
|
|
{
|
|
return guidance::DirectionModifier::Right;
|
|
}
|
|
|
|
if (bearing <= 225)
|
|
{
|
|
return guidance::DirectionModifier::Straight;
|
|
}
|
|
return guidance::DirectionModifier::Left;
|
|
}
|
|
|
|
namespace detail
|
|
{
|
|
|
|
const constexpr char *modifier_names[] = {"uturn",
|
|
"sharp right",
|
|
"right",
|
|
"slight right",
|
|
"straight",
|
|
"slight left",
|
|
"left",
|
|
"sharp left",
|
|
"UNDEFINED"};
|
|
|
|
/**
|
|
* Human readable values for TurnType enum values
|
|
*/
|
|
struct TurnTypeName
|
|
{
|
|
// String value we return with our API
|
|
const char *external_name;
|
|
// Internal only string name for the turn type - useful for debugging
|
|
// and used by debug tiles for visualizing hidden turn types
|
|
const char *internal_name;
|
|
};
|
|
|
|
// Indexes in this list correspond to the Enum values of osrm::guidance::TurnType
|
|
const constexpr TurnTypeName turn_type_names[] = {
|
|
{"invalid", "(not set)"},
|
|
{"new name", "new name"},
|
|
{"continue", "continue"},
|
|
{"turn", "turn"},
|
|
{"merge", "merge"},
|
|
{"on ramp", "on ramp"},
|
|
{"off ramp", "off ramp"},
|
|
{"fork", "fork"},
|
|
{"end of road", "end of road"},
|
|
{"notification", "notification"},
|
|
{"roundabout", "enter roundabout"},
|
|
{"exit roundabout", "enter and exit roundabout"},
|
|
{"rotary", "enter rotary"},
|
|
{"exit rotary", "enter and exit rotary"},
|
|
{"roundabout turn", "enter roundabout turn"},
|
|
{"roundabout turn", "enter and exit roundabout turn"},
|
|
{"use lane", "use lane"},
|
|
{"invalid", "(noturn)"},
|
|
{"invalid", "(suppressed)"},
|
|
{"roundabout", "roundabout"},
|
|
{"exit roundabout", "exit roundabout"},
|
|
{"rotary", "rotary"},
|
|
{"exit rotary", "exit rotary"},
|
|
{"roundabout turn", "roundabout turn"},
|
|
{"exit roundabout", "exit roundabout turn"},
|
|
{"invalid", "(stay on roundabout)"},
|
|
{"invalid", "(sliproad)"},
|
|
{"MAXVALUE", "MAXVALUE"}};
|
|
|
|
} // namespace detail
|
|
|
|
inline std::string instructionTypeToString(const TurnType::Enum type)
|
|
{
|
|
static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >=
|
|
TurnType::MaxTurnType,
|
|
"Some turn types have no string representation.");
|
|
return detail::turn_type_names[static_cast<std::size_t>(type)].external_name;
|
|
}
|
|
|
|
inline std::string internalInstructionTypeToString(const TurnType::Enum type)
|
|
{
|
|
static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >=
|
|
TurnType::MaxTurnType,
|
|
"Some turn types have no string representation.");
|
|
return detail::turn_type_names[static_cast<std::size_t>(type)].internal_name;
|
|
}
|
|
|
|
inline std::string instructionModifierToString(const DirectionModifier::Enum modifier)
|
|
{
|
|
static_assert((sizeof(detail::modifier_names) + 1) / sizeof(detail::modifier_names[0]) >=
|
|
DirectionModifier::MaxDirectionModifier,
|
|
"Some direction modifiers have no string representation.");
|
|
return detail::modifier_names[static_cast<std::size_t>(modifier)];
|
|
}
|
|
|
|
} // namespace osrm::guidance
|
|
|
|
#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|