Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Koronchik <denis@mapswithme.com>2014-11-13 17:11:00 +0300
committerAlex Zolotarev <alex@maps.me>2015-09-23 02:33:17 +0300
commit0ba3d4287e0dd96b3799a070e226ca99690e42c6 (patch)
treeb63cefd4b376fec0e92d6b48ac136ba13a4d3104
parentbd3ec5e57651ebe476016c4d45574d154bf5b88c (diff)
[routing][framework] Add functions to get next turn information and route time
-rw-r--r--android/jni/com/mapswithme/maps/Framework.cpp2
-rw-r--r--indexer/ftypes_matcher.cpp21
-rw-r--r--indexer/ftypes_matcher.hpp7
-rw-r--r--iphone/Maps/Classes/MapViewController.mm2
-rw-r--r--map/routing_session.cpp27
-rw-r--r--platform/location.hpp13
-rw-r--r--routing/osrm_router.cpp239
-rw-r--r--routing/osrm_router.hpp9
-rw-r--r--routing/route.cpp32
-rw-r--r--routing/route.hpp43
-rw-r--r--routing/routing.pro2
-rw-r--r--routing/turns.cpp59
-rw-r--r--routing/turns.hpp51
13 files changed, 412 insertions, 95 deletions
diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp
index 9ba64b7c67..9b8051f5cc 100644
--- a/android/jni/com/mapswithme/maps/Framework.cpp
+++ b/android/jni/com/mapswithme/maps/Framework.cpp
@@ -1294,7 +1294,7 @@ extern "C"
return env->NewObject(klass, methodID,
jni::ToJavaString(env, info.m_distToTarget),
- jni::ToJavaString(env, info.m_unitsSuffix));
+ jni::ToJavaString(env, info.m_targetUnitsSuffix));
}
}
diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp
index 5b3119d594..2b4f4543be 100644
--- a/indexer/ftypes_matcher.cpp
+++ b/indexer/ftypes_matcher.cpp
@@ -94,6 +94,27 @@ IsRoundAboutChecker const & IsRoundAboutChecker::Instance()
return inst;
}
+IsLinkChecker::IsLinkChecker()
+{
+ Classificator const & c = classif();
+ char const * arr[][2] = {
+ { "highway", "motorway_link" },
+ { "highway", "trunk_link" },
+ { "highway", "primary_link" },
+ { "highway", "secondary_link" },
+ { "highway", "tertiary_link" }
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
+ m_types.push_back(c.GetTypeByPath(vector<string>(arr[i], arr[i] + 2)));
+}
+
+IsLinkChecker const & IsLinkChecker::Instance()
+{
+ static const IsLinkChecker inst;
+ return inst;
+}
+
IsBuildingChecker::IsBuildingChecker()
{
Classificator const & c = classif();
diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp
index ecf1aabe26..2c1b905ed5 100644
--- a/indexer/ftypes_matcher.hpp
+++ b/indexer/ftypes_matcher.hpp
@@ -50,6 +50,13 @@ public:
static IsRoundAboutChecker const & Instance();
};
+class IsLinkChecker : public BaseChecker
+{
+public:
+ IsLinkChecker();
+ static IsLinkChecker const & Instance();
+};
+
class IsBuildingChecker : public BaseChecker
{
public:
diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm
index 2cbf42a49a..0523731c57 100644
--- a/iphone/Maps/Classes/MapViewController.mm
+++ b/iphone/Maps/Classes/MapViewController.mm
@@ -128,7 +128,7 @@
if (res.IsValid())
{
[self.routeView updateDistance:[NSString stringWithUTF8String:res.m_distToTarget.c_str()]
- withMetrics:[NSString stringWithUTF8String:res.m_unitsSuffix.c_str()]];
+ withMetrics:[NSString stringWithUTF8String:res.m_targetUnitsSuffix.c_str()]];
}
}
}
diff --git a/map/routing_session.cpp b/map/routing_session.cpp
index 3044db1b4d..3f1d433d64 100644
--- a/map/routing_session.cpp
+++ b/map/routing_session.cpp
@@ -119,15 +119,32 @@ RoutingSession::State RoutingSession::OnLocationPositionChanged(m2::PointD const
void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
{
- if (m_route.IsValid())
+ auto formatDistFn = [](double dist, string & value, string & suffix)
{
/// @todo Make better formatting of distance and units.
- MeasurementUtils::FormatDistance(m_route.GetCurrentDistanceToEnd(), info.m_distToTarget);
+ MeasurementUtils::FormatDistance(dist, value);
- size_t const delim = info.m_distToTarget.find(' ');
+ size_t const delim = value.find(' ');
ASSERT(delim != string::npos, ());
- info.m_unitsSuffix = info.m_distToTarget.substr(delim + 1);
- info.m_distToTarget.erase(delim);
+ suffix = value.substr(delim + 1);
+ value.erase(delim);
+ };
+
+ if (m_route.IsValid())
+ {
+ formatDistFn(m_route.GetCurrentDistanceToEnd(), info.m_distToTarget, info.m_targetUnitsSuffix);
+
+ {
+ double dist;
+ Route::TurnItem turn;
+ m_route.GetTurn(dist, turn);
+
+ formatDistFn(dist, info.m_distToTurn, info.m_turnUnitsSuffix);
+ info.m_turn = turn.m_turn;
+ info.m_exitNum = turn.m_exitNum;
+ }
+
+ info.m_time = m_route.GetTime();
}
}
diff --git a/platform/location.hpp b/platform/location.hpp
index 922926e3c3..978bf8aeab 100644
--- a/platform/location.hpp
+++ b/platform/location.hpp
@@ -2,6 +2,8 @@
#include "../base/base.hpp"
+#include "../routing/turns.hpp"
+
#include "../std/string.hpp"
@@ -77,8 +79,17 @@ namespace location
/// @name Formatted covered distance with measurement units suffix.
//@{
string m_distToTarget;
- string m_unitsSuffix;
+ string m_targetUnitsSuffix;
+ //@}
+
+ /// @name Formated distance to next turn with measurement unit suffix
+ //@{
+ string m_distToTurn;
+ string m_turnUnitsSuffix;
+ routing::turns::TurnDirection m_turn;
+ uint32_t m_exitNum;
//@}
+ int m_time;
bool IsValid() const { return !m_distToTarget.empty(); }
};
diff --git a/routing/osrm_router.cpp b/routing/osrm_router.cpp
index edfe909318..f9a2278273 100644
--- a/routing/osrm_router.cpp
+++ b/routing/osrm_router.cpp
@@ -516,7 +516,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
break;
}
- m_dataFacade.Clear();
+ //m_dataFacade.Clear();
if (!IsRouteExist(rawRoute))
return RouteNotFound;
@@ -541,7 +541,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
ASSERT(segBegin.IsValid(), ());
ASSERT(segEnd.IsValid(), ());
- Route::TurnsT turns;
+ Route::TurnsT turnsDir;
uint32_t estimateTime = 0;
vector<m2::PointD> points;
@@ -551,17 +551,26 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
return Cancelled;
// Get all the coordinates for the computed route
- m2::PointD p1, p;
- feature::TypesHolder fTypePrev;
- string namePrev;
-
size_t const n = rawRoute.unpacked_path_segments[i].size();
for (size_t j = 0; j < n; ++j)
{
PathData const & path_data = rawRoute.unpacked_path_segments[i][j];
- if (j != 0)
+
+ // turns
+ if (j > 0)
+ {
estimateTime += path_data.segment_duration;
+ Route::TurnItem t;
+ t.m_index = points.size() - 1;
+
+ GetTurnDirection(rawRoute.unpacked_path_segments[i][j - 1],
+ rawRoute.unpacked_path_segments[i][j],
+ mwmId, t);
+ if (t.m_turn != turns::NoTurn)
+ turnsDir.push_back(t);
+ }
+
buffer_vector<SegT, 8> buffer;
m_mapping.ForEachFtSeg(path_data.node, MakeBackInsertFunctor(buffer));
@@ -604,34 +613,6 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
if (j == n - 1 && k == endK - 1)
endIdx = (seg.m_pointEnd > seg.m_pointStart) ? segEnd.m_pointEnd : segEnd.m_pointStart;
-
- if (j > 0 && k == startK)
- {
- ASSERT_LESS(MercatorBounds::DistanceOnEarth(p, ft.GetPoint(startIdx)), 2, ());
-
- m2::PointD const p2 = ft.GetPoint((seg.m_pointEnd > seg.m_pointStart) ? startIdx + 1 : startIdx - 1);
-
- string name;
- ft.GetName(FeatureType::DEFAULT_LANG, name);
-
- string n1, n2;
- search::GetStreetNameAsKey(namePrev, n1);
- search::GetStreetNameAsKey(name, n2);
-
- Route::TurnInstruction const t = GetTurnInstruction(fTypePrev, ft, p1, p, p2, (!n1.empty() && (n1 == n2)));
-
- if (t != Route::NoTurn)
- turns.push_back(Route::TurnItemT(points.size(), t));
- }
-
- if (k == endK - 1)
- {
- fTypePrev = feature::TypesHolder(ft);
- ft.GetName(FeatureType::DEFAULT_LANG, namePrev);
- p1 = ft.GetPoint((seg.m_pointEnd > seg.m_pointStart) ? (endIdx - 1) : (endIdx + 1));
- p = ft.GetPoint(endIdx);
- }
-
if (seg.m_pointEnd > seg.m_pointStart)
{
for (auto idx = startIdx; idx <= endIdx; ++idx)
@@ -644,6 +625,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
points.push_back(ft.GetPoint(endIdx));
}
}
+
}
}
@@ -653,11 +635,19 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
if (points.size() < 2)
return RouteNotFound;
+ turnsDir.push_back(Route::TurnItem(points.size() - 1, turns::ReachedYourDestination));
+ FixupTurns(points, turnsDir);
+
// osrm multiple seconds to 10, so we need to divide it back
estimateTime /= 10;
+#ifdef _DEBUG
+ for (auto t : turnsDir)
+ LOG(LDEBUG, (turns::GetTurnString(t.m_turn), ":", t.m_index, t.m_srcName, "-", t.m_trgName, "exit:", t.m_exitNum));
+#endif
+
route.SetGeometry(points.begin(), points.end());
- route.SetTurnInstructions(turns);
+ route.SetTurnInstructions(turnsDir);
route.SetTime(estimateTime);
LOG(LDEBUG, ("Estimate time:", estimateTime, "s"));
@@ -665,51 +655,176 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt
return NoError;
}
-Route::TurnInstruction OsrmRouter::GetTurnInstruction(feature::TypesHolder const & ft1, feature::TypesHolder const & ft2,
- m2::PointD const & p1, m2::PointD const & p, m2::PointD const & p2,
- bool isStreetEqual) const
+void OsrmRouter::GetTurnDirection(PathData const & node1,
+ PathData const & node2,
+ uint32_t mwmId, Route::TurnItem & turn)
{
+
+ auto nSegs1 = m_mapping.GetSegmentsRange(node1.node);
+ auto nSegs2 = m_mapping.GetSegmentsRange(node2.node);
+
+ ASSERT_GREATER(nSegs1.second, 0, ());
+
+ OsrmFtSegMapping::FtSeg seg1, seg2;
+ m_mapping.GetSegmentByIndex(nSegs1.second - 1, seg1);
+ m_mapping.GetSegmentByIndex(nSegs2.first, seg2);
+
+ FeatureType ft1, ft2;
+ Index::FeaturesLoaderGuard loader1(*m_pIndex, mwmId);
+ Index::FeaturesLoaderGuard loader2(*m_pIndex, mwmId);
+
+ loader1.GetFeature(seg1.m_fid, ft1);
+ loader2.GetFeature(seg2.m_fid, ft2);
+
+ ft1.ParseGeometry(FeatureType::BEST_GEOMETRY);
+ ft2.ParseGeometry(FeatureType::BEST_GEOMETRY);
+
+ ASSERT_LESS(MercatorBounds::DistanceOnEarth(ft1.GetPoint(seg1.m_pointEnd), ft2.GetPoint(seg2.m_pointStart)), 2, ());
+
+ m2::PointD p = ft1.GetPoint(seg1.m_pointEnd);
+ m2::PointD p1 = ft1.GetPoint(seg1.m_pointEnd > seg1.m_pointStart ? seg1.m_pointEnd - 1 : seg1.m_pointEnd + 1);
+ m2::PointD p2 = ft2.GetPoint(seg2.m_pointEnd > seg2.m_pointStart ? seg2.m_pointStart + 1 : seg2.m_pointStart - 1);
+
+ double a = my::RadToDeg(ang::AngleTo(p, p2) - ang::AngleTo(p, p1));
+ while (a < 0)
+ a += 360;
+
+ turn.m_turn = turns::NoTurn;
+ if (a >= 23 && a < 67)
+ turn.m_turn = turns::TurnSharpRight;
+ else if (a >= 67 && a < 113)
+ turn.m_turn = turns::TurnRight;
+ else if (a >= 113 && a < 177)
+ turn.m_turn = turns::TurnSlightRight;
+ else if (a >= 177 && a < 183)
+ turn.m_turn = turns::GoStraight;
+ else if (a >= 183 && a < 248)
+ turn.m_turn = turns::TurnSlightLeft;
+ else if (a >= 248 && a < 292)
+ turn.m_turn = turns::TurnLeft;
+ else if (a >= 292 && a < 336)
+ turn.m_turn = turns::TurnSharpLeft;
+
bool const isRound1 = ftypes::IsRoundAboutChecker::Instance()(ft1);
bool const isRound2 = ftypes::IsRoundAboutChecker::Instance()(ft2);
+ auto hasMultiTurns = [&]()
+ {
+ for (EdgeID e : m_dataFacade.GetAdjacentEdgeRange(node1.node))
+ {
+ QueryEdge::EdgeData const & d = m_dataFacade.GetEdgeData(e, node1.node);
+ if (d.forward && m_dataFacade.GetTarget(e) != node2.node)
+ return true;
+ }
+ return false;
+ };
+
if (isRound1 && isRound2)
- return Route::StayOnRoundAbout;
+ {
+ if (hasMultiTurns())
+ turn.m_turn = turns::StayOnRoundAbout;
+ else // No turn possible.
+ turn.m_turn = turns::NoTurn;
+ return;
+ }
if (!isRound1 && isRound2)
- return Route::EnterRoundAbout;
+ {
+ turn.m_turn = turns::EnterRoundAbout;
+ return;
+ }
if (isRound1 && !isRound2)
- return Route::LeaveRoundAbout;
+ {
+ STATIC_ASSERT(turns::TurnRight < turns::TurnSlightRight);
+ STATIC_ASSERT(turns::TurnLeft < turns::TurnSlightLeft);
+
+ /// @todo: add support of left-hand traffic
+ if (!turns::IsLeftTurn(turn.m_turn))
+ turn.m_turn = turns::LeaveRoundAbout;
+ return;
+ }
- if (isStreetEqual)
- return Route::NoTurn;
+ // get names
+ string name1, name2;
+ {
+ ft1.GetName(FeatureType::DEFAULT_LANG, turn.m_srcName);
+ ft2.GetName(FeatureType::DEFAULT_LANG, turn.m_trgName);
- double a = my::RadToDeg(ang::AngleTo(p, p2) - ang::AngleTo(p, p1));
- while (a < 0)
- a += 360;
+ search::GetStreetNameAsKey(turn.m_srcName, name1);
+ search::GetStreetNameAsKey(turn.m_trgName, name2);
+ }
- if (a >= 23 && a < 67)
- return Route::TurnSharpRight;
+ string road1 = ft1.GetRoadNumber();
+ string road2 = ft2.GetRoadNumber();
+
+ if ((!name1.empty() && name1 == name2) ||
+ (!road1.empty() && road1 == road2))
+ {
+ turn.m_turn = turns::NoTurn;
+ return;
+ }
+
+ if (turn.m_turn == turns::GoStraight)
+ {
+ if (!hasMultiTurns())
+ turn.m_turn = turns::NoTurn;
+
+ return;
+ }
- if (a >= 67 && a < 113)
- return Route::TurnRight;
+ if (turn.m_turn == turns::NoTurn)
+ turn.m_turn = turns::UTurn;
+}
- if (a >= 113 && a < 158)
- return Route::TurnSlightRight;
+void OsrmRouter::FixupTurns(vector<m2::PointD> const & points, Route::TurnsT & turnsDir) const
+{
+ uint32_t exitNum = 0;
+ Route::TurnItem * roundabout = 0;
- if (a >= 158 && a < 202)
- return Route::GoStraight;
+ auto distance = [&points](uint32_t start, uint32_t end)
+ {
+ double res = 0.0;
+ for (uint32_t i = start + 1; i < end; ++i)
+ res += MercatorBounds::DistanceOnEarth(points[i - 1], points[i]);
+ return res;
+ };
- if (a >= 202 && a < 248)
- return Route::TurnSlightLeft;
+ for (uint32_t idx = 0; idx < turnsDir.size(); )
+ {
+ Route::TurnItem & t = turnsDir[idx];
+ if (roundabout && t.m_turn != turns::StayOnRoundAbout && t.m_turn != turns::LeaveRoundAbout)
+ {
+ exitNum = 0;
+ roundabout = 0;
+ }
+ else if (t.m_turn == turns::EnterRoundAbout)
+ {
+ ASSERT_EQUAL(roundabout, 0, ());
+ roundabout = &t;
+ }
+ else if (t.m_turn == turns::StayOnRoundAbout)
+ ++exitNum;
+ else if (t.m_turn == turns::LeaveRoundAbout)
+ {
+ roundabout->m_exitNum = exitNum + 1;
+ roundabout = 0;
+ exitNum = 0;
+ }
- if (a >= 248 && a < 292)
- return Route::TurnLeft;
+ double const mergeDist = 30.0;
- if (a >= 292 && a < 336)
- return Route::TurnSharpLeft;
+ if (idx > 0 &&
+ turns::IsStayOnRoad(turnsDir[idx - 1].m_turn) &&
+ turns::IsLeftOrRightTurn(turnsDir[idx].m_turn) &&
+ distance(turnsDir[idx - 1].m_index, turnsDir[idx].m_index) < mergeDist)
+ {
+ turnsDir.erase(turnsDir.begin() + idx - 1);
+ continue;
+ }
- return Route::UTurn;
+ ++idx;
+ }
}
IRouter::ResultCode OsrmRouter::FindPhantomNodes(string const & fName, m2::PointD const & startPt, m2::PointD const & finalPt,
diff --git a/routing/osrm_router.hpp b/routing/osrm_router.hpp
index 54a3ac1c63..fcab040be8 100644
--- a/routing/osrm_router.hpp
+++ b/routing/osrm_router.hpp
@@ -16,6 +16,7 @@ namespace feature { class TypesHolder; }
class Index;
struct PhantomNode;
+struct PathData;
namespace routing
{
@@ -51,10 +52,10 @@ protected:
void CalculateRouteAsync(ReadyCallback const & callback);
ResultCode CalculateRouteImpl(m2::PointD const & startPt, m2::PointD const & finalPt, Route & route);
- Route::TurnInstruction GetTurnInstruction(feature::TypesHolder const & ft1, feature::TypesHolder const & ft2,
- m2::PointD const & p1, m2::PointD const & p, m2::PointD const & p2,
- bool isStreetEqual) const;
-
+ void GetTurnDirection(PathData const & node1,
+ PathData const & node2,
+ uint32_t mwmId, Route::TurnItem & turn);
+ void FixupTurns(vector<m2::PointD> const & points, Route::TurnsT & turnsDir) const;
private:
Index const * m_pIndex;
diff --git a/routing/route.cpp b/routing/route.cpp
index 863fefdb38..4594e11168 100644
--- a/routing/route.cpp
+++ b/routing/route.cpp
@@ -35,6 +35,7 @@ void Route::Swap(Route & rhs)
swap(m_current, rhs.m_current);
swap(m_currentTime, rhs.m_currentTime);
swap(m_turns, rhs.m_turns);
+ swap(m_time, rhs.m_time);
}
void Route::SetTurnInstructions(TurnsT & v)
@@ -74,6 +75,37 @@ double Route::GetCurrentDistanceToEnd() const
MercatorBounds::DistanceOnEarth(m_current.m_pt, m_poly.GetPoint(m_current.m_ind + 1)));
}
+void Route::GetTurn(double & distance, Route::TurnItem & turn) const
+{
+ if (m_segDistance.empty())
+ {
+ distance = 0;
+ turn = Route::TurnItem();
+ }
+
+ if (m_current.m_ind == 0)
+ {
+ ASSERT_GREATER(m_turns[0].m_index, 0, ());
+ distance = m_segDistance[m_turns[0].m_index - 1];
+ turn = m_turns.empty() ? (Route::TurnItem()) : m_turns[0];
+ return;
+ }
+
+ TurnItem t;
+ t.m_index = m_current.m_ind;
+ auto it = upper_bound(m_turns.begin(), m_turns.end(), t,
+ [](TurnItem const & v, TurnItem const & item)
+ {
+ return v.m_index < item.m_index;
+ });
+
+ ASSERT_GREATER_OR_EQUAL((*it).m_index - 1, 0, ());
+
+ size_t const segIdx = (*it).m_index - 1;
+ turn = (*it);
+ distance = m_segDistance[segIdx] - GetCurrentDistanceFromBegin();
+}
+
bool Route::MoveIterator(location::GpsInfo const & info) const
{
double predictDistance = -1.0;
diff --git a/routing/route.hpp b/routing/route.hpp
index cbdaa1e11e..04d6f3e3fd 100644
--- a/routing/route.hpp
+++ b/routing/route.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "turns.hpp"
+
#include "../geometry/polyline2d.hpp"
#include "../std/vector.hpp"
@@ -15,30 +17,27 @@ class Route
{
public:
- enum TurnInstruction
+ struct TurnItem
{
- NoTurn = 0,
- GoStraight,
- TurnRight,
- TurnSharpRight,
- TurnSlightRight,
- TurnLeft,
- TurnSharpLeft,
- TurnSlightLeft,
- UTurn,
- HeadOn,
- EnterRoundAbout,
- LeaveRoundAbout,
- StayOnRoundAbout,
- StartAtEndOfStreet,
- ReachedYourDestination,
- EnterAgainstAllowedDirection,
- LeaveAgainstAllowedDirection
+ TurnItem()
+ : m_index(std::numeric_limits<uint32_t>::max())
+ , m_turn(turns::NoTurn), m_exitNum(0)
+ {
+ }
+
+ TurnItem(uint32_t idx, turns::TurnDirection t)
+ : m_index(idx), m_turn(t), m_exitNum(0)
+ {
+ }
+
+ uint32_t m_index; // number of point on polyline (number of segment + 1)
+ turns::TurnDirection m_turn;
+ uint32_t m_exitNum; // number of exit on roundabout
+ string m_srcName;
+ string m_trgName;
};
- // first value of piar is an number of point in polyline (number of segment + 1)
- typedef pair<uint32_t, TurnInstruction> TurnItemT;
- typedef vector<TurnItemT> TurnsT;
+ typedef vector<TurnItem> TurnsT;
explicit Route(string const & router) : m_router(router) {}
@@ -88,6 +87,8 @@ public:
double GetCurrentDistanceToEnd() const;
//@}
+ void GetTurn(double & distance, Route::TurnItem & turn) const;
+
/// @return true If position was updated successfully (projection within gps error radius).
bool MoveIterator(location::GpsInfo const & info) const;
diff --git a/routing/routing.pro b/routing/routing.pro
index cc381f6871..19beb97e0e 100644
--- a/routing/routing.pro
+++ b/routing/routing.pro
@@ -12,6 +12,7 @@ INCLUDEPATH += $$ROOT_DIR/3party/jansson/src \
$$ROOT_DIR/3party/osrm/osrm-backend/Include
SOURCES += \
+ turns.cpp \
route.cpp \
osrm_router.cpp \
osrm_online_router.cpp \
@@ -20,6 +21,7 @@ SOURCES += \
../3party/succinct/rs_bit_vector.cpp \
HEADERS += \
+ turns.hpp \
route.hpp \
router.hpp \
osrm_router.hpp \
diff --git a/routing/turns.cpp b/routing/turns.cpp
new file mode 100644
index 0000000000..289f3f5703
--- /dev/null
+++ b/routing/turns.cpp
@@ -0,0 +1,59 @@
+#include "turns.hpp"
+
+
+namespace routing
+{
+
+namespace turns
+{
+
+
+string turnStrings[] = {
+ "NoTurn",
+ "GoStraight",
+ "TurnRight",
+ "TurnSharpRight",
+ "TurnSlightRight",
+ "TurnLeft",
+ "TurnSharpLeft",
+ "TurnSlightLeft",
+ "UTurn",
+
+ "TakeTheExit",
+
+ "EnterRoundAbout",
+ "LeaveRoundAbout",
+ "StayOnRoundAbout",
+
+ "StartAtEndOfStreet",
+ "ReachedYourDestination"
+};
+
+string const & GetTurnString(TurnDirection turn)
+{
+ return turnStrings[turn];
+}
+
+bool IsLeftTurn(TurnDirection t)
+{
+ return (t >= turns::TurnLeft && t <= turns::TurnSlightLeft);
+}
+
+bool IsRightTurn(TurnDirection t)
+{
+ return (t >= turns::TurnRight && t <= turns::TurnSlightRight);
+}
+
+bool IsLeftOrRightTurn(TurnDirection t)
+{
+ return IsLeftTurn(t) || IsRightTurn(t);
+}
+
+bool IsStayOnRoad(TurnDirection t)
+{
+ return (t == turns::GoStraight || t == turns::StayOnRoundAbout);
+}
+
+}
+
+}
diff --git a/routing/turns.hpp b/routing/turns.hpp
new file mode 100644
index 0000000000..3099490252
--- /dev/null
+++ b/routing/turns.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "../std/string.hpp"
+
+namespace routing
+{
+
+namespace turns
+{
+
+// Do not change the order of right and left turns
+// TurnRight(TurnLeft) must have a minimal value
+// TurnSlightRight(TurnSlightLeft) must have a maximum value
+// to make check TurnRight <= turn <= TurnSlightRight work
+//
+// turnStrings array in cpp file must be synchronized with state of TurnDirection enum.
+enum TurnDirection
+{
+ NoTurn = 0,
+ GoStraight,
+
+ TurnRight,
+ TurnSharpRight,
+ TurnSlightRight,
+
+ TurnLeft,
+ TurnSharpLeft,
+ TurnSlightLeft,
+
+ UTurn,
+
+ TakeTheExit,
+
+ EnterRoundAbout,
+ LeaveRoundAbout,
+ StayOnRoundAbout,
+
+ StartAtEndOfStreet,
+ ReachedYourDestination,
+
+};
+
+string const & GetTurnString(TurnDirection turn);
+
+bool IsLeftTurn(TurnDirection t);
+bool IsRightTurn(TurnDirection t);
+bool IsLeftOrRightTurn(TurnDirection t);
+bool IsStayOnRoad(TurnDirection t);
+
+}
+}