Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
24
libs/routing/lanes/lane_info.cpp
Normal file
24
libs/routing/lanes/lane_info.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#include "lane_info.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
std::string DebugPrint(LaneInfo const & laneInfo)
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "LaneInfo{" << DebugPrint(laneInfo.laneWays) << ", recommendedWay: " << DebugPrint(laneInfo.recommendedWay)
|
||||
<< "}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string DebugPrint(LanesInfo const & lanesInfo)
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "LanesInfo[";
|
||||
for (auto const & laneInfo : lanesInfo)
|
||||
out << DebugPrint(laneInfo) << ", ";
|
||||
out << "]";
|
||||
return out.str();
|
||||
}
|
||||
} // namespace routing::turns::lanes
|
||||
23
libs/routing/lanes/lane_info.hpp
Normal file
23
libs/routing/lanes/lane_info.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/lanes/lane_way.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
struct LaneInfo
|
||||
{
|
||||
LaneWays laneWays;
|
||||
LaneWay recommendedWay = LaneWay::None;
|
||||
|
||||
bool operator==(LaneInfo const & rhs) const
|
||||
{
|
||||
return laneWays == rhs.laneWays && recommendedWay == rhs.recommendedWay;
|
||||
}
|
||||
};
|
||||
using LanesInfo = std::vector<LaneInfo>;
|
||||
|
||||
std::string DebugPrint(LaneInfo const & laneInfo);
|
||||
std::string DebugPrint(LanesInfo const & lanesInfo);
|
||||
} // namespace routing::turns::lanes
|
||||
50
libs/routing/lanes/lane_way.cpp
Normal file
50
libs/routing/lanes/lane_way.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "lane_way.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
std::string DebugPrint(LaneWay const laneWay)
|
||||
{
|
||||
using enum LaneWay;
|
||||
switch (laneWay)
|
||||
{
|
||||
case None: return "None";
|
||||
case ReverseLeft: return "ReverseLeft";
|
||||
case SharpLeft: return "SharpLeft";
|
||||
case Left: return "Left";
|
||||
case MergeToLeft: return "MergeToLeft";
|
||||
case SlightLeft: return "SlightLeft";
|
||||
case Through: return "Through";
|
||||
case SlightRight: return "SlightRight";
|
||||
case MergeToRight: return "MergeToRight";
|
||||
case Right: return "Right";
|
||||
case SharpRight: return "SharpRight";
|
||||
case ReverseRight: return "ReverseRight";
|
||||
case Count: return "Count";
|
||||
default:
|
||||
ASSERT_FAIL("Unsupported value: " + std::to_string(static_cast<std::uint8_t>(laneWay)));
|
||||
return "Unsupported";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DebugPrint(LaneWays const & laneWays)
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "LaneWays: [";
|
||||
std::uint8_t const waysCount = laneWays.m_laneWays.count();
|
||||
std::uint8_t waysPrinted = 0;
|
||||
for (std::size_t i = 0; i < laneWays.m_laneWays.size(); ++i)
|
||||
{
|
||||
if (laneWays.m_laneWays.test(i))
|
||||
{
|
||||
out << DebugPrint(static_cast<LaneWay>(i));
|
||||
if (waysPrinted < waysCount - 1)
|
||||
out << ", ";
|
||||
waysPrinted++;
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
return out.str();
|
||||
}
|
||||
} // namespace routing::turns::lanes
|
||||
84
libs/routing/lanes/lane_way.hpp
Normal file
84
libs/routing/lanes/lane_way.hpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
enum class LaneWay : std::uint8_t
|
||||
{
|
||||
None = 0,
|
||||
ReverseLeft,
|
||||
SharpLeft,
|
||||
Left,
|
||||
MergeToLeft,
|
||||
SlightLeft,
|
||||
Through,
|
||||
SlightRight,
|
||||
MergeToRight,
|
||||
Right,
|
||||
SharpRight,
|
||||
ReverseRight,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
class LaneWays
|
||||
{
|
||||
using LaneWaysT = std::bitset<static_cast<std::uint8_t>(LaneWay::Count)>;
|
||||
|
||||
friend std::string DebugPrint(LaneWays const & laneWays);
|
||||
|
||||
public:
|
||||
constexpr LaneWays() = default;
|
||||
constexpr LaneWays(std::initializer_list<LaneWay> const laneWays)
|
||||
{
|
||||
for (auto const & laneWay : laneWays)
|
||||
Add(laneWay);
|
||||
}
|
||||
|
||||
constexpr bool operator==(LaneWays const & rhs) const { return m_laneWays == rhs.m_laneWays; }
|
||||
|
||||
constexpr void Add(LaneWay laneWay)
|
||||
{
|
||||
ASSERT_LESS(laneWay, LaneWay::Count, ());
|
||||
m_laneWays.set(static_cast<std::uint8_t>(laneWay));
|
||||
}
|
||||
|
||||
constexpr void Remove(LaneWay laneWay)
|
||||
{
|
||||
ASSERT_LESS(laneWay, LaneWay::Count, ());
|
||||
m_laneWays.reset(static_cast<std::uint8_t>(laneWay));
|
||||
}
|
||||
|
||||
constexpr bool Contains(LaneWay laneWay) const
|
||||
{
|
||||
ASSERT_LESS(laneWay, LaneWay::Count, ());
|
||||
return m_laneWays.test(static_cast<std::uint8_t>(laneWay));
|
||||
}
|
||||
|
||||
/// An unrestricted lane is a lane that has no restrictions, i.e., it contains no lane ways.
|
||||
constexpr bool IsUnrestricted() const
|
||||
{
|
||||
return m_laneWays.none() || (m_laneWays.count() == 1 && Contains(LaneWay::None));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<LaneWay> GetActiveLaneWays() const
|
||||
{
|
||||
std::vector<LaneWay> result;
|
||||
for (std::size_t i = 0; i < m_laneWays.size(); ++i)
|
||||
if (m_laneWays.test(i))
|
||||
result.emplace_back(static_cast<LaneWay>(i));
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
LaneWaysT m_laneWays;
|
||||
};
|
||||
|
||||
std::string DebugPrint(LaneWay laneWay);
|
||||
std::string DebugPrint(LaneWays const & laneWays);
|
||||
} // namespace routing::turns::lanes
|
||||
84
libs/routing/lanes/lanes_parser.cpp
Normal file
84
libs/routing/lanes/lanes_parser.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include "lanes_parser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::uint8_t constexpr kLaneWayNamesCount = static_cast<std::uint8_t>(LaneWay::Count) + 4;
|
||||
|
||||
/**
|
||||
* The order is important. Starting with the most frequent tokens according to
|
||||
* taginfo.openstreetmap.org we minimize the number of the comparisons in ParseSingleLane().
|
||||
*
|
||||
* A `none` lane can be represented either as "none" or as "". That means both "none" and ""
|
||||
* should be considered names, even though they refer to the same thing. As a result,
|
||||
* `LaneWay::None` appears twice in this array, which is one longer than the number of
|
||||
* enum values.
|
||||
*/
|
||||
std::array<std::pair<LaneWay, std::string_view>, kLaneWayNamesCount> constexpr g_laneWayNames{{
|
||||
{LaneWay::None, ""},
|
||||
{LaneWay::Through, "through"},
|
||||
{LaneWay::Left, "left"},
|
||||
{LaneWay::Right, "right"},
|
||||
{LaneWay::None, "none"},
|
||||
{LaneWay::SharpLeft, "sharp_left"},
|
||||
{LaneWay::SlightLeft, "slight_left"},
|
||||
{LaneWay::MergeToRight, "merge_to_right"},
|
||||
{LaneWay::MergeToLeft, "merge_to_left"},
|
||||
{LaneWay::SlightRight, "slight_right"},
|
||||
{LaneWay::SharpRight, "sharp_right"},
|
||||
{LaneWay::ReverseLeft, "reverse"},
|
||||
{LaneWay::Right,
|
||||
"next_right"}, // "next_right" means "turn right, not in the first intersection but the one after that".
|
||||
{LaneWay::Through, "slide_left"}, // "slide_left" means "move a bit left within the lane".
|
||||
{LaneWay::Through, "slide_right"} // "slide_right" means "move a bit right within the lane".
|
||||
}};
|
||||
|
||||
bool ParseSingleLane(auto && laneWayRange, LaneWay & laneWay)
|
||||
{
|
||||
auto const it = std::ranges::find_if(
|
||||
g_laneWayNames, [&laneWayRange](auto const & pair) { return std::ranges::equal(laneWayRange, pair.second); });
|
||||
if (it != g_laneWayNames.end())
|
||||
{
|
||||
laneWay = it->first;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LanesInfo ParseLanes(std::string_view lanesString)
|
||||
{
|
||||
if (lanesString.empty())
|
||||
return {};
|
||||
|
||||
LanesInfo lanes;
|
||||
for (auto && laneInfo : lanesString | std::views::split('|'))
|
||||
{
|
||||
LaneInfo lane;
|
||||
if (std::ranges::empty(laneInfo))
|
||||
lane.laneWays.Add(LaneWay::None);
|
||||
else
|
||||
{
|
||||
for (auto && laneWay : laneInfo | std::views::split(';'))
|
||||
{
|
||||
auto way = LaneWay::None;
|
||||
auto && laneWayProcessed = laneWay | std::views::filter([](char const c) { return !std::isspace(c); }) |
|
||||
std::views::transform([](char const c) { return std::tolower(c); });
|
||||
if (!ParseSingleLane(laneWayProcessed, way))
|
||||
return {};
|
||||
lane.laneWays.Add(way);
|
||||
if (way == LaneWay::ReverseLeft)
|
||||
lane.laneWays.Add(LaneWay::ReverseRight);
|
||||
}
|
||||
}
|
||||
|
||||
lanes.push_back(lane);
|
||||
}
|
||||
return lanes;
|
||||
}
|
||||
} // namespace routing::turns::lanes
|
||||
16
libs/routing/lanes/lanes_parser.hpp
Normal file
16
libs/routing/lanes/lanes_parser.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/lanes/lane_info.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
/**
|
||||
* Parse lane information which comes from lanesString
|
||||
* @param lanesString lane information. Example through|through|through|through;right
|
||||
* @return LanesInfo. @see LanesInfo
|
||||
* @note if lanesString is empty, returns empty LanesInfo.
|
||||
*/
|
||||
LanesInfo ParseLanes(std::string_view lanesString);
|
||||
} // namespace routing::turns::lanes
|
||||
129
libs/routing/lanes/lanes_recommendation.cpp
Normal file
129
libs/routing/lanes/lanes_recommendation.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include "lanes_recommendation.hpp"
|
||||
|
||||
#include "routing/route.hpp"
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void FixRecommendedReverseLane(LaneWays & ways, LaneWay const recommendedWay)
|
||||
{
|
||||
if (recommendedWay == LaneWay::ReverseLeft)
|
||||
ways.Remove(LaneWay::ReverseRight);
|
||||
else if (recommendedWay == LaneWay::ReverseRight)
|
||||
ways.Remove(LaneWay::ReverseLeft);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments)
|
||||
{
|
||||
for (auto & segment : routeSegments)
|
||||
{
|
||||
auto & t = segment.GetTurn();
|
||||
if (t.IsTurnNone() || t.m_lanes.empty())
|
||||
continue;
|
||||
auto & lanesInfo = segment.GetTurnLanes();
|
||||
// Check if there are elements in lanesInfo that correspond with the turn exactly.
|
||||
// If so, fix up all the elements in lanesInfo that correspond with the turn.
|
||||
if (impl::SetRecommendedLaneWays(t.m_turn, lanesInfo))
|
||||
continue;
|
||||
// If not, check if there are elements in lanesInfo that correspond with the turn
|
||||
// approximately. If so, fix up all those elements.
|
||||
if (impl::SetRecommendedLaneWaysApproximately(t.m_turn, lanesInfo))
|
||||
continue;
|
||||
// If not, check if there is an unrestricted lane that could correspond to the
|
||||
// turn. If so, fix up that lane.
|
||||
if (impl::SetUnrestrictedLaneAsRecommended(t.m_turn, lanesInfo))
|
||||
continue;
|
||||
// Otherwise, we don't have lane recommendations for the user, so we don't
|
||||
// want to send the lane data any further.
|
||||
segment.ClearTurnLanes();
|
||||
}
|
||||
}
|
||||
|
||||
bool impl::SetRecommendedLaneWays(CarDirection const carDirection, LanesInfo & lanesInfo)
|
||||
{
|
||||
LaneWay laneWay;
|
||||
switch (carDirection)
|
||||
{
|
||||
case CarDirection::GoStraight: laneWay = LaneWay::Through; break;
|
||||
case CarDirection::TurnRight: laneWay = LaneWay::Right; break;
|
||||
case CarDirection::TurnSharpRight: laneWay = LaneWay::SharpRight; break;
|
||||
case CarDirection::TurnSlightRight: [[fallthrough]];
|
||||
case CarDirection::ExitHighwayToRight: laneWay = LaneWay::SlightRight; break;
|
||||
case CarDirection::TurnLeft: laneWay = LaneWay::Left; break;
|
||||
case CarDirection::TurnSharpLeft: laneWay = LaneWay::SharpLeft; break;
|
||||
case CarDirection::TurnSlightLeft: [[fallthrough]];
|
||||
case CarDirection::ExitHighwayToLeft: laneWay = LaneWay::SlightLeft; break;
|
||||
case CarDirection::UTurnLeft: laneWay = LaneWay::ReverseLeft; break;
|
||||
case CarDirection::UTurnRight: laneWay = LaneWay::ReverseRight; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
bool isLaneConformed = false;
|
||||
for (auto & [laneWays, recommendedWay] : lanesInfo)
|
||||
{
|
||||
if (laneWays.Contains(laneWay))
|
||||
{
|
||||
recommendedWay = laneWay;
|
||||
isLaneConformed = true;
|
||||
}
|
||||
FixRecommendedReverseLane(laneWays, recommendedWay);
|
||||
}
|
||||
return isLaneConformed;
|
||||
}
|
||||
|
||||
bool impl::SetRecommendedLaneWaysApproximately(CarDirection const carDirection, LanesInfo & lanesInfo)
|
||||
{
|
||||
std::vector<LaneWay> approximateLaneWays;
|
||||
switch (carDirection)
|
||||
{
|
||||
case CarDirection::UTurnLeft: approximateLaneWays = {LaneWay::SharpLeft}; break;
|
||||
case CarDirection::TurnSharpLeft: approximateLaneWays = {LaneWay::Left}; break;
|
||||
case CarDirection::TurnLeft: approximateLaneWays = {LaneWay::SlightLeft, LaneWay::SharpLeft}; break;
|
||||
case CarDirection::TurnSlightLeft: [[fallthrough]];
|
||||
case CarDirection::ExitHighwayToLeft: approximateLaneWays = {LaneWay::Left}; break;
|
||||
case CarDirection::GoStraight: approximateLaneWays = {LaneWay::SlightRight, LaneWay::SlightLeft}; break;
|
||||
case CarDirection::ExitHighwayToRight: [[fallthrough]];
|
||||
case CarDirection::TurnSlightRight: approximateLaneWays = {LaneWay::Right}; break;
|
||||
case CarDirection::TurnRight: approximateLaneWays = {LaneWay::SlightRight, LaneWay::SharpRight}; break;
|
||||
case CarDirection::TurnSharpRight: approximateLaneWays = {LaneWay::Right}; break;
|
||||
case CarDirection::UTurnRight: approximateLaneWays = {LaneWay::SharpRight}; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
bool isLaneConformed = false;
|
||||
for (auto & [laneWays, recommendedWay] : lanesInfo)
|
||||
{
|
||||
for (auto const & laneWay : approximateLaneWays)
|
||||
{
|
||||
if (laneWays.Contains(laneWay))
|
||||
{
|
||||
recommendedWay = laneWay;
|
||||
isLaneConformed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isLaneConformed;
|
||||
}
|
||||
|
||||
bool impl::SetUnrestrictedLaneAsRecommended(CarDirection const carDirection, LanesInfo & lanesInfo)
|
||||
{
|
||||
static auto constexpr setFirstUnrestrictedLane = [](LaneWay const laneWay, auto beginIt, auto endIt)
|
||||
{
|
||||
auto it = std::find_if(beginIt, endIt, [](auto const & laneInfo) { return laneInfo.laneWays.IsUnrestricted(); });
|
||||
if (it == endIt)
|
||||
return false;
|
||||
it->recommendedWay = laneWay;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (IsTurnMadeFromLeft(carDirection))
|
||||
return setFirstUnrestrictedLane(LaneWay::Left, lanesInfo.begin(), lanesInfo.end());
|
||||
if (IsTurnMadeFromRight(carDirection))
|
||||
return setFirstUnrestrictedLane(LaneWay::Right, lanesInfo.rbegin(), lanesInfo.rend());
|
||||
return false;
|
||||
}
|
||||
} // namespace routing::turns::lanes
|
||||
31
libs/routing/lanes/lanes_recommendation.hpp
Normal file
31
libs/routing/lanes/lanes_recommendation.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/lanes/lane_info.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
class RouteSegment;
|
||||
|
||||
namespace turns
|
||||
{
|
||||
enum class CarDirection;
|
||||
} // namespace turns
|
||||
} // namespace routing
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
/// Selects lanes which are recommended for an end user.
|
||||
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments);
|
||||
|
||||
// Keep signatures in the header for testing purposes
|
||||
namespace impl
|
||||
{
|
||||
bool SetRecommendedLaneWays(CarDirection carDirection, LanesInfo & lanesInfo);
|
||||
|
||||
bool SetRecommendedLaneWaysApproximately(CarDirection carDirection, LanesInfo & lanesInfo);
|
||||
|
||||
bool SetUnrestrictedLaneAsRecommended(CarDirection carDirection, LanesInfo & lanesInfo);
|
||||
} // namespace impl
|
||||
} // namespace routing::turns::lanes
|
||||
Loading…
Add table
Add a link
Reference in a new issue