WIP: [generator][core][android] Lanes refactoring #7077
22 changed files with 722 additions and 606 deletions
|
@ -1244,7 +1244,7 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass)
|
|||
"Ljava/lang/String;Ljava/lang/String;DIIIII"
|
||||
"[Lapp/organicmaps/routing/SingleLaneInfo;ZZ)V");
|
||||
|
||||
vector<routing::FollowingInfo::SingleLaneInfoClient> const & lanes = info.m_lanes;
|
||||
auto const & lanes = info.m_lanes;
|
||||
jobjectArray jLanes = nullptr;
|
||||
if (!lanes.empty())
|
||||
{
|
||||
|
@ -1252,18 +1252,13 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass)
|
|||
auto const lanesSize = static_cast<jsize>(lanes.size());
|
||||
jLanes = env->NewObjectArray(lanesSize, laneClass, nullptr);
|
||||
ASSERT(jLanes, (jni::DescribeException()));
|
||||
static jmethodID const ctorSingleLaneInfoID = jni::GetConstructorID(env, laneClass, "([BZ)V");
|
||||
static jmethodID const ctorSingleLaneInfoID = jni::GetConstructorID(env, laneClass, "(SS)V");
|
||||
|
||||
for (jsize j = 0; j < lanesSize; ++j)
|
||||
{
|
||||
auto const laneSize = static_cast<jsize>(lanes[j].m_lane.size());
|
||||
jni::TScopedLocalByteArrayRef singleLane(env, env->NewByteArray(laneSize));
|
||||
ASSERT(singleLane.get(), (jni::DescribeException()));
|
||||
env->SetByteArrayRegion(singleLane.get(), 0, laneSize, lanes[j].m_lane.data());
|
||||
|
||||
jni::TScopedLocalRef singleLaneInfo(
|
||||
env, env->NewObject(laneClass, ctorSingleLaneInfoID, singleLane.get(),
|
||||
lanes[j].m_isRecommended));
|
||||
env, env->NewObject(laneClass, ctorSingleLaneInfoID, static_cast<jshort>(lanes[j].m_laneWays.data),
|
||||
static_cast<jshort>(lanes[j].m_recommendedLaneWays.data)));
|
||||
ASSERT(singleLaneInfo.get(), (jni::DescribeException()));
|
||||
env->SetObjectArrayElement(jLanes, j, singleLaneInfo.get());
|
||||
}
|
||||
|
|
|
@ -2,33 +2,45 @@ package app.organicmaps.routing;
|
|||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.log.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SingleLaneInfo
|
||||
{
|
||||
public LaneWay[] mLane;
|
||||
public boolean mIsActive;
|
||||
@NonNull
|
||||
public final LaneWay[] mLane;
|
||||
@Nullable
|
||||
public final LaneWay[] mRecommendedLaneWays;
|
||||
|
||||
public final boolean mIsActive;
|
||||
|
||||
/**
|
||||
* IMPORTANT : Order of enum values MUST BE the same
|
||||
* with native LaneWay enum (see routing/turns.hpp for details).
|
||||
* with native LaneWay union (see routing/lanes.hpp for details).
|
||||
* Information for every lane is composed of some number values below.
|
||||
* For example, a lane may have THROUGH and RIGHT values.
|
||||
*/
|
||||
public enum LaneWay
|
||||
{
|
||||
NONE(R.drawable.ic_turn_straight),
|
||||
REVERSE(R.drawable.ic_turn_uleft),
|
||||
SHARP_LEFT(R.drawable.ic_turn_left_sharp),
|
||||
LEFT(R.drawable.ic_turn_left),
|
||||
SLIGHT_LEFT(R.drawable.ic_turn_left_slight),
|
||||
MERGE_TO_RIGHT(R.drawable.ic_turn_right_slight),
|
||||
SHARP_LEFT(R.drawable.ic_turn_left_sharp),
|
||||
THROUGH(R.drawable.ic_turn_straight),
|
||||
MERGE_TO_LEFT(R.drawable.ic_turn_left_slight),
|
||||
SLIGHT_RIGHT(R.drawable.ic_turn_right_slight),
|
||||
RIGHT(R.drawable.ic_turn_right),
|
||||
SHARP_RIGHT(R.drawable.ic_turn_right_sharp);
|
||||
SLIGHT_RIGHT(R.drawable.ic_turn_right_slight),
|
||||
SHARP_RIGHT(R.drawable.ic_turn_right_sharp),
|
||||
REVERSE(R.drawable.ic_turn_uleft),
|
||||
MERGE_TO_LEFT(R.drawable.ic_turn_left_slight),
|
||||
MERGE_TO_RIGHT(R.drawable.ic_turn_right_slight),
|
||||
SLIDE_LEFT(R.drawable.ic_turn_left_slight),
|
||||
SLIDE_RIGHT(R.drawable.ic_turn_right_slight),
|
||||
NEXT_RIGHT(R.drawable.ic_turn_right);
|
||||
|
||||
public final int mTurnRes;
|
||||
|
||||
|
@ -40,25 +52,54 @@ public class SingleLaneInfo
|
|||
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
SingleLaneInfo(byte[] laneOrdinals, boolean isActive)
|
||||
SingleLaneInfo(final short laneWays, final short recommendedLaneWays)
|
||||
{
|
||||
mLane = new LaneWay[laneOrdinals.length];
|
||||
final LaneWay[] values = LaneWay.values();
|
||||
for (int i = 0; i < mLane.length; i++)
|
||||
mLane[i] = values[laneOrdinals[i]];
|
||||
|
||||
mIsActive = isActive;
|
||||
if (laneWays == 0)
|
||||
mLane = new LaneWay[]{LaneWay.THROUGH};
|
||||
else
|
||||
mLane = createLaneWays(laneWays);
|
||||
if (recommendedLaneWays == 0)
|
||||
mRecommendedLaneWays = null;
|
||||
else
|
||||
mRecommendedLaneWays = createLaneWays(recommendedLaneWays);
|
||||
mIsActive = recommendedLaneWays != 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
final int initialCapacity = 32;
|
||||
StringBuilder sb = new StringBuilder(initialCapacity);
|
||||
sb.append("Is the lane active? ").append(mIsActive).append(". The lane directions IDs are");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("SingleLaneInfo[mLaneWays=[");
|
||||
for (LaneWay i : mLane)
|
||||
sb.append(" ").append(i.ordinal());
|
||||
sb.append(i.toString()).append(" ");
|
||||
sb.append("], mRecommendedLaneWays=[");
|
||||
if (mRecommendedLaneWays != null)
|
||||
for (LaneWay i : mRecommendedLaneWays)
|
||||
sb.append(i.toString()).append(" ");
|
||||
else
|
||||
sb.append("null]");
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static LaneWay[] createLaneWays(final short laneWays)
|
||||
{
|
||||
final LaneWay[] values = LaneWay.values();
|
||||
final List<LaneWay> result = new ArrayList<>();
|
||||
for (int index = 0, ways = laneWays; ways > 0; ways >>= 1, index++)
|
||||
{
|
||||
if ((ways & 1) != 1)
|
||||
continue;
|
||||
if (index >= values.length)
|
||||
{
|
||||
Logger.w(SingleLaneInfo.class.getSimpleName(), "Index >= LaneWay.size(); Probably, unused bits are set");
|
||||
continue;
|
||||
}
|
||||
result.add(values[index]);
|
||||
}
|
||||
return result.toArray(new LaneWay[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,10 +319,11 @@ public:
|
|||
bool operator()(UniChar c) const;
|
||||
};
|
||||
|
||||
template <class StringT> class SimpleTokenizer : public
|
||||
TokenizeIterator<SimpleDelimiter, ::utf8::unchecked::iterator<typename StringT::const_iterator>, false /* KeepEmptyTokens */>
|
||||
template <class StringT, bool KeepEmptyTokens = false>
|
||||
class SimpleTokenizer : public
|
||||
TokenizeIterator<SimpleDelimiter, ::utf8::unchecked::iterator<typename StringT::const_iterator>, KeepEmptyTokens>
|
||||
{
|
||||
using BaseT = TokenizeIterator<SimpleDelimiter, ::utf8::unchecked::iterator<typename StringT::const_iterator>, false /* KeepEmptyTokens */>;
|
||||
using BaseT = TokenizeIterator<SimpleDelimiter, ::utf8::unchecked::iterator<typename StringT::const_iterator>, KeepEmptyTokens>;
|
||||
public:
|
||||
SimpleTokenizer(StringT const & str, SimpleDelimiter const & delims)
|
||||
: BaseT(str.begin(), str.end(), delims)
|
||||
|
@ -330,10 +331,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename TFunctor>
|
||||
template <typename TFunctor, bool KeepEmptyTokens = false>
|
||||
void Tokenize(std::string_view str, char const * delims, TFunctor && f)
|
||||
{
|
||||
SimpleTokenizer iter(str, delims);
|
||||
SimpleTokenizer<std::string_view, KeepEmptyTokens> iter(str, delims);
|
||||
while (iter)
|
||||
{
|
||||
f(*iter);
|
||||
|
@ -342,11 +343,12 @@ void Tokenize(std::string_view str, char const * delims, TFunctor && f)
|
|||
}
|
||||
|
||||
/// @note Lifetime of return container is the same as \a str lifetime. Avoid temporary input.
|
||||
template <class ResultT = std::string_view>
|
||||
template <class ResultT = std::string_view, bool KeepEmtyTokens = false>
|
||||
std::vector<ResultT> Tokenize(std::string_view str, char const * delims)
|
||||
{
|
||||
std::vector<ResultT> c;
|
||||
Tokenize(str, delims, [&c](std::string_view v) { c.push_back(ResultT(v)); });
|
||||
auto f = [&c](std::string_view v) { c.push_back(ResultT(v)); };
|
||||
Tokenize<decltype(f), KeepEmtyTokens>(str, delims, std::move(f));
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -428,6 +430,16 @@ bool ToInteger(char const * start, T & result, int base = 10)
|
|||
return internal::ToInteger(s, i, base);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool to_int16(char const * s, short & i, int base = 10)
|
||||
{
|
||||
return internal::ToInteger(s, i, base);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool to_uint16(char const * s, unsigned short & i, int base = 10)
|
||||
{
|
||||
return internal::ToInteger(s, i, base);
|
||||
}
|
||||
|
||||
// Note: negative values will be converted too.
|
||||
// For ex.:
|
||||
// "-1" converts to std::numeric_limits<uint64_t>::max();
|
||||
|
|
|
@ -225,6 +225,8 @@ set(SRC
|
|||
translator_world.hpp
|
||||
translators_pool.cpp
|
||||
translators_pool.hpp
|
||||
turn_lanes_metadata_processor.cpp
|
||||
turn_lanes_metadata_processor.hpp
|
||||
unpack_mwm.cpp
|
||||
unpack_mwm.hpp
|
||||
utils.cpp
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "generator/osm2meta.hpp"
|
||||
|
||||
#include "generator/turn_lanes_metadata_processor.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/ftypes_matcher.hpp"
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
|
@ -170,17 +172,7 @@ std::string MetadataTagProcessorImpl::ValidateAndFormat_junction_ref(std::string
|
|||
|
||||
std::string MetadataTagProcessorImpl::ValidateAndFormat_turn_lanes(std::string const & v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string MetadataTagProcessorImpl::ValidateAndFormat_turn_lanes_forward(std::string const & v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string MetadataTagProcessorImpl::ValidateAndFormat_turn_lanes_backward(std::string const & v)
|
||||
{
|
||||
return v;
|
||||
return generator::TurnLanesMetadataProcessor::ValidateAndFormat(v);
|
||||
}
|
||||
|
||||
std::string MetadataTagProcessorImpl::ValidateAndFormat_email(std::string const & v)
|
||||
|
@ -498,9 +490,9 @@ void MetadataTagProcessor::operator()(std::string const & k, std::string const &
|
|||
case Metadata::FMD_DESTINATION: valid = ValidateAndFormat_destination(v); break;
|
||||
case Metadata::FMD_DESTINATION_REF: valid = ValidateAndFormat_destination_ref(v); break;
|
||||
case Metadata::FMD_JUNCTION_REF: valid = ValidateAndFormat_junction_ref(v); break;
|
||||
case Metadata::FMD_TURN_LANES: valid = ValidateAndFormat_turn_lanes(v); break;
|
||||
case Metadata::FMD_TURN_LANES_FORWARD: valid = ValidateAndFormat_turn_lanes_forward(v); break;
|
||||
case Metadata::FMD_TURN_LANES_BACKWARD: valid = ValidateAndFormat_turn_lanes_backward(v); break;
|
||||
case Metadata::FMD_TURN_LANES:
|
||||
case Metadata::FMD_TURN_LANES_FORWARD:
|
||||
case Metadata::FMD_TURN_LANES_BACKWARD: valid = ValidateAndFormat_turn_lanes(v); break;
|
||||
case Metadata::FMD_EMAIL: valid = ValidateAndFormat_email(v); break;
|
||||
case Metadata::FMD_POSTCODE: valid = ValidateAndFormat_postcode(v); break;
|
||||
case Metadata::FMD_WIKIPEDIA: valid = ValidateAndFormat_wikipedia(v); break;
|
||||
|
|
|
@ -19,9 +19,7 @@ struct MetadataTagProcessorImpl
|
|||
static std::string ValidateAndFormat_destination(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_destination_ref(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_junction_ref(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_turn_lanes(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_turn_lanes_forward(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_turn_lanes_backward(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_turn_lanes(std::string const & v);
|
||||
static std::string ValidateAndFormat_email(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_postcode(std::string const & v) ;
|
||||
static std::string ValidateAndFormat_flats(std::string const & v) ;
|
||||
|
|
67
generator/turn_lanes_metadata_processor.cpp
Normal file
67
generator/turn_lanes_metadata_processor.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "turn_lanes_metadata_processor.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
/// @warning The order of these values must be synchronized with @union LaneWays
|
||||
const std::unordered_map<std::string_view, std::uint8_t> laneWayStrToBitIdx = {
|
||||
// clang-format off
|
||||
{"left", 0},
|
||||
{"slight_left", 1},
|
||||
{"sharp_left", 2},
|
||||
{"through", 3},
|
||||
{"right", 4},
|
||||
{"slight_right", 5},
|
||||
{"sharp_right", 6},
|
||||
{"reverse", 7},
|
||||
{"merge_to_left", 8},
|
||||
{"merge_to_right", 9},
|
||||
{"slide_left", 10},
|
||||
{"slide_right", 11},
|
||||
{"next_right", 12},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::string ToString(std::vector<std::uint16_t> const & lanes)
|
||||
{
|
||||
std::ostringstream os;
|
||||
bool first = true;
|
||||
for (auto const & laneWays : lanes)
|
||||
{
|
||||
if (!first)
|
||||
os << "|";
|
||||
os << std::to_string(laneWays);
|
||||
first = false;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace generator
|
||||
{
|
||||
std::string TurnLanesMetadataProcessor::ValidateAndFormat(std::string value)
|
||||
{
|
||||
if (value.empty())
|
||||
return "";
|
||||
|
||||
strings::AsciiToLower(value);
|
||||
base::EraseIf(value, [](char const c) { return isspace(c); });
|
||||
|
||||
std::vector<std::uint16_t> lanes;
|
||||
for (auto const lanesStr : strings::Tokenize(value, "|"))
|
||||
{
|
||||
lanes.emplace_back(0);
|
||||
auto & laneWays = lanes.back();
|
||||
for (auto const laneWayStr : strings::Tokenize(lanesStr, ";"))
|
||||
{
|
||||
if (laneWayStrToBitIdx.count(laneWayStr))
|
||||
laneWays |= 1 << laneWayStrToBitIdx.at(laneWayStr);
|
||||
}
|
||||
}
|
||||
return ToString(lanes);
|
||||
}
|
||||
} // namespace generator
|
12
generator/turn_lanes_metadata_processor.hpp
Normal file
12
generator/turn_lanes_metadata_processor.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace generator
|
||||
{
|
||||
class TurnLanesMetadataProcessor
|
||||
{
|
||||
public:
|
||||
static std::string ValidateAndFormat(std::string value);
|
||||
};
|
||||
} // namespace generator
|
|
@ -80,6 +80,8 @@ set(SRC
|
|||
joint_segment.hpp
|
||||
junction_visitor.cpp
|
||||
junction_visitor.hpp
|
||||
lanes.cpp
|
||||
lanes.hpp
|
||||
latlon_with_altitude.cpp
|
||||
latlon_with_altitude.hpp
|
||||
leaps_graph.cpp
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "geometry/angles.hpp"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
using namespace std;
|
||||
|
@ -85,7 +83,7 @@ void FixupCarTurns(vector<RouteSegment> & routeSegments)
|
|||
routeSegments[idx - 1].ClearTurn();
|
||||
}
|
||||
}
|
||||
SelectRecommendedLanes(routeSegments);
|
||||
lanes::SelectRecommendedLanes(routeSegments);
|
||||
}
|
||||
|
||||
void GetTurnDirectionBasic(IRoutingResult const & result, size_t const outgoingSegmentIndex,
|
||||
|
@ -599,73 +597,4 @@ size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t const outgoingSeg
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FixupLaneSet(CarDirection turn, vector<SingleLaneInfo> & lanes, bool (*checker)(LaneWay, CarDirection))
|
||||
{
|
||||
bool isLaneConformed = false;
|
||||
// There are two nested loops below. (There is a for-loop in checker.)
|
||||
// But the number of calls of the body of inner one (in checker) is relatively small.
|
||||
// Less than 10 in most cases.
|
||||
for (auto & singleLane : lanes)
|
||||
{
|
||||
for (LaneWay laneWay : singleLane.m_lane)
|
||||
{
|
||||
if (checker(laneWay, turn))
|
||||
{
|
||||
singleLane.m_isRecommended = true;
|
||||
isLaneConformed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isLaneConformed;
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
bool SelectFirstUnrestrictedLane(LaneWay direction, It lanesBegin, It lanesEnd)
|
||||
{
|
||||
const It firstUnrestricted = find_if(lanesBegin, lanesEnd, IsLaneUnrestricted);
|
||||
if (firstUnrestricted == lanesEnd)
|
||||
return false;
|
||||
|
||||
firstUnrestricted->m_isRecommended = true;
|
||||
firstUnrestricted->m_lane.insert(firstUnrestricted->m_lane.begin(), direction);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SelectUnrestrictedLane(CarDirection turn, vector<SingleLaneInfo> & lanes)
|
||||
{
|
||||
if (IsTurnMadeFromLeft(turn))
|
||||
return SelectFirstUnrestrictedLane(LaneWay::Left, lanes.begin(), lanes.end());
|
||||
else if (IsTurnMadeFromRight(turn))
|
||||
return SelectFirstUnrestrictedLane(LaneWay::Right, lanes.rbegin(), lanes.rend());
|
||||
return false;
|
||||
}
|
||||
|
||||
void SelectRecommendedLanes(vector<RouteSegment> & routeSegments)
|
||||
{
|
||||
for (auto & segment : routeSegments)
|
||||
{
|
||||
auto & t = segment.GetTurn();
|
||||
if (t.IsTurnNone() || t.m_lanes.empty())
|
||||
continue;
|
||||
auto & lanes = segment.GetTurnLanes();
|
||||
// Checking if there are elements in lanes which correspond with the turn exactly.
|
||||
// If so fixing up all the elements in lanes which correspond with the turn.
|
||||
if (FixupLaneSet(t.m_turn, lanes, &IsLaneWayConformedTurnDirection))
|
||||
continue;
|
||||
// If not checking if there are elements in lanes which corresponds with the turn
|
||||
// approximately. If so fixing up all these elements.
|
||||
if (FixupLaneSet(t.m_turn, lanes, &IsLaneWayConformedTurnDirectionApproximately))
|
||||
continue;
|
||||
// If not, check if there is an unrestricted lane which could correspond to the
|
||||
// turn. If so, fix up that lane.
|
||||
if (SelectUnrestrictedLane(t.m_turn, lanes))
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace routing
|
||||
|
|
|
@ -24,11 +24,6 @@ protected:
|
|||
virtual void FixupTurns(std::vector<RouteSegment> & routeSegments);
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Selects lanes which are recommended for an end user.
|
||||
*/
|
||||
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments);
|
||||
|
||||
void FixupCarTurns(std::vector<RouteSegment> & routeSegments);
|
||||
|
||||
/*!
|
||||
|
|
|
@ -27,25 +27,6 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
// SingleLaneInfoClient is used for passing information about a lane to client platforms such as
|
||||
// Android, iOS and so on.
|
||||
struct SingleLaneInfoClient
|
||||
{
|
||||
std::vector<int8_t> m_lane; // Possible directions for the lane.
|
||||
bool m_isRecommended; // m_isRecommended is true if the lane is recommended for a user.
|
||||
|
||||
explicit SingleLaneInfoClient(turns::SingleLaneInfo const & singleLaneInfo)
|
||||
: m_isRecommended(singleLaneInfo.m_isRecommended)
|
||||
{
|
||||
turns::TSingleLane const & lane = singleLaneInfo.m_lane;
|
||||
m_lane.resize(lane.size());
|
||||
std::transform(lane.cbegin(), lane.cend(), m_lane.begin(), [](turns::LaneWay l)
|
||||
{
|
||||
return static_cast<int8_t>(l);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bool IsValid() const { return m_distToTarget.IsValid(); }
|
||||
|
||||
/// @name Formatted covered distance.
|
||||
|
@ -61,7 +42,7 @@ public:
|
|||
//@}
|
||||
int m_time;
|
||||
// m_lanes contains lane information on the edge before the turn.
|
||||
std::vector<SingleLaneInfoClient> m_lanes;
|
||||
turns::lanes::LanesInfo m_lanes;
|
||||
// m_turnNotifications contains information about the next turn notifications.
|
||||
// If there is nothing to pronounce m_turnNotifications is empty.
|
||||
// If there is something to pronounce the size of m_turnNotifications may be one or even more
|
||||
|
|
249
routing/lanes.cpp
Normal file
249
routing/lanes.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include "lanes.hpp"
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
using routing::turns::CarDirection;
|
||||
|
||||
// TODO: remove before merge
|
||||
namespace
|
||||
{
|
||||
/// @warning The order of these values must be synchronized with @union LaneWays
|
||||
const std::unordered_map<std::string_view, std::uint8_t> laneWayStrToBitIdx = {
|
||||
// clang-format off
|
||||
{"left", 0},
|
||||
{"slight_left", 1},
|
||||
{"sharp_left", 2},
|
||||
{"through", 3},
|
||||
{"right", 4},
|
||||
{"slight_right", 5},
|
||||
{"sharp_right", 6},
|
||||
{"reverse", 7},
|
||||
{"merge_to_left", 8},
|
||||
{"merge_to_right", 9},
|
||||
{"slide_left", 10},
|
||||
{"slide_right", 11},
|
||||
{"next_right", 12},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::string ToString(std::vector<std::uint16_t> const & lanes)
|
||||
{
|
||||
std::ostringstream os;
|
||||
bool first = true;
|
||||
for (auto const & laneWays : lanes)
|
||||
{
|
||||
if (!first)
|
||||
os << "|";
|
||||
os << std::to_string(laneWays);
|
||||
first = false;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace generator
|
||||
{
|
||||
std::string ValidateAndFormat(std::string value)
|
||||
{
|
||||
if (value.empty())
|
||||
return "";
|
||||
|
||||
strings::AsciiToLower(value);
|
||||
base::EraseIf(value, [](char const c) { return isspace(c); });
|
||||
|
||||
std::vector<std::uint16_t> lanes;
|
||||
for (auto const lanesStr : strings::Tokenize<std::string_view, true>(value, "|"))
|
||||
{
|
||||
lanes.emplace_back(0);
|
||||
auto & laneWays = lanes.back();
|
||||
for (auto const laneWayStr : strings::Tokenize<std::string_view, true>(lanesStr, ";"))
|
||||
{
|
||||
if (laneWayStrToBitIdx.count(laneWayStr))
|
||||
laneWays |= 1 << laneWayStrToBitIdx.at(laneWayStr);
|
||||
}
|
||||
}
|
||||
return ToString(lanes);
|
||||
}
|
||||
}
|
||||
// TODO: Remove before merge
|
||||
|
||||
namespace
|
||||
{
|
||||
using CarDirectionToLaneWays = std::unordered_map<CarDirection, std::uint16_t>;
|
||||
|
||||
const CarDirectionToLaneWays carDirectionToLaneWays{
|
||||
// clang-format off
|
||||
{CarDirection::TurnLeft, 0b00000001}, // Left
|
||||
{CarDirection::TurnSlightLeft, 0b00000010}, // SlightLeft
|
||||
{CarDirection::ExitHighwayToLeft, 0b00000010}, // SlightLeft
|
||||
{CarDirection::TurnSharpLeft, 0b00000100}, // SharpLeft
|
||||
{CarDirection::GoStraight, 0b00001000}, // Through
|
||||
{CarDirection::TurnRight, 0b00010000}, // Right
|
||||
{CarDirection::TurnSlightRight, 0b00100000}, // SlightRight
|
||||
{CarDirection::ExitHighwayToRight, 0b00100000}, // SlightRight
|
||||
{CarDirection::TurnSharpRight, 0b01000000}, // SharpRight
|
||||
{CarDirection::UTurnLeft, 0b10000000}, // Reverse
|
||||
{CarDirection::UTurnRight, 0b10000000}, // Reverse
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
const CarDirectionToLaneWays carDirectionToLaneWaysApproximate{
|
||||
// clang-format off
|
||||
{CarDirection::TurnLeft, 0b0000000000111}, // Left, SlightLeft, SharpLeft
|
||||
{CarDirection::TurnSlightLeft, 0b0000000001011}, // Left, SlightLeft, Through
|
||||
{CarDirection::ExitHighwayToLeft, 0b0000000000011}, // Left, SlightLeft
|
||||
{CarDirection::TurnSharpLeft, 0b0000000000101}, // Left, SharpLeft
|
||||
{CarDirection::GoStraight, 0b0111100101010}, // SlightLeft, Through, SlightRight, MergeToLeft, MergeToRight, SlideLeft, SlideRight
|
||||
{CarDirection::TurnRight, 0b1000001110000}, // Right, SlightRight, SharpRight, NextRight
|
||||
{CarDirection::TurnSlightRight, 0b0000000111000}, // Through, Right, SlightRight
|
||||
{CarDirection::ExitHighwayToRight, 0b0000000110000}, // Right, SlightRight
|
||||
{CarDirection::TurnSharpRight, 0b0000001010000}, // Right, SharpRight
|
||||
{CarDirection::UTurnLeft, 0b0000010000000}, // Reverse
|
||||
{CarDirection::UTurnRight, 0b0000010000000}, // Reverse
|
||||
// clang-format on
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool FixupLaneSet(CarDirection const turn, LanesInfo & lanes, CarDirectionToLaneWays const & mapping)
|
||||
{
|
||||
bool isLaneConformed = false;
|
||||
for (auto & [laneWays, recommendedLaneWays] : lanes)
|
||||
{
|
||||
if (mapping.count(turn))
|
||||
{
|
||||
recommendedLaneWays.data = laneWays.data & mapping.at(turn);
|
||||
if (recommendedLaneWays.data != 0)
|
||||
isLaneConformed = true;
|
||||
}
|
||||
}
|
||||
return isLaneConformed;
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
bool SelectFirstUnrestrictedLane(bool direction, It lanesBegin, It lanesEnd)
|
||||
{
|
||||
const It firstUnrestricted = find_if(lanesBegin, lanesEnd, [](auto const & it) { return it.m_laneWays.data == 0; });
|
||||
if (firstUnrestricted == lanesEnd)
|
||||
return false;
|
||||
|
||||
if (direction)
|
||||
{
|
||||
firstUnrestricted->m_laneWays.turns.left = 1;
|
||||
firstUnrestricted->m_recommendedLaneWays.turns.left = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstUnrestricted->m_laneWays.turns.right = 1;
|
||||
firstUnrestricted->m_recommendedLaneWays.turns.right = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SelectUnrestrictedLane(CarDirection const turn, LanesInfo & lanes)
|
||||
{
|
||||
if (IsTurnMadeFromLeft(turn))
|
||||
return SelectFirstUnrestrictedLane(true, lanes.begin(), lanes.end());
|
||||
if (IsTurnMadeFromRight(turn))
|
||||
return SelectFirstUnrestrictedLane(false, lanes.rbegin(), lanes.rend());
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SelectRecommendedLanes(vector<RouteSegment> & routeSegments)
|
||||
{
|
||||
for (auto & segment : routeSegments)
|
||||
{
|
||||
auto & t = segment.GetTurn();
|
||||
if (t.IsTurnNone() || t.m_lanes.empty())
|
||||
continue;
|
||||
auto & lanes = segment.GetTurnLanes();
|
||||
// Checking if there are elements in lanes which correspond with the turn exactly.
|
||||
// If so fixing up all the elements in lanes which correspond with the turn.
|
||||
if (FixupLaneSet(t.m_turn, lanes, carDirectionToLaneWays))
|
||||
continue;
|
||||
// If not checking if there are elements in lanes which corresponds with the turn
|
||||
// approximately. If so fixing up all these elements.
|
||||
if (FixupLaneSet(t.m_turn, lanes, carDirectionToLaneWaysApproximate))
|
||||
continue;
|
||||
// If not, check if there is an unrestricted lane which could correspond to the
|
||||
// turn. If so, fix up that lane.
|
||||
if (SelectUnrestrictedLane(t.m_turn, lanes))
|
||||
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 ParseLanes(std::string const & lanesString, LanesInfo & lanes)
|
||||
{
|
||||
if (lanesString.empty())
|
||||
return false;
|
||||
lanes.clear();
|
||||
|
||||
// TODO: Complete solution requires maps regeneration. This is a workaround for testing. Remove before merge.
|
||||
std::string const & lanesStr = generator::ValidateAndFormat(lanesString);
|
||||
|
||||
for (auto const laneWaysStr : strings::Tokenize<std::string_view, true>(lanesStr, "|"))
|
||||
{
|
||||
SingleLaneInfo info;
|
||||
// TODO: Fix std::string(laneWaysStr).c_str()
|
||||
VERIFY(strings::to_uint16(std::string(laneWaysStr).c_str(), info.m_laneWays.data), ());
|
||||
ASSERT(info.m_laneWays.turns.unused == 0, ());
|
||||
lanes.push_back(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DebugPrint(LaneWays const laneWays)
|
||||
{
|
||||
std::ostringstream out;
|
||||
if (laneWays.data == 0)
|
||||
{
|
||||
out << "LaneWays[none]";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
out << "LaneWays["
|
||||
<< "Left: " << laneWays.turns.left
|
||||
<< ", SlightLeft: " << laneWays.turns.slightLeft
|
||||
<< ", SharpLeft: " << laneWays.turns.sharpLeft
|
||||
<< ", Through: " << laneWays.turns.through
|
||||
<< ", Right: " << laneWays.turns.right
|
||||
<< ", SlightRight: " << laneWays.turns.slightRight
|
||||
<< ", SharpRight: " << laneWays.turns.sharpRight
|
||||
<< ", Reverse: " << laneWays.turns.reverse
|
||||
<< ", MergeToLeft: " << laneWays.turns.mergeToLeft
|
||||
<< ", MergeToRight: " << laneWays.turns.mergeToRight
|
||||
<< ", SlideLeft: " << laneWays.turns.slideLeft
|
||||
<< ", SlideRight: " << laneWays.turns.slideRight
|
||||
<< ", NextRight: " << laneWays.turns.nextRight
|
||||
<< ", UnusedBits: " << laneWays.turns.unused
|
||||
<< "]";
|
||||
// clang-format on
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string DebugPrint(SingleLaneInfo const & singleLaneInfo)
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "SingleLaneInfo[m_laneWays: " << DebugPrint(singleLaneInfo.m_laneWays)
|
||||
<< ", m_recommendedLaneWays: " << DebugPrint(singleLaneInfo.m_recommendedLaneWays) << "]";
|
||||
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
|
82
routing/lanes.hpp
Normal file
82
routing/lanes.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
class RouteSegment;
|
||||
|
||||
namespace turns
|
||||
{
|
||||
enum class CarDirection;
|
||||
}
|
||||
} // namespace routing
|
||||
|
||||
namespace routing::turns::lanes
|
||||
{
|
||||
/**
|
||||
* \brief This union represents all possible lane turns according to
|
||||
* \link https://wiki.openstreetmap.org/wiki/Key:turn \endlink
|
||||
*/
|
||||
union LaneWays
|
||||
{
|
||||
/**
|
||||
* \warning Do not change the order of these variables.
|
||||
*/
|
||||
struct
|
||||
{
|
||||
// clang-format off
|
||||
std::uint16_t left : 1;
|
||||
std::uint16_t slightLeft : 1;
|
||||
std::uint16_t sharpLeft : 1;
|
||||
std::uint16_t through : 1;
|
||||
std::uint16_t right : 1;
|
||||
std::uint16_t slightRight : 1;
|
||||
std::uint16_t sharpRight : 1;
|
||||
std::uint16_t reverse : 1;
|
||||
std::uint16_t mergeToLeft : 1;
|
||||
std::uint16_t mergeToRight : 1;
|
||||
std::uint16_t slideLeft : 1;
|
||||
std::uint16_t slideRight : 1;
|
||||
std::uint16_t nextRight : 1;
|
||||
std::uint16_t unused : 3;
|
||||
// clang-format on
|
||||
} turns;
|
||||
std::uint16_t data = 0;
|
||||
|
||||
bool operator==(LaneWays const other) const { return data == other.data; }
|
||||
};
|
||||
|
||||
struct SingleLaneInfo
|
||||
{
|
||||
LaneWays m_laneWays;
|
||||
LaneWays m_recommendedLaneWays;
|
||||
|
||||
bool operator==(SingleLaneInfo const & other) const
|
||||
{
|
||||
return m_laneWays == other.m_laneWays && m_recommendedLaneWays == other.m_recommendedLaneWays;
|
||||
}
|
||||
};
|
||||
|
||||
using LanesInfo = std::vector<SingleLaneInfo>;
|
||||
|
||||
/*!
|
||||
* \brief Selects lanes which are recommended for an end user.
|
||||
*/
|
||||
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments);
|
||||
|
||||
/**
|
||||
* \brief Parse lane information which comes from @lanesString
|
||||
* \param lanesString lane information. Example 0b01001|0|0b1000. \see \union LaneWays
|
||||
* \param lanes the result of parsing.
|
||||
* \return true if @lanesString parsed successfully, false otherwise.
|
||||
* \note if @lanesString is empty returns false.
|
||||
*/
|
||||
bool ParseLanes(std::string const & lanesString, LanesInfo & lanes);
|
||||
|
||||
std::string DebugPrint(LaneWays laneWays);
|
||||
std::string DebugPrint(SingleLaneInfo const & singleLaneInfo);
|
||||
std::string DebugPrint(LanesInfo const & lanesInfo);
|
||||
} // namespace routing::turns::lanes
|
|
@ -23,7 +23,7 @@ namespace routing
|
|||
struct LoadedPathSegment
|
||||
{
|
||||
std::vector<geometry::PointWithAltitude> m_path;
|
||||
std::vector<turns::SingleLaneInfo> m_lanes;
|
||||
turns::lanes::LanesInfo m_lanes;
|
||||
RouteSegment::RoadNameInfo m_roadNameInfo;
|
||||
//double m_weight = 0.0; /*!< Time in seconds to pass the segment. */
|
||||
SegmentRange m_segmentRange;
|
||||
|
|
|
@ -111,7 +111,7 @@ public:
|
|||
|
||||
void SetTurnExits(uint32_t exitNum) { m_turn.m_exitNum = exitNum; }
|
||||
|
||||
std::vector<turns::SingleLaneInfo> & GetTurnLanes() { return m_turn.m_lanes; };
|
||||
turns::lanes::LanesInfo & GetTurnLanes() { return m_turn.m_lanes; };
|
||||
|
||||
void SetDistancesAndTime(double distFromBeginningMeters, double distFromBeginningMerc, double timeFromBeginningS)
|
||||
{
|
||||
|
|
|
@ -431,14 +431,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
|
|||
if (distanceToTurnMeters < kShowLanesMinDistInMeters || timeToNearestTurnSec < 60.0)
|
||||
{
|
||||
info.m_displayedStreetName = info.m_targetName;
|
||||
// There are two nested loops below. Outer one is for lanes and inner one (ctor of
|
||||
// SingleLaneInfo) is
|
||||
// for each lane's directions. The size of turn.m_lanes is relatively small. Less than 10 in
|
||||
// most cases.
|
||||
info.m_lanes.clear();
|
||||
info.m_lanes.reserve(turn.m_lanes.size());
|
||||
for (size_t j = 0; j < turn.m_lanes.size(); ++j)
|
||||
info.m_lanes.emplace_back(turn.m_lanes[j]);
|
||||
info.m_lanes = turn.m_lanes;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ set(SRC
|
|||
index_graph_test.cpp
|
||||
index_graph_tools.cpp
|
||||
index_graph_tools.hpp
|
||||
lanes_tests.cpp
|
||||
maxspeeds_tests.cpp
|
||||
mwm_hierarchy_test.cpp
|
||||
nearest_edge_finder_tests.cpp
|
||||
|
|
202
routing/routing_tests/lanes_tests.cpp
Normal file
202
routing/routing_tests/lanes_tests.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/lanes.hpp"
|
||||
#include "routing/car_directions.hpp"
|
||||
/*
|
||||
namespace routing::turns::lanes::test
|
||||
{
|
||||
UNIT_TEST(TestSplitLanes)
|
||||
{
|
||||
vector<string> result;
|
||||
SplitLanes("through|through|through|through;right", '|', result);
|
||||
vector<string> const expected1 = {"through", "through", "through", "through;right"};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
SplitLanes("adsjkddfasui8747&sxdsdlad8\"\'", '|', result);
|
||||
TEST_EQUAL(result, vector<string>({"adsjkddfasui8747&sxdsdlad8\"\'"}), ());
|
||||
|
||||
SplitLanes("|||||||", '|', result);
|
||||
vector<string> expected2 = {"", "", "", "", "", "", ""};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestParseSingleLane)
|
||||
{
|
||||
LaneWays result;
|
||||
TEST(ParseSingleLane("through;right", ';', result), ());
|
||||
LaneWays const expected1 = {LaneWay::Through, LaneWay::Right};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
TEST(!ParseSingleLane("through;Right", ';', result), ());
|
||||
|
||||
TEST(!ParseSingleLane("through ;right", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("SD32kk*887;;", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("Что-то на кириллице", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("משהו בעברית", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(ParseSingleLane("left;through", ';', result), ());
|
||||
LaneWays const expected2 = {LaneWay::Left, LaneWay::Through};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
|
||||
TEST(ParseSingleLane("left", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 1, ());
|
||||
TEST_EQUAL(result[0], LaneWay::Left, ());
|
||||
|
||||
TEST(ParseSingleLane("left;", ';', result), ());
|
||||
LaneWays const expected3 = {LaneWay::Left, LaneWay::None};
|
||||
TEST_EQUAL(result, expected3, ());
|
||||
|
||||
TEST(ParseSingleLane(";", ';', result), ());
|
||||
LaneWays const expected4 = {LaneWay::None, LaneWay::None};
|
||||
TEST_EQUAL(result, expected4, ());
|
||||
|
||||
TEST(ParseSingleLane("", ';', result), ());
|
||||
LaneWays const expected5 = {LaneWay::None};
|
||||
TEST_EQUAL(result, expected5, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestParseLanes)
|
||||
{
|
||||
vector<SingleLaneInfo> result;
|
||||
TEST(ParseLanes("through|through|through|through;right", result), ());
|
||||
vector<SingleLaneInfo> const expected1 = {{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through, LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
TEST(ParseLanes("left|left;through|through|through", result), ());
|
||||
vector<SingleLaneInfo> const expected2 = {
|
||||
{LaneWay::Left}, {LaneWay::Left, LaneWay::Through}, {LaneWay::Through}, {LaneWay::Through}};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
|
||||
TEST(ParseLanes("left|through|through", result), ());
|
||||
vector<SingleLaneInfo> const expected3 = {
|
||||
{LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}};
|
||||
TEST_EQUAL(result, expected3, ());
|
||||
|
||||
TEST(ParseLanes("left|le ft| through|through | right", result), ());
|
||||
vector<SingleLaneInfo> const expected4 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected4, ());
|
||||
|
||||
TEST(ParseLanes("left|Left|through|througH|right", result), ());
|
||||
vector<SingleLaneInfo> const expected5 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected5, ());
|
||||
|
||||
TEST(ParseLanes("left|Left|through|througH|through;right;sharp_rIght", result), ());
|
||||
vector<SingleLaneInfo> const expected6 = {
|
||||
{LaneWay::Left},
|
||||
{LaneWay::Left},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through, LaneWay::Right, LaneWay::SharpRight}};
|
||||
TEST_EQUAL(result, expected6, ());
|
||||
|
||||
TEST(!ParseLanes("left|Leftt|through|througH|right", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseLanes("Что-то на кириллице", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseLanes("משהו בעברית", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(ParseLanes("left |Left|through|througH|right", result), ());
|
||||
vector<SingleLaneInfo> const expected7 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected7, ());
|
||||
|
||||
TEST(ParseLanes("|||||slight_right", result), ());
|
||||
vector<SingleLaneInfo> const expected8 = {
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::SlightRight}
|
||||
};
|
||||
TEST_EQUAL(result, expected8, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestIsLaneWayConformedTurnDirection)
|
||||
{
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Left, CarDirection::TurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Right, CarDirection::TurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, CarDirection::TurnSlightLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::SharpRight, CarDirection::TurnSharpRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::UTurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::UTurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Through, CarDirection::GoStraight), ());
|
||||
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Left, CarDirection::TurnSlightLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Right, CarDirection::TurnSharpRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, CarDirection::GoStraight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::SharpRight, CarDirection::None), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::TurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::None, CarDirection::ReachedYourDestination), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestIsLaneWayConformedTurnDirectionApproximately)
|
||||
{
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, CarDirection::TurnSharpLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, CarDirection::TurnSlightLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, CarDirection::TurnSharpRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, CarDirection::TurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Reverse, CarDirection::UTurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Reverse, CarDirection::UTurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightLeft, CarDirection::GoStraight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight, CarDirection::GoStraight), ());
|
||||
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpLeft, CarDirection::UTurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpLeft, CarDirection::UTurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpRight, CarDirection::UTurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpRight, CarDirection::UTurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Through, CarDirection::ReachedYourDestination), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::Through, CarDirection::TurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight,
|
||||
CarDirection::TurnSharpLeft), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestAddingActiveLaneInformation)
|
||||
{
|
||||
vector<TurnItem> turns =
|
||||
{{1, CarDirection::GoStraight},
|
||||
{2, CarDirection::TurnLeft},
|
||||
{3, CarDirection::TurnRight},
|
||||
{4, CarDirection::ReachedYourDestination}};
|
||||
|
||||
turns[0].m_lanes.push_back({LaneWay::Left, LaneWay::Through});
|
||||
turns[0].m_lanes.push_back({LaneWay::Right});
|
||||
|
||||
turns[1].m_lanes.push_back({LaneWay::SlightLeft});
|
||||
turns[1].m_lanes.push_back({LaneWay::Through});
|
||||
turns[1].m_lanes.push_back({LaneWay::None});
|
||||
|
||||
turns[2].m_lanes.push_back({LaneWay::Left, LaneWay::SharpLeft});
|
||||
turns[2].m_lanes.push_back({LaneWay::None});
|
||||
|
||||
vector<RouteSegment> routeSegments;
|
||||
RouteSegmentsFrom({}, {}, turns, {}, routeSegments);
|
||||
SelectRecommendedLanes(routeSegments);
|
||||
|
||||
TEST(routeSegments[0].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(!routeSegments[0].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
|
||||
TEST(routeSegments[1].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(!routeSegments[1].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
TEST(!routeSegments[1].GetTurn().m_lanes[2].m_isRecommended, ());
|
||||
|
||||
TEST(!routeSegments[2].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(routeSegments[2].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -65,128 +65,6 @@ private:
|
|||
TUnpackedPathSegments m_segments;
|
||||
};
|
||||
|
||||
UNIT_TEST(TestSplitLanes)
|
||||
{
|
||||
vector<string> result;
|
||||
SplitLanes("through|through|through|through;right", '|', result);
|
||||
vector<string> const expected1 = {"through", "through", "through", "through;right"};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
SplitLanes("adsjkddfasui8747&sxdsdlad8\"\'", '|', result);
|
||||
TEST_EQUAL(result, vector<string>({"adsjkddfasui8747&sxdsdlad8\"\'"}), ());
|
||||
|
||||
SplitLanes("|||||||", '|', result);
|
||||
vector<string> expected2 = {"", "", "", "", "", "", ""};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestParseSingleLane)
|
||||
{
|
||||
TSingleLane result;
|
||||
TEST(ParseSingleLane("through;right", ';', result), ());
|
||||
TSingleLane const expected1 = {LaneWay::Through, LaneWay::Right};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
TEST(!ParseSingleLane("through;Right", ';', result), ());
|
||||
|
||||
TEST(!ParseSingleLane("through ;right", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("SD32kk*887;;", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("Что-то на кириллице", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseSingleLane("משהו בעברית", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(ParseSingleLane("left;through", ';', result), ());
|
||||
TSingleLane expected2 = {LaneWay::Left, LaneWay::Through};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
|
||||
TEST(ParseSingleLane("left", ';', result), ());
|
||||
TEST_EQUAL(result.size(), 1, ());
|
||||
TEST_EQUAL(result[0], LaneWay::Left, ());
|
||||
|
||||
TEST(ParseSingleLane("left;", ';', result), ());
|
||||
TSingleLane expected3 = {LaneWay::Left, LaneWay::None};
|
||||
TEST_EQUAL(result, expected3, ());
|
||||
|
||||
TEST(ParseSingleLane(";", ';', result), ());
|
||||
TSingleLane expected4 = {LaneWay::None, LaneWay::None};
|
||||
TEST_EQUAL(result, expected4, ());
|
||||
|
||||
TEST(ParseSingleLane("", ';', result), ());
|
||||
TSingleLane expected5 = {LaneWay::None};
|
||||
TEST_EQUAL(result, expected5, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestParseLanes)
|
||||
{
|
||||
vector<SingleLaneInfo> result;
|
||||
TEST(ParseLanes("through|through|through|through;right", result), ());
|
||||
vector<SingleLaneInfo> const expected1 = {{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through, LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected1, ());
|
||||
|
||||
TEST(ParseLanes("left|left;through|through|through", result), ());
|
||||
vector<SingleLaneInfo> const expected2 = {
|
||||
{LaneWay::Left}, {LaneWay::Left, LaneWay::Through}, {LaneWay::Through}, {LaneWay::Through}};
|
||||
TEST_EQUAL(result, expected2, ());
|
||||
|
||||
TEST(ParseLanes("left|through|through", result), ());
|
||||
vector<SingleLaneInfo> const expected3 = {
|
||||
{LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}};
|
||||
TEST_EQUAL(result, expected3, ());
|
||||
|
||||
TEST(ParseLanes("left|le ft| through|through | right", result), ());
|
||||
vector<SingleLaneInfo> const expected4 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected4, ());
|
||||
|
||||
TEST(ParseLanes("left|Left|through|througH|right", result), ());
|
||||
vector<SingleLaneInfo> const expected5 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected5, ());
|
||||
|
||||
TEST(ParseLanes("left|Left|through|througH|through;right;sharp_rIght", result), ());
|
||||
vector<SingleLaneInfo> const expected6 = {
|
||||
{LaneWay::Left},
|
||||
{LaneWay::Left},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through},
|
||||
{LaneWay::Through, LaneWay::Right, LaneWay::SharpRight}};
|
||||
TEST_EQUAL(result, expected6, ());
|
||||
|
||||
TEST(!ParseLanes("left|Leftt|through|througH|right", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseLanes("Что-то на кириллице", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(!ParseLanes("משהו בעברית", result), ());
|
||||
TEST_EQUAL(result.size(), 0, ());
|
||||
|
||||
TEST(ParseLanes("left |Left|through|througH|right", result), ());
|
||||
vector<SingleLaneInfo> const expected7 = {
|
||||
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
|
||||
TEST_EQUAL(result, expected7, ());
|
||||
|
||||
TEST(ParseLanes("|||||slight_right", result), ());
|
||||
vector<SingleLaneInfo> const expected8 = {
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::None},
|
||||
{LaneWay::SlightRight}
|
||||
};
|
||||
TEST_EQUAL(result, expected8, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestFixupTurns)
|
||||
{
|
||||
double const kHalfSquareSideMeters = 10.;
|
||||
|
@ -260,78 +138,6 @@ UNIT_TEST(TestFixupTurns)
|
|||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(TestIsLaneWayConformedTurnDirection)
|
||||
{
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Left, CarDirection::TurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Right, CarDirection::TurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, CarDirection::TurnSlightLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::SharpRight, CarDirection::TurnSharpRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::UTurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::UTurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirection(LaneWay::Through, CarDirection::GoStraight), ());
|
||||
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Left, CarDirection::TurnSlightLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Right, CarDirection::TurnSharpRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, CarDirection::GoStraight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::SharpRight, CarDirection::None), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Reverse, CarDirection::TurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::None, CarDirection::ReachedYourDestination), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestIsLaneWayConformedTurnDirectionApproximately)
|
||||
{
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, CarDirection::TurnSharpLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, CarDirection::TurnSlightLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, CarDirection::TurnSharpRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, CarDirection::TurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Reverse, CarDirection::UTurnLeft), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Reverse, CarDirection::UTurnRight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightLeft, CarDirection::GoStraight), ());
|
||||
TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight, CarDirection::GoStraight), ());
|
||||
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpLeft, CarDirection::UTurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpLeft, CarDirection::UTurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpRight, CarDirection::UTurnLeft), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpRight, CarDirection::UTurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirection(LaneWay::Through, CarDirection::ReachedYourDestination), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::Through, CarDirection::TurnRight), ());
|
||||
TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight,
|
||||
CarDirection::TurnSharpLeft), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestAddingActiveLaneInformation)
|
||||
{
|
||||
vector<turns::TurnItem> turns =
|
||||
{{1, CarDirection::GoStraight},
|
||||
{2, CarDirection::TurnLeft},
|
||||
{3, CarDirection::TurnRight},
|
||||
{4, CarDirection::ReachedYourDestination}};
|
||||
|
||||
turns[0].m_lanes.push_back({LaneWay::Left, LaneWay::Through});
|
||||
turns[0].m_lanes.push_back({LaneWay::Right});
|
||||
|
||||
turns[1].m_lanes.push_back({LaneWay::SlightLeft});
|
||||
turns[1].m_lanes.push_back({LaneWay::Through});
|
||||
turns[1].m_lanes.push_back({LaneWay::None});
|
||||
|
||||
turns[2].m_lanes.push_back({LaneWay::Left, LaneWay::SharpLeft});
|
||||
turns[2].m_lanes.push_back({LaneWay::None});
|
||||
|
||||
vector<RouteSegment> routeSegments;
|
||||
RouteSegmentsFrom({}, {}, turns, {}, routeSegments);
|
||||
SelectRecommendedLanes(routeSegments);
|
||||
|
||||
TEST(routeSegments[0].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(!routeSegments[0].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
|
||||
TEST(routeSegments[1].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(!routeSegments[1].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
TEST(!routeSegments[1].GetTurn().m_lanes[2].m_isRecommended, ());
|
||||
|
||||
TEST(!routeSegments[2].GetTurn().m_lanes[0].m_isRecommended, ());
|
||||
TEST(routeSegments[2].GetTurn().m_lanes[1].m_isRecommended, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TestGetRoundaboutDirection)
|
||||
{
|
||||
// The signature of GetRoundaboutDirection function is
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "routing/turns.hpp"
|
||||
#include "turns.hpp"
|
||||
|
||||
#include "geometry/angles.hpp"
|
||||
|
||||
|
@ -20,29 +20,6 @@ using namespace std;
|
|||
|
||||
namespace
|
||||
{
|
||||
/// 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.
|
||||
array<pair<LaneWay, char const *>, static_cast<size_t>(LaneWay::Count) + 1> const 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::Reverse, "reverse"}}};
|
||||
static_assert(g_laneWayNames.size() == static_cast<size_t>(LaneWay::Count) + 1,
|
||||
"Check the size of g_laneWayNames");
|
||||
|
||||
array<pair<CarDirection, char const *>, static_cast<size_t>(CarDirection::Count)> const
|
||||
g_turnNames = {{{CarDirection::None, "None"},
|
||||
{CarDirection::GoStraight, "GoStraight"},
|
||||
|
@ -165,19 +142,13 @@ string DebugPrint(SegmentRange const & segmentRange)
|
|||
|
||||
namespace turns
|
||||
{
|
||||
// SingleLaneInfo ---------------------------------------------------------------------------------
|
||||
bool SingleLaneInfo::operator==(SingleLaneInfo const & other) const
|
||||
{
|
||||
return m_lane == other.m_lane && m_isRecommended == other.m_isRecommended;
|
||||
}
|
||||
|
||||
string DebugPrint(TurnItem const & turnItem)
|
||||
{
|
||||
stringstream out;
|
||||
out << "TurnItem "
|
||||
<< "{ m_index = " << turnItem.m_index
|
||||
<< ", m_turn = " << DebugPrint(turnItem.m_turn)
|
||||
<< ", m_lanes = " << ::DebugPrint(turnItem.m_lanes)
|
||||
<< ", m_lanes = " << lanes::DebugPrint(turnItem.m_lanes)
|
||||
<< ", m_exitNum = " << turnItem.m_exitNum
|
||||
<< ", m_pedestrianDir = " << DebugPrint(turnItem.m_pedestrianTurn)
|
||||
<< " }";
|
||||
|
@ -242,146 +213,6 @@ bool IsGoStraightOrSlightTurn(CarDirection t)
|
|||
t == CarDirection::TurnSlightRight);
|
||||
}
|
||||
|
||||
bool IsLaneWayConformedTurnDirection(LaneWay l, CarDirection t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case CarDirection::GoStraight:
|
||||
return l == LaneWay::Through;
|
||||
case CarDirection::TurnRight:
|
||||
return l == LaneWay::Right;
|
||||
case CarDirection::TurnSharpRight:
|
||||
return l == LaneWay::SharpRight;
|
||||
case CarDirection::TurnSlightRight:
|
||||
case CarDirection::ExitHighwayToRight:
|
||||
return l == LaneWay::SlightRight;
|
||||
case CarDirection::TurnLeft:
|
||||
return l == LaneWay::Left;
|
||||
case CarDirection::TurnSharpLeft:
|
||||
return l == LaneWay::SharpLeft;
|
||||
case CarDirection::TurnSlightLeft:
|
||||
case CarDirection::ExitHighwayToLeft:
|
||||
return l == LaneWay::SlightLeft;
|
||||
case CarDirection::UTurnLeft:
|
||||
case CarDirection::UTurnRight:
|
||||
return l == LaneWay::Reverse;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, CarDirection t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case CarDirection::GoStraight:
|
||||
return l == LaneWay::Through || l == LaneWay::SlightRight || l == LaneWay::SlightLeft;
|
||||
case CarDirection::TurnRight:
|
||||
return l == LaneWay::Right || l == LaneWay::SharpRight || l == LaneWay::SlightRight;
|
||||
case CarDirection::TurnSharpRight:
|
||||
return l == LaneWay::SharpRight || l == LaneWay::Right;
|
||||
case CarDirection::TurnSlightRight:
|
||||
return l == LaneWay::SlightRight || l == LaneWay::Through || l == LaneWay::Right;
|
||||
case CarDirection::TurnLeft:
|
||||
return l == LaneWay::Left || l == LaneWay::SlightLeft || l == LaneWay::SharpLeft;
|
||||
case CarDirection::TurnSharpLeft:
|
||||
return l == LaneWay::SharpLeft || l == LaneWay::Left;
|
||||
case CarDirection::TurnSlightLeft:
|
||||
return l == LaneWay::SlightLeft || l == LaneWay::Through || l == LaneWay::Left;
|
||||
case CarDirection::UTurnLeft:
|
||||
case CarDirection::UTurnRight:
|
||||
return l == LaneWay::Reverse;
|
||||
case CarDirection::ExitHighwayToLeft:
|
||||
return l == LaneWay::SlightLeft || l == LaneWay::Left;
|
||||
case CarDirection::ExitHighwayToRight:
|
||||
return l == LaneWay::SlightRight || l == LaneWay::Right;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLaneUnrestricted(const SingleLaneInfo & lane)
|
||||
{
|
||||
/// @todo Is there any reason to store None single lane?
|
||||
return lane.m_lane.size() == 1 && lane.m_lane[0] == LaneWay::None;
|
||||
}
|
||||
|
||||
void SplitLanes(string const & lanesString, char delimiter, vector<string> & lanes)
|
||||
{
|
||||
lanes.clear();
|
||||
istringstream lanesStream(lanesString);
|
||||
string token;
|
||||
while (getline(lanesStream, token, delimiter))
|
||||
{
|
||||
lanes.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
bool ParseSingleLane(string const & laneString, char delimiter, TSingleLane & lane)
|
||||
{
|
||||
lane.clear();
|
||||
// When `laneString` ends with "" representing none, for example, in "right;",
|
||||
// `getline` will not read any characters, so it exits the loop and does not
|
||||
// handle the "". So, we add a delimiter to the end of `laneString`. Nonempty
|
||||
// final tokens consume the delimiter and act as expected, and empty final tokens
|
||||
// read a the delimiter, so `getline` sets `token` to the empty string rather than
|
||||
// exiting the loop.
|
||||
istringstream laneStream(laneString + delimiter);
|
||||
string token;
|
||||
while (getline(laneStream, token, delimiter))
|
||||
{
|
||||
auto const it = find_if(g_laneWayNames.begin(), g_laneWayNames.end(),
|
||||
[&token](pair<LaneWay, string> const & p)
|
||||
{
|
||||
return p.second == token;
|
||||
});
|
||||
if (it == g_laneWayNames.end())
|
||||
return false;
|
||||
lane.push_back(it->first);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseLanes(string lanesString, vector<SingleLaneInfo> & lanes)
|
||||
{
|
||||
if (lanesString.empty())
|
||||
return false;
|
||||
lanes.clear();
|
||||
strings::AsciiToLower(lanesString);
|
||||
base::EraseIf(lanesString, [](char c) { return isspace(c); });
|
||||
|
||||
vector<string> SplitLanesStrings;
|
||||
SingleLaneInfo lane;
|
||||
SplitLanes(lanesString, '|', SplitLanesStrings);
|
||||
for (string const & s : SplitLanesStrings)
|
||||
{
|
||||
if (!ParseSingleLane(s, ';', lane.m_lane))
|
||||
{
|
||||
lanes.clear();
|
||||
return false;
|
||||
}
|
||||
lanes.push_back(lane);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
string DebugPrint(LaneWay const l)
|
||||
{
|
||||
auto const it = find_if(g_laneWayNames.begin(), g_laneWayNames.end(),
|
||||
[&l](pair<LaneWay, string> const & p)
|
||||
{
|
||||
return p.first == l;
|
||||
});
|
||||
|
||||
if (it == g_laneWayNames.end())
|
||||
{
|
||||
stringstream out;
|
||||
out << "unknown LaneWay (" << static_cast<int>(l) << ")";
|
||||
return out.str();
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
string DebugPrint(CarDirection const turn)
|
||||
{
|
||||
return GetTurnString(turn);
|
||||
|
@ -406,14 +237,6 @@ string DebugPrint(PedestrianDirection const l)
|
|||
return "unknown PedestrianDirection";
|
||||
}
|
||||
|
||||
string DebugPrint(SingleLaneInfo const & singleLaneInfo)
|
||||
{
|
||||
stringstream out;
|
||||
out << "SingleLaneInfo [ m_isRecommended == " << singleLaneInfo.m_isRecommended
|
||||
<< ", m_lane == " << ::DebugPrint(singleLaneInfo.m_lane) << " ]" << endl;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
double PiMinusTwoVectorsAngle(m2::PointD const & junctionPoint, m2::PointD const & ingoingPoint,
|
||||
m2::PointD const & outgoingPoint)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/segment.hpp"
|
||||
#include "routing/lanes.hpp"
|
||||
|
||||
#include "routing_common/num_mwm_id.hpp"
|
||||
|
||||
|
@ -118,41 +119,6 @@ enum class PedestrianDirection
|
|||
|
||||
std::string DebugPrint(PedestrianDirection const l);
|
||||
|
||||
/*!
|
||||
* \warning The values of LaneWay shall be synchronized with values of LaneWay enum in java.
|
||||
*/
|
||||
enum class LaneWay
|
||||
{
|
||||
None = 0,
|
||||
Reverse,
|
||||
SharpLeft,
|
||||
Left,
|
||||
SlightLeft,
|
||||
MergeToRight,
|
||||
Through,
|
||||
MergeToLeft,
|
||||
SlightRight,
|
||||
Right,
|
||||
SharpRight,
|
||||
Count /**< This value is used for internals only. */
|
||||
};
|
||||
|
||||
std::string DebugPrint(LaneWay const l);
|
||||
|
||||
typedef std::vector<LaneWay> TSingleLane;
|
||||
|
||||
struct SingleLaneInfo
|
||||
{
|
||||
TSingleLane m_lane;
|
||||
bool m_isRecommended = false;
|
||||
|
||||
SingleLaneInfo() = default;
|
||||
SingleLaneInfo(std::initializer_list<LaneWay> const & l) : m_lane(l) {}
|
||||
bool operator==(SingleLaneInfo const & other) const;
|
||||
};
|
||||
|
||||
std::string DebugPrint(SingleLaneInfo const & singleLaneInfo);
|
||||
|
||||
struct TurnItem
|
||||
{
|
||||
TurnItem()
|
||||
|
@ -192,10 +158,10 @@ struct TurnItem
|
|||
return m_turn == CarDirection::None && m_pedestrianTurn == PedestrianDirection::None;
|
||||
}
|
||||
|
||||
uint32_t m_index; /*!< Index of point on route polyline (Index of segment + 1). */
|
||||
CarDirection m_turn = CarDirection::None; /*!< The turn instruction of the TurnItem */
|
||||
std::vector<SingleLaneInfo> m_lanes; /*!< Lane information on the edge before the turn. */
|
||||
uint32_t m_exitNum; /*!< Number of exit on roundabout. */
|
||||
uint32_t m_index; // Index of point on route polyline (Index of segment + 1).
|
||||
CarDirection m_turn = CarDirection::None; // The turn instruction of the TurnItem.
|
||||
lanes::LanesInfo m_lanes; // Lane information on the edge before the turn.
|
||||
uint32_t m_exitNum; // Number of exit on roundabout.
|
||||
/*!
|
||||
* \brief m_pedestrianTurn is type of corresponding direction for a pedestrian, or None
|
||||
* if there is no pedestrian specific direction
|
||||
|
@ -229,38 +195,6 @@ bool IsTurnMadeFromRight(CarDirection t);
|
|||
bool IsStayOnRoad(CarDirection t);
|
||||
bool IsGoStraightOrSlightTurn(CarDirection t);
|
||||
|
||||
/*!
|
||||
* \param l A variant of going along a lane.
|
||||
* \param t A turn direction.
|
||||
* \return True if @l corresponds with @t exactly. For example it returns true
|
||||
* when @l equals to LaneWay::Right and @t equals to TurnDirection::TurnRight.
|
||||
* Otherwise it returns false.
|
||||
*/
|
||||
bool IsLaneWayConformedTurnDirection(LaneWay l, CarDirection t);
|
||||
|
||||
/*!
|
||||
* \param l A variant of going along a lane.
|
||||
* \param t A turn direction.
|
||||
* \return True if @l corresponds with @t approximately. For example it returns true
|
||||
* when @l equals to LaneWay::Right and @t equals to TurnDirection::TurnSlightRight.
|
||||
* Otherwise it returns false.
|
||||
*/
|
||||
bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, CarDirection t);
|
||||
|
||||
bool IsLaneUnrestricted(const SingleLaneInfo & lane);
|
||||
|
||||
/*!
|
||||
* \brief Parse lane information which comes from @lanesString
|
||||
* \param lanesString lane information. Example through|through|through|through;right
|
||||
* \param lanes the result of parsing.
|
||||
* \return true if @lanesString parsed successfully, false otherwise.
|
||||
* Note 1: if @lanesString is empty returns false.
|
||||
* Note 2: @laneString is passed by value on purpose. It'll be used(changed) in the method.
|
||||
*/
|
||||
bool ParseLanes(std::string lanesString, std::vector<SingleLaneInfo> & lanes);
|
||||
void SplitLanes(std::string const & lanesString, char delimiter, std::vector<std::string> & lanes);
|
||||
bool ParseSingleLane(std::string const & laneString, char delimiter, TSingleLane & lane);
|
||||
|
||||
/*!
|
||||
* \returns pi minus angle from vector [junctionPoint, ingoingPoint]
|
||||
* to vector [junctionPoint, outgoingPoint]. A counterclockwise rotation.
|
||||
|
|
Reference in a new issue