diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 4d0cc6e8c6..b6d9442ad1 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -1315,39 +1315,45 @@ extern "C" jclass const klass = env->FindClass("com/mapswithme/maps/LocationState$RoutingInfo"); ASSERT(klass, (jni::DescribeException())); - static jmethodID const methodID = - env->GetMethodID(klass, "", - "(Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II[[B)V"); - ASSERT(methodID, (jni::DescribeException())); + static jmethodID const ctorRouteInfoID = env->GetMethodID(klass, "", + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II" + "[Lcom/mapswithme/maps/LocationState$SingleLaneInfo;)V"); + ASSERT(ctorRouteInfoID, (jni::DescribeException())); - vector> const & lanes = info.m_lanes; + vector const & lanes = info.m_lanes; jobjectArray jLanes = nullptr; if (!lanes.empty()) { - // A new java two-dimensional array for lane information is allocated here. - // Then it will be saved in com.mapswithme.maps.LocationState, and then removed by java - // GC. - jclass const myClassArray = env->FindClass("[B"); + // A new java array of SingleLaneInfo classes for lane information is allocated here. + // Then it will be saved in com.mapswithme.maps.LocationState, and then removed by java GC. + jclass const myClassArray = env->FindClass("com/mapswithme/maps/LocationState$SingleLaneInfo"); ASSERT(myClassArray, (jni::DescribeException())); size_t const lanesSize = lanes.size(); jLanes = env->NewObjectArray(lanesSize, myClassArray, nullptr); ASSERT(jLanes, (jni::DescribeException())); - jbyteArray jOneLane = nullptr; + static jmethodID const ctorSingleLaneInfoID = env->GetMethodID(myClassArray, "", "([BZ)V"); + ASSERT(ctorSingleLaneInfoID, (jni::DescribeException())); + + jbyteArray singleLane = nullptr; + jobject singleLaneInfo = nullptr; for (size_t j = 0; j < lanesSize; ++j) { - size_t const laneSize = lanes[j].size(); - jOneLane = env->NewByteArray(laneSize); - ASSERT(jOneLane, (jni::DescribeException())); - env->SetByteArrayRegion(jOneLane, 0, laneSize, lanes[j].data()); - env->SetObjectArrayElement(jLanes, j, jOneLane); - env->DeleteLocalRef(jOneLane); + size_t const laneSize = lanes[j].m_lane.size(); + singleLane = env->NewByteArray(laneSize); + ASSERT(singleLane, (jni::DescribeException())); + env->SetByteArrayRegion(singleLane, 0, laneSize, lanes[j].m_lane.data()); + singleLaneInfo = env->NewObject(myClassArray, ctorSingleLaneInfoID, singleLane, lanes[j].m_isActive); + ASSERT(singleLaneInfo, (jni::DescribeException())); + env->SetObjectArrayElement(jLanes, j, singleLaneInfo); + env->DeleteLocalRef(singleLaneInfo); + env->DeleteLocalRef(singleLane); } } jobject const result = env->NewObject( - klass, methodID, jni::ToJavaString(env, info.m_distToTarget), + klass, ctorRouteInfoID, jni::ToJavaString(env, info.m_distToTarget), jni::ToJavaString(env, info.m_targetUnitsSuffix), jni::ToJavaString(env, info.m_distToTurn), jni::ToJavaString(env, info.m_turnUnitsSuffix), jni::ToJavaString(env, info.m_targetName), diff --git a/android/src/com/mapswithme/maps/LocationState.java b/android/src/com/mapswithme/maps/LocationState.java index 7c2a3048ed..196155861c 100644 --- a/android/src/com/mapswithme/maps/LocationState.java +++ b/android/src/com/mapswithme/maps/LocationState.java @@ -26,6 +26,28 @@ public enum LocationState public native void invalidatePosition(); + public static class SingleLaneInfo + { + byte[] mLane; + boolean mIsActive; + + SingleLaneInfo(byte[] lane, boolean isActive) + { + mLane = lane; + mIsActive = isActive; + } + + private String DumpString() + { + final int startCapacity = 32; + StringBuilder sb = new StringBuilder(startCapacity); + sb.append("Is lane active? ").append(mIsActive).append(":"); + for (byte i : mLane) + sb.append(" ").append(i); + return sb.toString(); + } + } + public static class RoutingInfo { public String mDistToTarget; @@ -99,21 +121,19 @@ public enum LocationState SHARP_RIGHT }; - private void DumpLanes(byte[][] lanes) + private void DumpLanes(SingleLaneInfo[] lanes) { for (int j = 0; j < lanes.length; j++) { final int startCapacity = 32; StringBuilder sb = new StringBuilder(startCapacity); - sb.append("Lane number ").append(j).append(":"); - for (int i : lanes[j]) - sb.append(" ").append(i); + sb.append("Lane number ").append(j).append(":").append(lanes[j].DumpString()); Log.d("JNIARRAY", " " + sb.toString()); } } - public RoutingInfo(String distToTarget, String units, String distTurn, String turnSuffix, String targetName, int direction, int totalTime - , byte[][] lanes) + public RoutingInfo(String distToTarget, String units, String distTurn, String turnSuffix, + String targetName, int direction, int totalTime, SingleLaneInfo[] lanes) { // lanes is not equal to null if any lane information is available and should be displayed. // If so, lanes contains values of Lane enum for every lane. diff --git a/map/routing_session.cpp b/map/routing_session.cpp index 70208949b7..7167e392ca 100644 --- a/map/routing_session.cpp +++ b/map/routing_session.cpp @@ -173,19 +173,12 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const if (dist < kShowLanesDistInMeters) { - // There are two nested for-loops below. Outer one is for lanes and inner one (transform) is - // for each lane's directions. - // The meaning of the code below is info.m_lanes = turn.m_lanes; (vector> = - // vector>). - // The size of turn.m_lanes is relatively small. Less than 10 in most cases. + // 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(); for (size_t j = 0; j < turn.m_lanes.size(); ++j) { - vector lane; - lane.reserve(turn.m_lanes[j].size()); - transform(turn.m_lanes[j].begin(), turn.m_lanes[j].end(), back_inserter(lane), - [](routing::turns::LaneWay l) { return static_cast(l); }); - info.m_lanes.push_back(move(lane)); + info.m_lanes.push_back(move(FollowingInfo::SingleLaneInfoOuter(turn.m_lanes[j]))); } } else diff --git a/platform/location.hpp b/platform/location.hpp index a93aada073..2bb9921230 100644 --- a/platform/location.hpp +++ b/platform/location.hpp @@ -93,6 +93,19 @@ namespace location class FollowingInfo { public: + struct SingleLaneInfoOuter + { + vector m_lane; + bool m_isActive; + SingleLaneInfoOuter(routing::turns::SingleLaneInfo singleLaneInfo) : m_isActive(singleLaneInfo.m_isActive) + { + routing::turns::TSingleLane const & lane = singleLaneInfo.m_lane; + m_lane.resize(lane.size()); + transform(lane.begin(), lane.end(), m_lane.begin(), + [] (routing::turns::LaneWay l) { return static_cast(l); }); + } + }; + /// @name Formatted covered distance with measurement units suffix. //@{ string m_distToTarget; @@ -108,8 +121,7 @@ namespace location //@} int m_time; // m_lanes contains lane information on the edge before the turn. - // Template parameter int is used for passing the information to Android and iOS. - vector> m_lanes; + vector m_lanes; // The next street name string m_targetName; diff --git a/routing/route.cpp b/routing/route.cpp index 3caca68dfe..6d01607aeb 100644 --- a/routing/route.cpp +++ b/routing/route.cpp @@ -26,6 +26,7 @@ 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_exitNum = " << turnItem.m_exitNum << ", m_sourceName = " << turnItem.m_sourceName << ", m_targetName = " << turnItem.m_targetName diff --git a/routing/route.hpp b/routing/route.hpp index 502305526e..358925da40 100644 --- a/routing/route.hpp +++ b/routing/route.hpp @@ -40,7 +40,7 @@ struct TurnItem uint32_t m_index; // Index of point on polyline (number of segment + 1). turns::TurnDirection m_turn; - vector m_lanes; // Lane information on the edge before the turn. + vector m_lanes; // Lane information on the edge before the turn. uint32_t m_exitNum; // Number of exit on roundabout. string m_sourceName; string m_targetName; diff --git a/routing/routing_tests/turns_generator_test.cpp b/routing/routing_tests/turns_generator_test.cpp index 978c4f0a5d..7b7f888cd3 100644 --- a/routing/routing_tests/turns_generator_test.cpp +++ b/routing/routing_tests/turns_generator_test.cpp @@ -37,7 +37,7 @@ UNIT_TEST(TestParseSingleLane) { TSingleLane result; TEST(ParseSingleLane("through;right", ';', result), ()); - vector expected1 = {LaneWay::Through, LaneWay::Right}; + TSingleLane expected1 = {LaneWay::Through, LaneWay::Right}; TEST_EQUAL(result, expected1, ()); TEST(!ParseSingleLane("through;Right", ';', result), ()); @@ -55,7 +55,7 @@ UNIT_TEST(TestParseSingleLane) TEST_EQUAL(result.size(), 0, ()); TEST(ParseSingleLane("left;through", ';', result), ()); - vector expected2 = {LaneWay::Left, LaneWay::Through}; + TSingleLane expected2 = {LaneWay::Left, LaneWay::Through}; TEST_EQUAL(result, expected2, ()); TEST(ParseSingleLane("left", ';', result), ()); @@ -65,59 +65,59 @@ UNIT_TEST(TestParseSingleLane) UNIT_TEST(TestParseLanes) { - vector result; + vector result; TEST(ParseLanes("through|through|through|through;right", result), ()); TEST_EQUAL(result.size(), 4, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[3].size(), 2, ()); - TEST_EQUAL(result[0][0], LaneWay::Through, ()); - TEST_EQUAL(result[3][0], LaneWay::Through, ()); - TEST_EQUAL(result[3][1], LaneWay::Right, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[3].m_lane.size(), 2, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[3].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[3].m_lane[1], LaneWay::Right, ()); TEST(ParseLanes("left|left;through|through|through", result), ()); TEST_EQUAL(result.size(), 4, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[1].size(), 2, ()); - TEST_EQUAL(result[3].size(), 1, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[1][0], LaneWay::Left, ()); - TEST_EQUAL(result[1][1], LaneWay::Through, ()); - TEST_EQUAL(result[3][0], LaneWay::Through, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[1].m_lane.size(), 2, ()); + TEST_EQUAL(result[3].m_lane.size(), 1, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[1].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[1].m_lane[1], LaneWay::Through, ()); + TEST_EQUAL(result[3].m_lane[0], LaneWay::Through, ()); TEST(ParseLanes("left|through|through", result), ()); TEST_EQUAL(result.size(), 3, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[1].size(), 1, ()); - TEST_EQUAL(result[2].size(), 1, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[1][0], LaneWay::Through, ()); - TEST_EQUAL(result[2][0], LaneWay::Through, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[1].m_lane.size(), 1, ()); + TEST_EQUAL(result[2].m_lane.size(), 1, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[1].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[2].m_lane[0], LaneWay::Through, ()); TEST(ParseLanes("left|le ft| through|through | right", result), ()); TEST_EQUAL(result.size(), 5, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[4].size(), 1, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[1][0], LaneWay::Left, ()); - TEST_EQUAL(result[2][0], LaneWay::Through, ()); - TEST_EQUAL(result[3][0], LaneWay::Through, ()); - TEST_EQUAL(result[4][0], LaneWay::Right, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[4].m_lane.size(), 1, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[1].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[2].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[3].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[4].m_lane[0], LaneWay::Right, ()); TEST(ParseLanes("left|Left|through|througH|right", result), ()); TEST_EQUAL(result.size(), 5, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[4].size(), 1, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[4][0], LaneWay::Right, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[4].m_lane.size(), 1, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[4].m_lane[0], LaneWay::Right, ()); TEST(ParseLanes("left|Left|through|througH|through;right;sharp_rIght", result), ()); TEST_EQUAL(result.size(), 5, ()); - TEST_EQUAL(result[0].size(), 1, ()); - TEST_EQUAL(result[4].size(), 3, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[4][0], LaneWay::Through, ()); - TEST_EQUAL(result[4][1], LaneWay::Right, ()); - TEST_EQUAL(result[4][2], LaneWay::SharpRight, ()); + TEST_EQUAL(result[0].m_lane.size(), 1, ()); + TEST_EQUAL(result[4].m_lane.size(), 3, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[4].m_lane[0], LaneWay::Through, ()); + TEST_EQUAL(result[4].m_lane[1], LaneWay::Right, ()); + TEST_EQUAL(result[4].m_lane[2], LaneWay::SharpRight, ()); TEST(!ParseLanes("left|Leftt|through|througH|right", result), ()); TEST_EQUAL(result.size(), 0, ()); @@ -130,8 +130,8 @@ UNIT_TEST(TestParseLanes) TEST(ParseLanes("left |Left|through|througH|right", result), ()); TEST_EQUAL(result.size(), 5, ()); - TEST_EQUAL(result[0][0], LaneWay::Left, ()); - TEST_EQUAL(result[1][0], LaneWay::Left, ()); + TEST_EQUAL(result[0].m_lane[0], LaneWay::Left, ()); + TEST_EQUAL(result[1].m_lane[0], LaneWay::Left, ()); } UNIT_TEST(TestFixupTurns) @@ -270,4 +270,60 @@ UNIT_TEST(TestCalculateTurnGeometry) turnsGeom3[0].m_points[3]) - kSquareSideMeters), kErrorMeters, ()); } + +UNIT_TEST(TestIsLaneWayConformedTurnDirection) +{ + TEST(IsLaneWayConformedTurnDirection(LaneWay::Left, TurnDirection::TurnLeft), ()); + TEST(IsLaneWayConformedTurnDirection(LaneWay::Right, TurnDirection::TurnRight), ()); + TEST(IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, TurnDirection::TurnSlightLeft), ()); + TEST(IsLaneWayConformedTurnDirection(LaneWay::SharpRight, TurnDirection::TurnSharpRight), ()); + TEST(IsLaneWayConformedTurnDirection(LaneWay::Reverse, TurnDirection::UTurn), ()); + TEST(IsLaneWayConformedTurnDirection(LaneWay::Through, TurnDirection::GoStraight), ()); + + TEST(!IsLaneWayConformedTurnDirection(LaneWay::Left, TurnDirection::TurnSlightLeft), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::Right, TurnDirection::TurnSharpRight), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::SlightLeft, TurnDirection::GoStraight), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::SharpRight, TurnDirection::NoTurn), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::Reverse, TurnDirection::TurnLeft), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::None, TurnDirection::ReachedYourDestination), ()); } + +UNIT_TEST(TestIsLaneWayConformedTurnDirectionApproximately) +{ + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, TurnDirection::TurnSharpLeft), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Left, TurnDirection::TurnSlightLeft), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, TurnDirection::TurnSharpRight), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Right, TurnDirection::TurnRight), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::Reverse, TurnDirection::UTurn), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightLeft, TurnDirection::GoStraight), ()); + TEST(IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight, TurnDirection::GoStraight), ()); + + TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpLeft, TurnDirection::UTurn), ()); + TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SharpRight, TurnDirection::UTurn), ()); + TEST(!IsLaneWayConformedTurnDirection(LaneWay::Through, TurnDirection::ReachedYourDestination), ()); + TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::Through, TurnDirection::TurnRight), ()); + TEST(!IsLaneWayConformedTurnDirectionApproximately(LaneWay::SlightRight, TurnDirection::TurnSharpLeft), ()); +} + +UNIT_TEST(TestAddingActiveLaneInformation) +{ + Route::TurnsT turns = {{0, TurnDirection::GoStraight}, + {1, TurnDirection::TurnLeft}, + {2, TurnDirection::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::Through}); + + AddingActiveLaneInformation(turns); + + TEST(turns[0].m_lanes[0].m_isActive, ()); + TEST(!turns[0].m_lanes[1].m_isActive, ()); + + TEST(turns[1].m_lanes[0].m_isActive, ()); + TEST(!turns[1].m_lanes[1].m_isActive, ()); + TEST(!turns[1].m_lanes[1].m_isActive, ()); +} +} // namespace diff --git a/routing/turns.cpp b/routing/turns.cpp index 86169485e1..e9684b75e4 100644 --- a/routing/turns.cpp +++ b/routing/turns.cpp @@ -1,5 +1,7 @@ #include "routing/turns.hpp" +#include "base/internal/message.hpp" + #include "std/array.hpp" @@ -55,6 +57,11 @@ bool TurnGeom::operator==(TurnGeom const & other) const && m_points == other.m_points; } +bool SingleLaneInfo::operator==(SingleLaneInfo const & other) const +{ + return m_lane == other.m_lane && m_isActive == other.m_isActive; +} + string const GetTurnString(TurnDirection turn) { stringstream out; @@ -95,6 +102,70 @@ bool IsGoStraightOrSlightTurn(TurnDirection t) return (t == TurnDirection::GoStraight || t == TurnDirection::TurnSlightLeft || t == TurnDirection::TurnSlightRight); } +bool IsLaneWayConformedTurnDirection(LaneWay l, TurnDirection t) +{ + switch(t) + { + case TurnDirection::NoTurn: + case TurnDirection::TakeTheExit: + case TurnDirection::EnterRoundAbout: + case TurnDirection::LeaveRoundAbout: + case TurnDirection::StayOnRoundAbout: + case TurnDirection::StartAtEndOfStreet: + case TurnDirection::ReachedYourDestination: + default: + return false; + case TurnDirection::GoStraight: + return l == LaneWay::Through; + case TurnDirection::TurnRight: + return l == LaneWay::Right; + case TurnDirection::TurnSharpRight: + return l == LaneWay::SharpRight; + case TurnDirection::TurnSlightRight: + return l == LaneWay::SlightRight; + case TurnDirection::TurnLeft: + return l == LaneWay::Left; + case TurnDirection::TurnSharpLeft: + return l == LaneWay::SharpLeft; + case TurnDirection::TurnSlightLeft: + return l == LaneWay::SlightLeft; + case TurnDirection::UTurn: + return l == LaneWay::Reverse; + } +} + +bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, TurnDirection t) +{ + switch(t) + { + case TurnDirection::NoTurn: + case TurnDirection::TakeTheExit: + case TurnDirection::EnterRoundAbout: + case TurnDirection::LeaveRoundAbout: + case TurnDirection::StayOnRoundAbout: + case TurnDirection::StartAtEndOfStreet: + case TurnDirection::ReachedYourDestination: + default: + return false; + case TurnDirection::GoStraight: + return l == LaneWay::Through || l == LaneWay::SlightRight || l == LaneWay::SlightLeft; + case TurnDirection::TurnRight: + return l == LaneWay::Right || l == LaneWay::SharpRight || l == LaneWay::SlightRight; + case TurnDirection::TurnSharpRight: + return l == LaneWay::SharpRight || l == LaneWay::Right; + case TurnDirection::TurnSlightRight: + return l == LaneWay::SlightRight || l == LaneWay::Through || l == LaneWay::Right; + case TurnDirection::TurnLeft: + return l == LaneWay::Left || l == LaneWay::SlightLeft || l == LaneWay::SharpLeft; + case TurnDirection::TurnSharpLeft: + return l == LaneWay::SharpLeft || l == LaneWay::Left; + case TurnDirection::TurnSlightLeft: + return l == LaneWay::SlightLeft || l == LaneWay::Through || l == LaneWay::Left; + case TurnDirection::UTurn: + return l == LaneWay::Reverse; + } +} + void SplitLanes(string const & lanesString, char delimiter, vector & lanes) { lanes.clear(); @@ -125,7 +196,7 @@ bool ParseSingleLane(string const & laneString, char delimiter, TSingleLane & la return true; } -bool ParseLanes(string lanesString, vector & lanes) +bool ParseLanes(string lanesString, vector & lanes) { if (lanesString.empty()) return false; @@ -135,11 +206,11 @@ bool ParseLanes(string lanesString, vector & lanes) lanesString.end()); vector SplitLanesStrings; - TSingleLane lane; + SingleLaneInfo lane; SplitLanes(lanesString, '|', SplitLanesStrings); for (string const & s : SplitLanesStrings) { - if (!ParseSingleLane(s, ';', lane)) + if (!ParseSingleLane(s, ';', lane.m_lane)) { lanes.clear(); return false; @@ -179,5 +250,13 @@ string DebugPrint(TurnDirection const turn) return out.str(); } +string DebugPrint(SingleLaneInfo const & singleLaneInfo) +{ + stringstream out; + out << "[ m_isActive == " << singleLaneInfo.m_isActive << ". m_lane == " + << ::DebugPrint(singleLaneInfo.m_lane) << " ]" << endl; + return out.str(); +} + } } diff --git a/routing/turns.hpp b/routing/turns.hpp index 3b5b711f51..8ff71486d0 100644 --- a/routing/turns.hpp +++ b/routing/turns.hpp @@ -1,5 +1,6 @@ #pragma once +#include "std/initializer_list.hpp" #include "std/iostream.hpp" #include "std/string.hpp" #include "std/vector.hpp" @@ -88,6 +89,18 @@ string DebugPrint(TurnGeom const & turnGeom); typedef vector TurnsGeomT; typedef vector TSingleLane; +struct SingleLaneInfo +{ + SingleLaneInfo(initializer_list const & l = {}) : m_lane(l), m_isActive(false) {} + + TSingleLane m_lane; + bool m_isActive; + + bool operator==(SingleLaneInfo const & other) const; +}; + +string DebugPrint(SingleLaneInfo const & singleLaneInfo); + string const GetTurnString(TurnDirection turn); bool IsLeftTurn(TurnDirection t); @@ -96,6 +109,9 @@ bool IsLeftOrRightTurn(TurnDirection t); bool IsStayOnRoad(TurnDirection t); bool IsGoStraightOrSlightTurn(TurnDirection t); +bool IsLaneWayConformedTurnDirection(LaneWay l, TurnDirection t); +bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, TurnDirection t); + /*! * \brief Parse lane information which comes from @lanesString * \param lanesString lane information. Example through|through|through|through;right @@ -104,7 +120,7 @@ bool IsGoStraightOrSlightTurn(TurnDirection t); * 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(string lanesString, vector & lanes); +bool ParseLanes(string lanesString, vector & lanes); void SplitLanes(string const & lanesString, char delimiter, vector & lanes); bool ParseSingleLane(string const & laneString, char delimiter, TSingleLane & lane); diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index b056407c62..d69d1a9e1c 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -8,6 +8,31 @@ #include "std/numeric.hpp" #include "std/string.hpp" +namespace +{ +using namespace routing::turns; + +bool FixupLaneSet(TurnDirection turn, vector & lanes, function checker) +{ + bool isLaneConformed = false; + // There are two hidden nested loops below (transform and find_if). + // But the number of calls of the body of inner one (lambda in find_if) is relatively small. + // Less than 10 in most cases. + transform(lanes.begin(), lanes.end(), lanes.begin(), + [&isLaneConformed, turn, checker] (SingleLaneInfo & singleLane) + { + if (find_if(singleLane.m_lane.cbegin(), singleLane.m_lane.cend(), + [turn, checker] (LaneWay l) { return checker(l, turn); }) != singleLane.m_lane.cend()) + { + singleLane.m_isActive = true; + isLaneConformed = true; + } + return singleLane; + }); + return isLaneConformed; +} +} // namespace + namespace routing { namespace turns @@ -21,13 +46,13 @@ OsrmMappingTypes::FtSeg GetSegment(PathData const & node, RoutingMapping const & return seg; } -vector GetLanesInfo(PathData const & node, +vector GetLanesInfo(PathData const & node, RoutingMapping const & routingMapping, TGetIndexFunction GetIndex, Index const & index) { // seg1 is the last segment before a point of bifurcation (before turn) OsrmMappingTypes::FtSeg const seg1 = GetSegment(node, routingMapping, GetIndex); - vector lanes; + vector lanes; if (seg1.IsValid()) { FeatureType ft1; @@ -38,7 +63,7 @@ vector GetLanesInfo(PathData const & node, if (ftypes::IsOneWayChecker::Instance()(ft1)) { string const turnLanes = ft1.GetMetadata().Get(feature::FeatureMetadata::FMD_TURN_LANES); - routing::turns::ParseLanes(turnLanes, lanes); + ParseLanes(turnLanes, lanes); return lanes; } // two way roads @@ -47,20 +72,20 @@ vector GetLanesInfo(PathData const & node, // forward direction string const turnLanesForward = ft1.GetMetadata().Get(feature::FeatureMetadata::FMD_TURN_LANES_FORWARD); - routing::turns::ParseLanes(turnLanesForward, lanes); + ParseLanes(turnLanesForward, lanes); return lanes; } // backward direction string const turnLanesBackward = ft1.GetMetadata().Get(feature::FeatureMetadata::FMD_TURN_LANES_BACKWARD); - routing::turns::ParseLanes(turnLanesBackward, lanes); + ParseLanes(turnLanesBackward, lanes); return lanes; } return lanes; } void CalculateTurnGeometry(vector const & points, Route::TurnsT const & turnsDir, - turns::TurnsGeomT & turnsGeom) + TurnsGeomT & turnsGeom) { size_t const kNumPoints = points.size(); /// "Pivot point" is a point of bifurcation (a point of a turn). @@ -113,24 +138,24 @@ void FixupTurns(vector const & points, Route::TurnsT & turnsDir) for (size_t idx = 0; idx < turnsDir.size(); ) { TurnItem & t = turnsDir[idx]; - if (roundabout && t.m_turn != turns::TurnDirection::StayOnRoundAbout - && t.m_turn != turns::TurnDirection::LeaveRoundAbout) + if (roundabout && t.m_turn != TurnDirection::StayOnRoundAbout + && t.m_turn != TurnDirection::LeaveRoundAbout) { exitNum = 0; roundabout = nullptr; } - else if (t.m_turn == turns::TurnDirection::EnterRoundAbout) + else if (t.m_turn == TurnDirection::EnterRoundAbout) { ASSERT(!roundabout, ()); roundabout = &t; } - else if (t.m_turn == turns::TurnDirection::StayOnRoundAbout) + else if (t.m_turn == TurnDirection::StayOnRoundAbout) { ++exitNum; turnsDir.erase(turnsDir.begin() + idx); continue; } - else if (roundabout && t.m_turn == turns::TurnDirection::LeaveRoundAbout) + else if (roundabout && t.m_turn == TurnDirection::LeaveRoundAbout) { roundabout->m_exitNum = exitNum + 1; roundabout = nullptr; @@ -142,8 +167,8 @@ void FixupTurns(vector const & points, Route::TurnsT & turnsDir) // means the distance in meters between the former turn (idx - 1) // and the current turn (idx). if (idx > 0 && - turns::IsStayOnRoad(turnsDir[idx - 1].m_turn) && - turns::IsLeftOrRightTurn(turnsDir[idx].m_turn) && + IsStayOnRoad(turnsDir[idx - 1].m_turn) && + IsLeftOrRightTurn(turnsDir[idx].m_turn) && routeDistanceMeters(turnsDir[idx - 1].m_index, turnsDir[idx].m_index) < kMergeDistMeters) { turnsDir.erase(turnsDir.begin() + idx - 1); @@ -151,7 +176,7 @@ void FixupTurns(vector const & points, Route::TurnsT & turnsDir) } if (!t.m_keepAnyway - && turns::IsGoStraightOrSlightTurn(t.m_turn) + && IsGoStraightOrSlightTurn(t.m_turn) && !t.m_sourceName.empty() && strings::AlmostEqual(t.m_sourceName, t.m_targetName, 2 /* mismatched symbols count */)) { @@ -161,7 +186,23 @@ void FixupTurns(vector const & points, Route::TurnsT & turnsDir) ++idx; } + AddingActiveLaneInformation(turnsDir); return; } + +void AddingActiveLaneInformation(Route::TurnsT & turnsDir) +{ + for (auto & t : turnsDir) + { + vector & lanes = t.m_lanes; + if (lanes.empty()) + continue; + TurnDirection const turn = t.m_turn; + if (FixupLaneSet(turn, lanes, IsLaneWayConformedTurnDirection)) + continue; + FixupLaneSet(turn, lanes, IsLaneWayConformedTurnDirectionApproximately); + } +} + } } diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index c4e3099514..c461911908 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -22,14 +22,16 @@ typedef function)> TGetIndexFunction; OsrmMappingTypes::FtSeg GetSegment(PathData const & node, RoutingMapping const & routingMapping, TGetIndexFunction GetIndex); -vector GetLanesInfo(PathData const & node, +vector GetLanesInfo(PathData const & node, RoutingMapping const & routingMapping, TGetIndexFunction GetIndex, Index const & index); /// CalculateTurnGeometry calculates geometry for all the turns. That means that for every turn /// CalculateTurnGeometry calculates a sequence of points which will be used /// for displaying arrows on the route. void CalculateTurnGeometry(vector const & points, Route::TurnsT const & turnsDir, - turns::TurnsGeomT & turnsGeom); + TurnsGeomT & turnsGeom); +// Selects lanes which are recommended for the end user. +void AddingActiveLaneInformation(Route::TurnsT & turnsDir); void FixupTurns(vector const & points, Route::TurnsT & turnsDir); } }