[routing] New API for lanes

Signed-off-by: Andrei Shkrob <github@shkrob.dev>
This commit is contained in:
Andrei Shkrob
2025-07-29 22:54:24 +02:00
committed by x7z4w
parent 93293e7b31
commit c014e224b2
36 changed files with 1047 additions and 634 deletions

View File

@@ -31,18 +31,19 @@ public final class RoutingHelpers
@NonNull @NonNull
public static LaneDirection createLaneDirection(@NonNull LaneWay laneWay, boolean isRecommended) public static LaneDirection createLaneDirection(@NonNull LaneWay laneWay, boolean isRecommended)
{ {
int shape = LaneDirection.SHAPE_UNKNOWN; @LaneDirection.Shape
shape = switch (laneWay) final int shape = switch (laneWay)
{ {
case REVERSE -> LaneDirection.SHAPE_U_TURN_LEFT; case ReverseLeft -> LaneDirection.SHAPE_U_TURN_LEFT;
case SHARP_LEFT -> LaneDirection.SHAPE_SHARP_LEFT; case SharpLeft -> LaneDirection.SHAPE_SHARP_LEFT;
case LEFT -> LaneDirection.SHAPE_NORMAL_LEFT; case Left -> LaneDirection.SHAPE_NORMAL_LEFT;
case SLIGHT_LEFT, MERGE_TO_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT; case MergeToLeft, SlightLeft -> LaneDirection.SHAPE_SLIGHT_LEFT;
case SLIGHT_RIGHT, MERGE_TO_RIGHT -> LaneDirection.SHAPE_SLIGHT_RIGHT; case Through -> LaneDirection.SHAPE_STRAIGHT;
case THROUGH -> LaneDirection.SHAPE_STRAIGHT; case SlightRight, MergeToRight -> LaneDirection.SHAPE_SLIGHT_RIGHT;
case RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT; case Right -> LaneDirection.SHAPE_NORMAL_RIGHT;
case SHARP_RIGHT -> LaneDirection.SHAPE_SHARP_RIGHT; case SharpRight -> LaneDirection.SHAPE_SHARP_RIGHT;
default -> shape; case ReverseRight -> LaneDirection.SHAPE_U_TURN_RIGHT;
default -> LaneDirection.SHAPE_UNKNOWN;
}; };
return LaneDirection.create(shape, isRecommended); return LaneDirection.create(shape, isRecommended);

View File

@@ -8,14 +8,15 @@ import androidx.car.app.CarContext;
import androidx.car.app.model.CarIcon; import androidx.car.app.model.CarIcon;
import androidx.car.app.navigation.model.Destination; import androidx.car.app.navigation.model.Destination;
import androidx.car.app.navigation.model.Lane; import androidx.car.app.navigation.model.Lane;
import androidx.car.app.navigation.model.LaneDirection;
import androidx.car.app.navigation.model.Step; import androidx.car.app.navigation.model.Step;
import androidx.car.app.navigation.model.TravelEstimate; import androidx.car.app.navigation.model.TravelEstimate;
import androidx.car.app.navigation.model.Trip; import androidx.car.app.navigation.model.Trip;
import androidx.core.graphics.drawable.IconCompat; import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.sdk.bookmarks.data.MapObject; import app.organicmaps.sdk.bookmarks.data.MapObject;
import app.organicmaps.sdk.routing.LaneInfo;
import app.organicmaps.sdk.routing.LaneWay; import app.organicmaps.sdk.routing.LaneWay;
import app.organicmaps.sdk.routing.RoutingInfo; import app.organicmaps.sdk.routing.RoutingInfo;
import app.organicmaps.sdk.routing.SingleLaneInfo;
import app.organicmaps.sdk.util.Distance; import app.organicmaps.sdk.util.Distance;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.widget.LanesDrawable; import app.organicmaps.widget.LanesDrawable;
@@ -69,11 +70,12 @@ public final class RoutingUtils
builder.setManeuver(RoutingHelpers.createManeuver(context, info.carDirection, info.exitNum)); builder.setManeuver(RoutingHelpers.createManeuver(context, info.carDirection, info.exitNum));
if (info.lanes != null) if (info.lanes != null)
{ {
for (final SingleLaneInfo laneInfo : info.lanes) for (final LaneInfo laneInfo : info.lanes)
{ {
final Lane.Builder laneBuilder = new Lane.Builder(); final Lane.Builder laneBuilder = new Lane.Builder();
for (final LaneWay laneWay : laneInfo.mLane) for (final LaneWay laneWay : laneInfo.mLaneWays)
laneBuilder.addDirection(RoutingHelpers.createLaneDirection(laneWay, laneInfo.mIsActive)); laneBuilder.addDirection(
RoutingHelpers.createLaneDirection(laneWay, /* isRecommended */ laneWay == laneInfo.mActiveLaneWay));
builder.addLane(laneBuilder.build()); builder.addLane(laneBuilder.build());
} }
final LanesDrawable lanesDrawable = new LanesDrawable(context, info.lanes); final LanesDrawable lanesDrawable = new LanesDrawable(context, info.lanes);

View File

@@ -8,12 +8,14 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes; import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.routing.SingleLaneInfo; import app.organicmaps.sdk.routing.LaneInfo;
import app.organicmaps.sdk.routing.LaneWay;
import java.util.Objects; import java.util.Objects;
public class LanesDrawable extends Drawable public class LanesDrawable extends Drawable
@@ -41,16 +43,19 @@ public class LanesDrawable extends Drawable
{ {
private final Drawable mDrawable; private final Drawable mDrawable;
private LaneDrawable(@NonNull final Context context, @NonNull SingleLaneInfo laneInfo, int horizontalOffset, private LaneDrawable(@NonNull final Context context, @NonNull LaneInfo laneInfo, int horizontalOffset,
TintColorInfo colorInfo) TintColorInfo colorInfo)
{ {
mDrawable = Objects.requireNonNull(AppCompatResources.getDrawable(context, laneInfo.mLane[0].mTurnRes)); final boolean isActive = laneInfo.mActiveLaneWay != LaneWay.None;
@DrawableRes
final int turnRes = isActive ? laneInfo.mActiveLaneWay.mTurnRes : laneInfo.mLaneWays[0].mTurnRes;
mDrawable = Objects.requireNonNull(AppCompatResources.getDrawable(context, turnRes));
final int width = mDrawable.getIntrinsicWidth(); final int width = mDrawable.getIntrinsicWidth();
final int height = mDrawable.getIntrinsicHeight(); final int height = mDrawable.getIntrinsicHeight();
mDrawable.setBounds(horizontalOffset, 0, horizontalOffset + width, height); mDrawable.setBounds(horizontalOffset, 0, horizontalOffset + width, height);
mDrawable.setTint(laneInfo.mIsActive ? colorInfo.mActiveLaneTint : colorInfo.mInactiveLaneTint); mDrawable.setTint(isActive ? colorInfo.mActiveLaneTint : colorInfo.mInactiveLaneTint);
} }
private void draw(@NonNull final Canvas canvas) private void draw(@NonNull final Canvas canvas)
@@ -65,14 +70,14 @@ public class LanesDrawable extends Drawable
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
public LanesDrawable(@NonNull final Context context, @NonNull SingleLaneInfo[] lanes) public LanesDrawable(@NonNull final Context context, @NonNull LaneInfo[] lanes)
{ {
final TintColorInfo tintColorInfo = new TintColorInfo(ContextCompat.getColor(context, ACTIVE_LANE_TINT_RES), final TintColorInfo tintColorInfo = new TintColorInfo(ContextCompat.getColor(context, ACTIVE_LANE_TINT_RES),
ContextCompat.getColor(context, INACTIVE_LANE_TINT_RES)); ContextCompat.getColor(context, INACTIVE_LANE_TINT_RES));
mLanes = createLaneDrawables(context, lanes, tintColorInfo); mLanes = createLaneDrawables(context, lanes, tintColorInfo);
} }
public LanesDrawable(@NonNull final Context context, @NonNull SingleLaneInfo[] lanes, @ColorInt int activeLaneTint, public LanesDrawable(@NonNull final Context context, @NonNull LaneInfo[] lanes, @ColorInt int activeLaneTint,
@ColorInt int inactiveLaneTint) @ColorInt int inactiveLaneTint)
{ {
final TintColorInfo tintColorInfo = new TintColorInfo(activeLaneTint, inactiveLaneTint); final TintColorInfo tintColorInfo = new TintColorInfo(activeLaneTint, inactiveLaneTint);
@@ -143,7 +148,7 @@ public class LanesDrawable extends Drawable
} }
@NonNull @NonNull
private LaneDrawable[] createLaneDrawables(@NonNull Context context, @NonNull SingleLaneInfo[] lanes, private LaneDrawable[] createLaneDrawables(@NonNull Context context, @NonNull LaneInfo[] lanes,
@NonNull TintColorInfo tintColorInfo) @NonNull TintColorInfo tintColorInfo)
{ {
assert lanes.length > 0; assert lanes.length > 0;

View File

@@ -16,7 +16,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StyleableRes; import androidx.annotation.StyleableRes;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.routing.SingleLaneInfo; import app.organicmaps.sdk.routing.LaneInfo;
import app.organicmaps.sdk.routing.LaneWay;
public class LanesView extends View public class LanesView extends View
{ {
@@ -77,7 +78,7 @@ public class LanesView extends View
mBackgroundPaint.setColor(backgroundColor); mBackgroundPaint.setColor(backgroundColor);
} }
public void setLanes(@Nullable SingleLaneInfo[] lanes) public void setLanes(@Nullable LaneInfo[] lanes)
{ {
if (lanes == null || lanes.length == 0) if (lanes == null || lanes.length == 0)
mLanesDrawable = null; mLanesDrawable = null;
@@ -157,16 +158,16 @@ public class LanesView extends View
private void createLanesForEditMode(int lanesCount) private void createLanesForEditMode(int lanesCount)
{ {
final SingleLaneInfo[] lanes = new SingleLaneInfo[lanesCount]; final LaneInfo[] lanes = new LaneInfo[lanesCount];
lanes[0] = new SingleLaneInfo(new byte[] {1}, false); lanes[0] = new LaneInfo(new LaneWay[] {LaneWay.ReverseLeft, LaneWay.Left}, LaneWay.None);
if (lanes.length > 1) if (lanes.length > 1)
lanes[1] = new SingleLaneInfo(new byte[] {3}, false); lanes[1] = new LaneInfo(new LaneWay[] {LaneWay.SharpLeft, LaneWay.Left, LaneWay.Through}, LaneWay.None);
for (int i = 2; i <= lanes.length - 1; i++) for (int i = 2; i <= lanes.length - 1; i++)
lanes[i] = new SingleLaneInfo(new byte[] {0}, true); lanes[i] = new LaneInfo(new LaneWay[] {LaneWay.Through, LaneWay.Left}, LaneWay.Through);
if (lanes.length > 2) if (lanes.length > 2)
lanes[lanes.length - 2] = new SingleLaneInfo(new byte[] {8}, false); lanes[lanes.length - 2] = new LaneInfo(new LaneWay[] {LaneWay.SlightRight, LaneWay.Right}, LaneWay.SlightRight);
if (lanes.length > 3) if (lanes.length > 3)
lanes[lanes.length - 1] = new SingleLaneInfo(new byte[] {9}, false); lanes[lanes.length - 1] = new LaneInfo(new LaneWay[] {LaneWay.ReverseRight}, LaneWay.None);
setLanes(lanes); setLanes(lanes);
} }

View File

@@ -26,13 +26,14 @@ set(SRC
app/organicmaps/sdk/search/SearchEngine.cpp app/organicmaps/sdk/search/SearchEngine.cpp
app/organicmaps/sdk/search/SearchRecents.cpp app/organicmaps/sdk/search/SearchRecents.cpp
app/organicmaps/sdk/routing/JunctionInfo.hpp app/organicmaps/sdk/routing/JunctionInfo.hpp
app/organicmaps/sdk/routing/LaneInfo.cpp
app/organicmaps/sdk/routing/LaneInfo.hpp
app/organicmaps/sdk/routing/RouteMarkData.hpp app/organicmaps/sdk/routing/RouteMarkData.hpp
app/organicmaps/sdk/routing/RouteMarkType.hpp app/organicmaps/sdk/routing/RouteMarkType.hpp
app/organicmaps/sdk/routing/RoutePointInfo.hpp app/organicmaps/sdk/routing/RoutePointInfo.hpp
app/organicmaps/sdk/routing/RouteRecommendationType.hpp app/organicmaps/sdk/routing/RouteRecommendationType.hpp
app/organicmaps/sdk/routing/RoutingInfo.hpp app/organicmaps/sdk/routing/RoutingInfo.hpp
app/organicmaps/sdk/routing/RoutingOptions.cpp app/organicmaps/sdk/routing/RoutingOptions.cpp
app/organicmaps/sdk/routing/SingleLaneInfo.hpp
app/organicmaps/sdk/routing/TransitRouteInfo.hpp app/organicmaps/sdk/routing/TransitRouteInfo.hpp
app/organicmaps/sdk/routing/TransitStepInfo.hpp app/organicmaps/sdk/routing/TransitStepInfo.hpp
app/organicmaps/sdk/ChoosePositionMode.cpp app/organicmaps/sdk/ChoosePositionMode.cpp

View File

@@ -0,0 +1,50 @@
#include "LaneInfo.hpp"
#include "app/organicmaps/sdk/core/jni_helper.hpp"
#include <vector>
namespace
{
jobject ToJavaLaneWay(JNIEnv * env, routing::turns::lanes::LaneWay const & laneWay)
{
static jclass const laneWayClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/routing/LaneWay");
jfieldID fieldID =
env->GetStaticFieldID(laneWayClass, DebugPrint(laneWay).c_str(), "Lapp/organicmaps/sdk/routing/LaneWay;");
return env->GetStaticObjectField(laneWayClass, fieldID);
}
} // namespace
jobjectArray CreateLanesInfo(JNIEnv * env, routing::turns::lanes::LanesInfo const & lanes)
{
if (lanes.empty())
return nullptr;
static jclass const laneWayClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/routing/LaneWay");
static jclass const laneInfoClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/routing/LaneInfo");
auto const lanesSize = static_cast<jsize>(lanes.size());
jobjectArray jLanes = env->NewObjectArray(lanesSize, laneInfoClass, nullptr);
ASSERT(jLanes, (jni::DescribeException()));
// Java signature : LaneInfo(LaneWay[] laneWays, LaneWay activeLane)
static jmethodID const ctorLaneInfoID = jni::GetConstructorID(
env, laneInfoClass, "([Lapp/organicmaps/sdk/routing/LaneWay;Lapp/organicmaps/sdk/routing/LaneWay;)V");
for (jsize j = 0; j < lanesSize; ++j)
{
auto const laneWays = lanes[j].laneWays.GetActiveLaneWays();
auto const laneWaysSize = static_cast<jsize>(laneWays.size());
jni::TScopedLocalObjectArrayRef jLaneWays(env, env->NewObjectArray(laneWaysSize, laneWayClass, nullptr));
ASSERT(jLanes, (jni::DescribeException()));
for (jsize i = 0; i < laneWaysSize; ++i)
{
jni::TScopedLocalRef jLaneWay(env, ToJavaLaneWay(env, laneWays[i]));
env->SetObjectArrayElement(jLaneWays.get(), i, jLaneWay.get());
}
jni::TScopedLocalRef jLaneInfo(env, env->NewObject(laneInfoClass, ctorLaneInfoID, jLaneWays.get(),
ToJavaLaneWay(env, lanes[j].recommendedWay)));
ASSERT(jLaneInfo.get(), (jni::DescribeException()));
env->SetObjectArrayElement(jLanes, j, jLaneInfo.get());
}
return jLanes;
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <jni.h>
#include "routing/lanes/lane_info.hpp"
jobjectArray CreateLanesInfo(JNIEnv * env, routing::turns::lanes::LanesInfo const & lanes);

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "app/organicmaps/sdk/core/jni_helper.hpp" #include "app/organicmaps/sdk/core/jni_helper.hpp"
#include "app/organicmaps/sdk/routing/SingleLaneInfo.hpp" #include "app/organicmaps/sdk/routing/LaneInfo.hpp"
#include "app/organicmaps/sdk/util/Distance.hpp" #include "app/organicmaps/sdk/util/Distance.hpp"
#include "map/routing_manager.hpp" #include "map/routing_manager.hpp"
@@ -13,12 +13,13 @@ jobject CreateRoutingInfo(JNIEnv * env, routing::FollowingInfo const & info, Rou
// String currentStreet, String nextStreet, String nextNextStreet, // String currentStreet, String nextStreet, String nextNextStreet,
// double completionPercent, int vehicleTurnOrdinal, int // double completionPercent, int vehicleTurnOrdinal, int
// vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum, // vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum,
// int totalTime, SingleLaneInfo[] lanes) // int totalTime, LaneInfo[] lanes, double speedLimitMps, boolean speedLimitExceeded,
// boolean shouldPlayWarningSignal)
static jmethodID const ctorRouteInfoID = static jmethodID const ctorRouteInfoID =
jni::GetConstructorID(env, klass, jni::GetConstructorID(env, klass,
"(Lapp/organicmaps/sdk/util/Distance;Lapp/organicmaps/sdk/util/Distance;" "(Lapp/organicmaps/sdk/util/Distance;Lapp/organicmaps/sdk/util/Distance;"
"Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DIIIII" "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DIIIII"
"[Lapp/organicmaps/sdk/routing/SingleLaneInfo;DZZ)V"); "[Lapp/organicmaps/sdk/routing/LaneInfo;DZZ)V");
jobjectArray jLanes = CreateLanesInfo(env, info.m_lanes); jobjectArray jLanes = CreateLanesInfo(env, info.m_lanes);

View File

@@ -1,34 +0,0 @@
#pragma once
#include "app/organicmaps/sdk/core/jni_helper.hpp"
#include "routing/following_info.hpp"
#include <vector>
jobjectArray CreateLanesInfo(JNIEnv * env, std::vector<routing::FollowingInfo::SingleLaneInfoClient> const & lanes)
{
if (lanes.empty())
return nullptr;
static jclass const laneClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/routing/SingleLaneInfo");
auto const lanesSize = static_cast<jsize>(lanes.size());
jobjectArray jLanes = env->NewObjectArray(lanesSize, laneClass, nullptr);
ASSERT(jLanes, (jni::DescribeException()));
static jmethodID const ctorSingleLaneInfoID = jni::GetConstructorID(env, laneClass, "([BZ)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));
ASSERT(singleLaneInfo.get(), (jni::DescribeException()));
env->SetObjectArrayElement(jLanes, j, singleLaneInfo.get());
}
return jLanes;
}

View File

@@ -0,0 +1,27 @@
package app.organicmaps.sdk.routing;
import androidx.annotation.NonNull;
public final class LaneInfo
{
public final LaneWay[] mLaneWays;
public final LaneWay mActiveLaneWay;
public LaneInfo(@NonNull LaneWay[] laneWays, LaneWay activeLane)
{
mLaneWays = laneWays;
mActiveLaneWay = activeLane;
}
@NonNull
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("LaneInfo{activeLaneWay=").append(mActiveLaneWay.toString()).append(", laneWays=[");
for (LaneWay i : mLaneWays)
sb.append(" ").append(i);
sb.append("]}");
return sb.toString();
}
}

View File

@@ -3,25 +3,20 @@ package app.organicmaps.sdk.routing;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import app.organicmaps.sdk.R; import app.organicmaps.sdk.R;
/**
* IMPORTANT : Order of enum values MUST BE the same
* with native LaneWay enum (see routing/turns.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 public enum LaneWay
{ {
NONE(R.drawable.ic_turn_straight), None(R.drawable.ic_turn_straight),
REVERSE(R.drawable.ic_turn_uleft), ReverseLeft(R.drawable.ic_turn_uleft),
SHARP_LEFT(R.drawable.ic_turn_left_sharp), SharpLeft(R.drawable.ic_turn_left_sharp),
LEFT(R.drawable.ic_turn_left), Left(R.drawable.ic_turn_left),
SLIGHT_LEFT(R.drawable.ic_turn_left_slight), MergeToLeft(R.drawable.ic_turn_left_slight),
MERGE_TO_RIGHT(R.drawable.ic_turn_right_slight), SlightLeft(R.drawable.ic_turn_left_slight),
THROUGH(R.drawable.ic_turn_straight), Through(R.drawable.ic_turn_straight),
MERGE_TO_LEFT(R.drawable.ic_turn_left_slight), SlightRight(R.drawable.ic_turn_right_slight),
SLIGHT_RIGHT(R.drawable.ic_turn_right_slight), MergeToRight(R.drawable.ic_turn_right_slight),
RIGHT(R.drawable.ic_turn_right), Right(R.drawable.ic_turn_right),
SHARP_RIGHT(R.drawable.ic_turn_right_sharp); SharpRight(R.drawable.ic_turn_right_sharp),
ReverseRight(R.drawable.ic_turn_uright);
public final int mTurnRes; public final int mTurnRes;

View File

@@ -1,6 +1,7 @@
package app.organicmaps.sdk.routing; package app.organicmaps.sdk.routing;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import app.organicmaps.sdk.util.Distance; import app.organicmaps.sdk.util.Distance;
// Called from JNI. // Called from JNI.
@@ -25,7 +26,8 @@ public final class RoutingInfo
public final CarDirection carDirection; public final CarDirection carDirection;
public final CarDirection nextCarDirection; public final CarDirection nextCarDirection;
public final int exitNum; public final int exitNum;
public final SingleLaneInfo[] lanes; @Nullable
public final LaneInfo[] lanes;
// For pedestrian routing. // For pedestrian routing.
public final PedestrianTurnDirection pedestrianTurnDirection; public final PedestrianTurnDirection pedestrianTurnDirection;
// Current speed limit in meters per second. // Current speed limit in meters per second.
@@ -37,7 +39,7 @@ public final class RoutingInfo
private RoutingInfo(Distance distToTarget, Distance distToTurn, String currentStreet, String nextStreet, private RoutingInfo(Distance distToTarget, Distance distToTurn, String currentStreet, String nextStreet,
String nextNextStreet, double completionPercent, int vehicleTurnOrdinal, String nextNextStreet, double completionPercent, int vehicleTurnOrdinal,
int vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum, int totalTime, int vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum, int totalTime,
SingleLaneInfo[] lanes, double speedLimitMps, boolean speedLimitExceeded, @Nullable LaneInfo[] lanes, double speedLimitMps, boolean speedLimitExceeded,
boolean shouldPlayWarningSignal) boolean shouldPlayWarningSignal)
{ {
this.distToTarget = distToTarget; this.distToTarget = distToTarget;

View File

@@ -1,31 +0,0 @@
package app.organicmaps.sdk.routing;
import androidx.annotation.NonNull;
public final class SingleLaneInfo
{
public LaneWay[] mLane;
public boolean mIsActive;
public SingleLaneInfo(@NonNull byte[] laneOrdinals, boolean isActive)
{
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;
}
@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");
for (LaneWay i : mLane)
sb.append(" ").append(i.ordinal());
return sb.toString();
}
}

View File

@@ -16,6 +16,14 @@ set(SRC
base/routing_result.hpp base/routing_result.hpp
base/small_list.hpp base/small_list.hpp
base/small_list.cpp base/small_list.cpp
lanes/lane_info.cpp
lanes/lane_info.hpp
lanes/lane_way.cpp
lanes/lane_way.hpp
lanes/lanes_parser.cpp
lanes/lanes_parser.hpp
lanes/lanes_recommendation.cpp
lanes/lanes_recommendation.hpp
car_directions.cpp car_directions.cpp
car_directions.hpp car_directions.hpp
checkpoint_predictor.cpp checkpoint_predictor.cpp

View File

@@ -1,5 +1,6 @@
#include "routing/car_directions.hpp" #include "routing/car_directions.hpp"
#include "routing/lanes/lanes_recommendation.hpp"
#include "routing/turns.hpp" #include "routing/turns.hpp"
#include "routing/turns_generator.hpp" #include "routing/turns_generator.hpp"
#include "routing/turns_generator_utils.hpp" #include "routing/turns_generator_utils.hpp"
@@ -83,7 +84,7 @@ void FixupCarTurns(vector<RouteSegment> & routeSegments)
routeSegments[idx - 1].ClearTurn(); routeSegments[idx - 1].ClearTurn();
} }
} }
SelectRecommendedLanes(routeSegments); turns::lanes::SelectRecommendedLanes(routeSegments);
} }
void GetTurnDirectionBasic(IRoutingResult const & result, size_t const outgoingSegmentIndex, void GetTurnDirectionBasic(IRoutingResult const & result, size_t const outgoingSegmentIndex,
@@ -596,73 +597,4 @@ size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t const outgoingSeg
return 0; 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)
{
It const 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 } // namespace routing

View File

@@ -5,7 +5,6 @@
#include "routing/route.hpp" #include "routing/route.hpp"
#include "routing_common/num_mwm_id.hpp" #include "routing_common/num_mwm_id.hpp"
#include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -24,11 +23,6 @@ protected:
virtual void FixupTurns(std::vector<RouteSegment> & routeSegments); 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); void FixupCarTurns(std::vector<RouteSegment> & routeSegments);
/*! /*!

View File

@@ -2,6 +2,7 @@
#include "routing/data_source.hpp" #include "routing/data_source.hpp"
#include "routing/fake_feature_ids.hpp" #include "routing/fake_feature_ids.hpp"
#include "routing/lanes/lanes_parser.hpp"
#include "routing/routing_helpers.hpp" #include "routing/routing_helpers.hpp"
#include "routing/turns.hpp" #include "routing/turns.hpp"
@@ -36,7 +37,7 @@ feature::Metadata::EType GetLanesMetadataTag(FeatureType & ft, bool isForward)
void LoadLanes(LoadedPathSegment & pathSegment, FeatureType & ft, bool isForward) void LoadLanes(LoadedPathSegment & pathSegment, FeatureType & ft, bool isForward)
{ {
auto tag = GetLanesMetadataTag(ft, isForward); auto tag = GetLanesMetadataTag(ft, isForward);
ParseLanes(std::string(ft.GetMetadata(tag)), pathSegment.m_lanes); pathSegment.m_lanes = lanes::ParseLanes(ft.GetMetadata(tag));
} }
} // namespace } // namespace

View File

@@ -1,11 +1,9 @@
#pragma once #pragma once
#include "geometry/latlon.hpp"
#include "platform/distance.hpp" #include "platform/distance.hpp"
#include "routing/lanes/lane_info.hpp"
#include "routing/turns.hpp" #include "routing/turns.hpp"
#include "routing/turns_sound_settings.hpp"
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
@@ -26,23 +24,6 @@ public:
, m_pedestrianTurn(turns::PedestrianDirection::None) , m_pedestrianTurn(turns::PedestrianDirection::None)
{} {}
// 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(); } bool IsValid() const { return m_distToTarget.IsValid(); }
/// @name Formatted covered distance. /// @name Formatted covered distance.
@@ -57,8 +38,8 @@ public:
uint32_t m_exitNum; uint32_t m_exitNum;
//@} //@}
int m_time; int m_time;
// m_lanes contains lane information on the edge before the turn. /// 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. // m_turnNotifications contains information about the next turn notifications.
// If there is nothing to pronounce m_turnNotifications is empty. // 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 // If there is something to pronounce the size of m_turnNotifications may be one or even more

View File

@@ -0,0 +1,24 @@
#include "lane_info.hpp"
#include <sstream>
namespace routing::turns::lanes
{
std::string DebugPrint(LaneInfo const & laneInfo)
{
std::stringstream out;
out << "LaneInfo{" << DebugPrint(laneInfo.laneWays) << ", recommendedWay: " << DebugPrint(laneInfo.recommendedWay)
<< "}";
return out.str();
}
std::string DebugPrint(LanesInfo const & lanesInfo)
{
std::stringstream out;
out << "LanesInfo[";
for (auto const & laneInfo : lanesInfo)
out << DebugPrint(laneInfo) << ", ";
out << "]";
return out.str();
}
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,23 @@
#pragma once
#include "routing/lanes/lane_way.hpp"
#include <vector>
namespace routing::turns::lanes
{
struct LaneInfo
{
LaneWays laneWays;
LaneWay recommendedWay = LaneWay::None;
bool operator==(LaneInfo const & rhs) const
{
return laneWays == rhs.laneWays && recommendedWay == rhs.recommendedWay;
}
};
using LanesInfo = std::vector<LaneInfo>;
std::string DebugPrint(LaneInfo const & laneInfo);
std::string DebugPrint(LanesInfo const & lanesInfo);
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,50 @@
#include "lane_way.hpp"
#include "base/assert.hpp"
namespace routing::turns::lanes
{
std::string DebugPrint(LaneWay const laneWay)
{
using enum LaneWay;
switch (laneWay)
{
case None: return "None";
case ReverseLeft: return "ReverseLeft";
case SharpLeft: return "SharpLeft";
case Left: return "Left";
case MergeToLeft: return "MergeToLeft";
case SlightLeft: return "SlightLeft";
case Through: return "Through";
case SlightRight: return "SlightRight";
case MergeToRight: return "MergeToRight";
case Right: return "Right";
case SharpRight: return "SharpRight";
case ReverseRight: return "ReverseRight";
case Count: return "Count";
default:
ASSERT_FAIL("Unsupported value: " + std::to_string(static_cast<std::uint8_t>(laneWay)));
return "Unsupported";
}
}
std::string DebugPrint(LaneWays const & laneWays)
{
std::stringstream out;
out << "LaneWays: [";
std::uint8_t const waysCount = laneWays.m_laneWays.count();
std::uint8_t waysPrinted = 0;
for (std::size_t i = 0; i < laneWays.m_laneWays.size(); ++i)
{
if (laneWays.m_laneWays.test(i))
{
out << DebugPrint(static_cast<LaneWay>(i));
if (waysPrinted < waysCount - 1)
out << ", ";
waysPrinted++;
}
}
out << "]";
return out.str();
}
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,84 @@
#pragma once
#include "base/assert.hpp"
#include <bitset>
#include <initializer_list>
#include <string>
namespace routing::turns::lanes
{
enum class LaneWay : std::uint8_t
{
None = 0,
ReverseLeft,
SharpLeft,
Left,
MergeToLeft,
SlightLeft,
Through,
SlightRight,
MergeToRight,
Right,
SharpRight,
ReverseRight,
Count
};
class LaneWays
{
using LaneWaysT = std::bitset<static_cast<std::uint8_t>(LaneWay::Count)>;
friend std::string DebugPrint(LaneWays const & laneWays);
public:
constexpr LaneWays() = default;
constexpr LaneWays(std::initializer_list<LaneWay> const laneWays)
{
for (auto const & laneWay : laneWays)
Add(laneWay);
}
constexpr bool operator==(LaneWays const & rhs) const { return m_laneWays == rhs.m_laneWays; }
constexpr void Add(LaneWay laneWay)
{
ASSERT_LESS(laneWay, LaneWay::Count, ());
m_laneWays.set(static_cast<std::uint8_t>(laneWay));
}
constexpr void Remove(LaneWay laneWay)
{
ASSERT_LESS(laneWay, LaneWay::Count, ());
m_laneWays.reset(static_cast<std::uint8_t>(laneWay));
}
constexpr bool Contains(LaneWay laneWay) const
{
ASSERT_LESS(laneWay, LaneWay::Count, ());
return m_laneWays.test(static_cast<std::uint8_t>(laneWay));
}
/// An unrestricted lane is a lane that has no restrictions, i.e., it contains no lane ways.
constexpr bool IsUnrestricted() const
{
return m_laneWays.none() || (m_laneWays.count() == 1 && Contains(LaneWay::None));
}
[[nodiscard]] std::vector<LaneWay> GetActiveLaneWays() const
{
std::vector<LaneWay> result;
for (std::size_t i = 0; i < m_laneWays.size(); ++i)
if (m_laneWays.test(i))
result.emplace_back(static_cast<LaneWay>(i));
return result;
}
private:
LaneWaysT m_laneWays;
};
std::string DebugPrint(LaneWay laneWay);
std::string DebugPrint(LaneWays const & laneWays);
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,84 @@
#include "lanes_parser.hpp"
#include <algorithm>
#include <ranges>
namespace routing::turns::lanes
{
namespace
{
std::uint8_t constexpr kLaneWayNamesCount = static_cast<std::uint8_t>(LaneWay::Count) + 4;
/**
* The order is important. Starting with the most frequent tokens according to
* taginfo.openstreetmap.org we minimize the number of the comparisons in ParseSingleLane().
*
* A `none` lane can be represented either as "none" or as "". That means both "none" and ""
* should be considered names, even though they refer to the same thing. As a result,
* `LaneWay::None` appears twice in this array, which is one longer than the number of
* enum values.
*/
std::array<std::pair<LaneWay, std::string_view>, kLaneWayNamesCount> constexpr g_laneWayNames{{
{LaneWay::None, ""},
{LaneWay::Through, "through"},
{LaneWay::Left, "left"},
{LaneWay::Right, "right"},
{LaneWay::None, "none"},
{LaneWay::SharpLeft, "sharp_left"},
{LaneWay::SlightLeft, "slight_left"},
{LaneWay::MergeToRight, "merge_to_right"},
{LaneWay::MergeToLeft, "merge_to_left"},
{LaneWay::SlightRight, "slight_right"},
{LaneWay::SharpRight, "sharp_right"},
{LaneWay::ReverseLeft, "reverse"},
{LaneWay::Right,
"next_right"}, // "next_right" means "turn right, not in the first intersection but the one after that".
{LaneWay::Through, "slide_left"}, // "slide_left" means "move a bit left within the lane".
{LaneWay::Through, "slide_right"} // "slide_right" means "move a bit right within the lane".
}};
bool ParseSingleLane(auto && laneWayRange, LaneWay & laneWay)
{
auto const it = std::ranges::find_if(
g_laneWayNames, [&laneWayRange](auto const & pair) { return std::ranges::equal(laneWayRange, pair.second); });
if (it != g_laneWayNames.end())
{
laneWay = it->first;
return true;
}
return false;
}
} // namespace
LanesInfo ParseLanes(std::string_view lanesString)
{
if (lanesString.empty())
return {};
LanesInfo lanes;
for (auto && laneInfo : lanesString | std::views::split('|'))
{
LaneInfo lane;
if (std::ranges::empty(laneInfo))
lane.laneWays.Add(LaneWay::None);
else
{
for (auto && laneWay : laneInfo | std::views::split(';'))
{
auto way = LaneWay::None;
auto && laneWayProcessed = laneWay | std::views::filter([](char const c) { return !std::isspace(c); }) |
std::views::transform([](char const c) { return std::tolower(c); });
if (!ParseSingleLane(laneWayProcessed, way))
return {};
lane.laneWays.Add(way);
if (way == LaneWay::ReverseLeft)
lane.laneWays.Add(LaneWay::ReverseRight);
}
}
lanes.push_back(lane);
}
return lanes;
}
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,16 @@
#pragma once
#include "routing/lanes/lane_info.hpp"
#include <vector>
namespace routing::turns::lanes
{
/**
* Parse lane information which comes from lanesString
* @param lanesString lane information. Example through|through|through|through;right
* @return LanesInfo. @see LanesInfo
* @note if lanesString is empty, returns empty LanesInfo.
*/
LanesInfo ParseLanes(std::string_view lanesString);
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,129 @@
#include "lanes_recommendation.hpp"
#include "routing/route.hpp"
namespace routing::turns::lanes
{
namespace
{
void FixRecommendedReverseLane(LaneWays & ways, LaneWay const recommendedWay)
{
if (recommendedWay == LaneWay::ReverseLeft)
ways.Remove(LaneWay::ReverseRight);
else if (recommendedWay == LaneWay::ReverseRight)
ways.Remove(LaneWay::ReverseLeft);
}
} // namespace
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments)
{
for (auto & segment : routeSegments)
{
auto & t = segment.GetTurn();
if (t.IsTurnNone() || t.m_lanes.empty())
continue;
auto & lanesInfo = segment.GetTurnLanes();
// Check if there are elements in lanesInfo that correspond with the turn exactly.
// If so, fix up all the elements in lanesInfo that correspond with the turn.
if (impl::SetRecommendedLaneWays(t.m_turn, lanesInfo))
continue;
// If not, check if there are elements in lanesInfo that correspond with the turn
// approximately. If so, fix up all those elements.
if (impl::SetRecommendedLaneWaysApproximately(t.m_turn, lanesInfo))
continue;
// If not, check if there is an unrestricted lane that could correspond to the
// turn. If so, fix up that lane.
if (impl::SetUnrestrictedLaneAsRecommended(t.m_turn, lanesInfo))
continue;
// Otherwise, we don't have lane recommendations for the user, so we don't
// want to send the lane data any further.
segment.ClearTurnLanes();
}
}
bool impl::SetRecommendedLaneWays(CarDirection const carDirection, LanesInfo & lanesInfo)
{
LaneWay laneWay;
switch (carDirection)
{
case CarDirection::GoStraight: laneWay = LaneWay::Through; break;
case CarDirection::TurnRight: laneWay = LaneWay::Right; break;
case CarDirection::TurnSharpRight: laneWay = LaneWay::SharpRight; break;
case CarDirection::TurnSlightRight: [[fallthrough]];
case CarDirection::ExitHighwayToRight: laneWay = LaneWay::SlightRight; break;
case CarDirection::TurnLeft: laneWay = LaneWay::Left; break;
case CarDirection::TurnSharpLeft: laneWay = LaneWay::SharpLeft; break;
case CarDirection::TurnSlightLeft: [[fallthrough]];
case CarDirection::ExitHighwayToLeft: laneWay = LaneWay::SlightLeft; break;
case CarDirection::UTurnLeft: laneWay = LaneWay::ReverseLeft; break;
case CarDirection::UTurnRight: laneWay = LaneWay::ReverseRight; break;
default: return false;
}
bool isLaneConformed = false;
for (auto & [laneWays, recommendedWay] : lanesInfo)
{
if (laneWays.Contains(laneWay))
{
recommendedWay = laneWay;
isLaneConformed = true;
}
FixRecommendedReverseLane(laneWays, recommendedWay);
}
return isLaneConformed;
}
bool impl::SetRecommendedLaneWaysApproximately(CarDirection const carDirection, LanesInfo & lanesInfo)
{
std::vector<LaneWay> approximateLaneWays;
switch (carDirection)
{
case CarDirection::UTurnLeft: approximateLaneWays = {LaneWay::SharpLeft}; break;
case CarDirection::TurnSharpLeft: approximateLaneWays = {LaneWay::Left}; break;
case CarDirection::TurnLeft: approximateLaneWays = {LaneWay::SlightLeft, LaneWay::SharpLeft}; break;
case CarDirection::TurnSlightLeft: [[fallthrough]];
case CarDirection::ExitHighwayToLeft: approximateLaneWays = {LaneWay::Left}; break;
case CarDirection::GoStraight: approximateLaneWays = {LaneWay::SlightRight, LaneWay::SlightLeft}; break;
case CarDirection::ExitHighwayToRight: [[fallthrough]];
case CarDirection::TurnSlightRight: approximateLaneWays = {LaneWay::Right}; break;
case CarDirection::TurnRight: approximateLaneWays = {LaneWay::SlightRight, LaneWay::SharpRight}; break;
case CarDirection::TurnSharpRight: approximateLaneWays = {LaneWay::Right}; break;
case CarDirection::UTurnRight: approximateLaneWays = {LaneWay::SharpRight}; break;
default: return false;
}
bool isLaneConformed = false;
for (auto & [laneWays, recommendedWay] : lanesInfo)
{
for (auto const & laneWay : approximateLaneWays)
{
if (laneWays.Contains(laneWay))
{
recommendedWay = laneWay;
isLaneConformed = true;
break;
}
}
}
return isLaneConformed;
}
bool impl::SetUnrestrictedLaneAsRecommended(CarDirection const carDirection, LanesInfo & lanesInfo)
{
static auto constexpr setFirstUnrestrictedLane = [](LaneWay const laneWay, auto beginIt, auto endIt)
{
auto it = std::find_if(beginIt, endIt, [](auto const & laneInfo) { return laneInfo.laneWays.IsUnrestricted(); });
if (it == endIt)
return false;
it->recommendedWay = laneWay;
return true;
};
if (IsTurnMadeFromLeft(carDirection))
return setFirstUnrestrictedLane(LaneWay::Left, lanesInfo.begin(), lanesInfo.end());
if (IsTurnMadeFromRight(carDirection))
return setFirstUnrestrictedLane(LaneWay::Right, lanesInfo.rbegin(), lanesInfo.rend());
return false;
}
} // namespace routing::turns::lanes

View File

@@ -0,0 +1,31 @@
#pragma once
#include "routing/lanes/lane_info.hpp"
#include <vector>
namespace routing
{
class RouteSegment;
namespace turns
{
enum class CarDirection;
} // namespace turns
} // namespace routing
namespace routing::turns::lanes
{
/// Selects lanes which are recommended for an end user.
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments);
// Keep signatures in the header for testing purposes
namespace impl
{
bool SetRecommendedLaneWays(CarDirection carDirection, LanesInfo & lanesInfo);
bool SetRecommendedLaneWaysApproximately(CarDirection carDirection, LanesInfo & lanesInfo);
bool SetUnrestrictedLaneAsRecommended(CarDirection carDirection, LanesInfo & lanesInfo);
} // namespace impl
} // namespace routing::turns::lanes

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "routing/maxspeeds.hpp" #include "routing/lanes/lane_info.hpp"
#include "routing/road_point.hpp"
#include "routing/route.hpp" #include "routing/route.hpp"
#include "routing/segment.hpp" #include "routing/segment.hpp"
#include "routing/turns.hpp" #include "routing/turns.hpp"
@@ -10,7 +9,6 @@
#include "geometry/point_with_altitude.hpp" #include "geometry/point_with_altitude.hpp"
#include <string>
#include <vector> #include <vector>
namespace routing namespace routing
@@ -23,7 +21,7 @@ namespace routing
struct LoadedPathSegment struct LoadedPathSegment
{ {
std::vector<geometry::PointWithAltitude> m_path; std::vector<geometry::PointWithAltitude> m_path;
std::vector<turns::SingleLaneInfo> m_lanes; turns::lanes::LanesInfo m_lanes;
RouteSegment::RoadNameInfo m_roadNameInfo; RouteSegment::RoadNameInfo m_roadNameInfo;
// double m_weight = 0.0; /*!< Time in seconds to pass the segment. */ // double m_weight = 0.0; /*!< Time in seconds to pass the segment. */
SegmentRange m_segmentRange; SegmentRange m_segmentRange;

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "routing/lanes/lane_info.hpp"
#include "routing/routing_options.hpp" #include "routing/routing_options.hpp"
#include "routing/routing_settings.hpp" #include "routing/routing_settings.hpp"
#include "routing/segment.hpp" #include "routing/segment.hpp"
@@ -139,7 +140,7 @@ public:
void SetTurnExits(uint32_t exitNum) { m_turn.m_exitNum = exitNum; } 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) void SetDistancesAndTime(double distFromBeginningMeters, double distFromBeginningMerc, double timeFromBeginningS)
{ {

View File

@@ -422,15 +422,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
// Lane information // Lane information
info.m_lanes.clear(); info.m_lanes.clear();
if (distanceToTurnMeters < kShowLanesMinDistInMeters || m_route->GetCurrentTimeToNearestTurnSec() < 60.0) if (distanceToTurnMeters < kShowLanesMinDistInMeters || m_route->GetCurrentTimeToNearestTurnSec() < 60.0)
{ info.m_lanes = turn.m_lanes;
// 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.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]);
}
// Pedestrian info. // Pedestrian info.
info.m_pedestrianTurn = info.m_pedestrianTurn =

View File

@@ -1,6 +1,8 @@
project(routing_tests) project(routing_tests)
set(SRC set(SRC
lanes/lanes_parser_tests.cpp
lanes/lanes_recommendation_tests.cpp
applying_traffic_test.cpp applying_traffic_test.cpp
astar_algorithm_test.cpp astar_algorithm_test.cpp
astar_progress_test.cpp astar_progress_test.cpp

View File

@@ -0,0 +1,160 @@
#include "testing/testing.hpp"
#include "routing/lanes/lanes_parser.hpp"
namespace routing::turns::lanes::test
{
UNIT_TEST(TestParseLaneWays)
{
std::vector<std::pair<std::string, LaneWays>> const testData = {
{";", {LaneWay::None}},
{"none", {LaneWay::None}},
{"left", {LaneWay::Left}},
{"right", {LaneWay::Right}},
{"sharp_left", {LaneWay::SharpLeft}},
{"slight_left", {LaneWay::SlightLeft}},
{"merge_to_right", {LaneWay::MergeToRight}},
{"merge_to_left", {LaneWay::MergeToLeft}},
{"slight_right", {LaneWay::SlightRight}},
{"sharp_right", {LaneWay::SharpRight}},
{"reverse", {LaneWay::ReverseLeft, LaneWay::ReverseRight}},
{"next_right", {LaneWay::Right}},
{"slide_left", {LaneWay::Through}},
{"slide_right", {LaneWay::Through}},
};
for (auto const & [in, out] : testData)
{
LanesInfo const result = ParseLanes(in);
LaneWays const expected = {out};
TEST_EQUAL(result.front().laneWays, expected, ());
}
}
UNIT_TEST(TestParseSingleLane)
{
{
LanesInfo const result = ParseLanes("through;right");
LaneWays constexpr expected = {LaneWay::Through, LaneWay::Right};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes("through;Right");
LaneWays constexpr expected = {LaneWay::Through, LaneWay::Right};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes("through ;Right");
LaneWays constexpr expected = {LaneWay::Through, LaneWay::Right};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes("left;through");
LaneWays constexpr expected = {LaneWay::Left, LaneWay::Through};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes("left");
LaneWays constexpr expected = {LaneWay::Left};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes("left;");
LaneWays constexpr expected = {LaneWay::Left, LaneWay::None};
TEST_EQUAL(result.front().laneWays, expected, ());
}
{
LanesInfo const result = ParseLanes(";");
LaneWays constexpr expected = {LaneWay::None};
TEST_EQUAL(result.front().laneWays, expected, ());
}
TEST_EQUAL(ParseLanes("SD32kk*887;;").empty(), true, ());
TEST_EQUAL(ParseLanes("Что-то на кириллице").empty(), true, ());
TEST_EQUAL(ParseLanes("משהו בעברית").empty(), true, ());
}
UNIT_TEST(TestParseLanes)
{
{
LanesInfo const result = ParseLanes("through|through|through|through;right");
LanesInfo const expected = {
{{LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Through, LaneWay::Right}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|left;through|through|through");
LanesInfo const expected = {
{{LaneWay::Left}}, {{LaneWay::Left, LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Through}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|through|through");
LanesInfo const expected = {{{LaneWay::Left}}, {{LaneWay::Through}}, {{LaneWay::Through}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|le ft| through|through | right");
LanesInfo const expected = {
{{LaneWay::Left}}, {{LaneWay::Left}}, {{LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Right}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|Left|through|througH|right");
LanesInfo const expected = {
{{LaneWay::Left}}, {{LaneWay::Left}}, {{LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Right}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|Left|through|througH|through;right;sharp_rIght");
LanesInfo const expected = {{{LaneWay::Left}},
{{LaneWay::Left}},
{{LaneWay::Through}},
{{LaneWay::Through}},
{{LaneWay::Through, LaneWay::Right, LaneWay::SharpRight}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left |Left|through|througH|right");
LanesInfo const expected = {
{{LaneWay::Left}}, {{LaneWay::Left}}, {{LaneWay::Through}}, {{LaneWay::Through}}, {{LaneWay::Right}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("|||||slight_right");
LanesInfo const expected = {{{LaneWay::None}}, {{LaneWay::None}}, {{LaneWay::None}},
{{LaneWay::None}}, {{LaneWay::None}}, {{LaneWay::SlightRight}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("|");
LanesInfo const expected = {{{LaneWay::None}}, {{LaneWay::None}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes(";|;;;");
LanesInfo const expected = {{{LaneWay::None}}, {{LaneWay::None}}};
TEST_EQUAL(result, expected, ());
}
{
LanesInfo const result = ParseLanes("left|Leftt|through|througH|right");
TEST_EQUAL(result.empty(), true, ());
}
}
} // namespace routing::turns::lanes::test

View File

@@ -0,0 +1,221 @@
#include "routing/turns.hpp"
#include "testing/testing.hpp"
#include "routing/lanes/lanes_recommendation.hpp"
#include "routing/routing_tests/tools.hpp"
namespace routing::turns::lanes::test
{
UNIT_TEST(TestSetRecommendedLaneWays_Smoke)
{
using impl::SetRecommendedLaneWays;
struct CarDirectionToLaneWayMapping
{
CarDirection carDirection;
LaneWay laneWay;
bool shouldBeRecommended;
};
std::vector<CarDirectionToLaneWayMapping> const testData = {
{CarDirection::GoStraight, LaneWay::Through, true},
{CarDirection::TurnRight, LaneWay::Right, true},
{CarDirection::TurnSharpRight, LaneWay::SharpRight, true},
{CarDirection::TurnSlightRight, LaneWay::SlightRight, true},
{CarDirection::TurnLeft, LaneWay::Left, true},
{CarDirection::TurnSharpLeft, LaneWay::SharpLeft, true},
{CarDirection::TurnSlightLeft, LaneWay::SlightLeft, true},
{CarDirection::UTurnLeft, LaneWay::ReverseLeft, true},
{CarDirection::UTurnRight, LaneWay::ReverseRight, true},
{CarDirection::ExitHighwayToLeft, LaneWay::SlightLeft, true},
{CarDirection::ExitHighwayToRight, LaneWay::SlightRight, true},
// We do not recommend any lane way for these directions
{CarDirection::None, LaneWay::None, false},
{CarDirection::EnterRoundAbout, LaneWay::None, false},
{CarDirection::LeaveRoundAbout, LaneWay::None, false},
{CarDirection::StayOnRoundAbout, LaneWay::None, false},
{CarDirection::StartAtEndOfStreet, LaneWay::None, false},
{CarDirection::ReachedYourDestination, LaneWay::None, false},
};
TEST_EQUAL(testData.size(), static_cast<size_t>(CarDirection::Count), ("Not all CarDirection values are covered."));
for (auto const & [carDirection, laneWay, shouldBeRecommended] : testData)
{
LanesInfo lanesInfo = {{{laneWay}}};
bool const isRecommended = SetRecommendedLaneWays(carDirection, lanesInfo);
TEST_EQUAL(isRecommended, shouldBeRecommended,
("CarDirection:", DebugPrint(carDirection), "LaneWay:", DebugPrint(laneWay)));
TEST_EQUAL(lanesInfo[0].recommendedWay, shouldBeRecommended ? laneWay : LaneWay::None, ());
}
}
UNIT_TEST(TestSetRecommendedLaneWays)
{
{
LanesInfo lanesInfo = {
{{LaneWay::ReverseLeft, LaneWay::Left, LaneWay::Through}},
{{LaneWay::Through}},
{{LaneWay::Through}},
{{LaneWay::Through, LaneWay::Right}},
{{LaneWay::Right}},
};
TEST(impl::SetRecommendedLaneWays(CarDirection::GoStraight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::Through, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::Through, ());
TEST_EQUAL(lanesInfo[2].recommendedWay, LaneWay::Through, ());
TEST_EQUAL(lanesInfo[3].recommendedWay, LaneWay::Through, ());
TEST_EQUAL(lanesInfo[4].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = {
{{LaneWay::ReverseLeft, LaneWay::Left}},
{{LaneWay::Right}},
};
TEST(!impl::SetRecommendedLaneWays(CarDirection::GoStraight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = {
{{LaneWay::ReverseLeft, LaneWay::ReverseRight}},
};
TEST(impl::SetRecommendedLaneWays(CarDirection::UTurnLeft, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::ReverseLeft, ());
TEST_EQUAL(lanesInfo[0].laneWays.Contains(LaneWay::ReverseRight), false, ());
}
}
UNIT_TEST(SetRecommendedLaneWaysApproximately_Smoke)
{
{
struct CarDirectionToLaneWaysApproximateMapping
{
CarDirection carDirection;
std::vector<LaneWay> laneWay;
};
std::vector<CarDirectionToLaneWaysApproximateMapping> const testData = {
{CarDirection::UTurnLeft, {LaneWay::SharpLeft}},
{CarDirection::TurnSharpLeft, {LaneWay::Left}},
{CarDirection::TurnLeft, {LaneWay::SlightLeft, LaneWay::SharpLeft}},
{CarDirection::TurnSlightLeft, {LaneWay::Left}},
{CarDirection::ExitHighwayToLeft, {LaneWay::Left}},
{CarDirection::GoStraight, {LaneWay::SlightRight, LaneWay::SlightLeft}},
{CarDirection::ExitHighwayToRight, {LaneWay::Right}},
{CarDirection::TurnSlightRight, {LaneWay::Right}},
{CarDirection::TurnRight, {LaneWay::SlightRight, LaneWay::SharpRight}},
{CarDirection::TurnSharpRight, {LaneWay::Right}},
{CarDirection::UTurnRight, {LaneWay::SharpRight}},
};
for (auto const & [carDirection, laneWays] : testData)
{
for (auto const & laneWay : laneWays)
{
LanesInfo lanesInfo = {{{laneWay}}};
bool const isRecommended = impl::SetRecommendedLaneWaysApproximately(carDirection, lanesInfo);
TEST(isRecommended, ("CarDirection:", DebugPrint(carDirection), "LaneWay:", DebugPrint(laneWay)));
TEST_EQUAL(lanesInfo[0].recommendedWay, laneWay, ());
}
}
}
{
// Those directions do not have any recommended lane ways.
std::vector const carDirections = {CarDirection::None,
CarDirection::EnterRoundAbout,
CarDirection::LeaveRoundAbout,
CarDirection::StayOnRoundAbout,
CarDirection::StartAtEndOfStreet,
CarDirection::ReachedYourDestination};
for (auto const & carDirection : carDirections)
{
LanesInfo lanesInfo = {{{LaneWay::Through}}};
TEST(!impl::SetRecommendedLaneWaysApproximately(carDirection, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
}
}
}
UNIT_TEST(SetRecommendedLaneWaysApproximately)
{
{
LanesInfo lanesInfo = {
{{LaneWay::ReverseLeft, LaneWay::Left, LaneWay::SlightLeft}},
{{LaneWay::SlightRight, LaneWay::Right}},
{{LaneWay::Right}},
};
TEST(impl::SetRecommendedLaneWaysApproximately(CarDirection::GoStraight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::SlightLeft, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::SlightRight, ());
TEST_EQUAL(lanesInfo[2].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = {
{{LaneWay::ReverseLeft, LaneWay::Left}},
{{LaneWay::Right}},
};
TEST(!impl::SetRecommendedLaneWaysApproximately(CarDirection::GoStraight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = {
{{LaneWay::SharpLeft, LaneWay::SlightLeft}},
};
TEST(impl::SetRecommendedLaneWaysApproximately(CarDirection::TurnLeft, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::SlightLeft, ());
}
}
UNIT_TEST(SetUnrestrictedLaneAsRecommended)
{
LanesInfo const testData = {{{LaneWay::ReverseLeft}}, {{LaneWay::None}}, {{LaneWay::None}}, {{LaneWay::Right}}};
{
LanesInfo lanesInfo = testData;
TEST(impl::SetUnrestrictedLaneAsRecommended(CarDirection::TurnLeft, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::Left, ());
TEST_EQUAL(lanesInfo[2].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[3].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = testData;
TEST(impl::SetUnrestrictedLaneAsRecommended(CarDirection::TurnRight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[1].recommendedWay, LaneWay::None, ());
TEST_EQUAL(lanesInfo[2].recommendedWay, LaneWay::Right, ());
TEST_EQUAL(lanesInfo[3].recommendedWay, LaneWay::None, ());
}
{
LanesInfo lanesInfo = {};
TEST(!impl::SetUnrestrictedLaneAsRecommended(CarDirection::TurnRight, lanesInfo), ());
}
{
LanesInfo lanesInfo = {{{LaneWay::Right}}};
TEST(!impl::SetUnrestrictedLaneAsRecommended(CarDirection::TurnRight, lanesInfo), ());
TEST_EQUAL(lanesInfo[0].recommendedWay, LaneWay::None, ());
}
}
UNIT_TEST(SelectRecommendedLanes)
{
std::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}});
std::vector<RouteSegment> routeSegments;
RouteSegmentsFrom({}, {}, turns, {}, routeSegments);
SelectRecommendedLanes(routeSegments);
TEST_EQUAL(routeSegments[0].GetTurn().m_lanes[0].recommendedWay, LaneWay::Through, ());
TEST_EQUAL(routeSegments[0].GetTurn().m_lanes[1].recommendedWay, LaneWay::None, ());
TEST_EQUAL(routeSegments[1].GetTurn().m_lanes[0].recommendedWay, LaneWay::SlightLeft, ());
TEST_EQUAL(routeSegments[1].GetTurn().m_lanes[1].recommendedWay, LaneWay::None, ());
TEST_EQUAL(routeSegments[1].GetTurn().m_lanes[2].recommendedWay, LaneWay::None, ());
TEST_EQUAL(routeSegments[2].GetTurn().m_lanes[0].recommendedWay, LaneWay::None, ());
TEST_EQUAL(routeSegments[2].GetTurn().m_lanes[1].recommendedWay, LaneWay::Right, ());
}
} // namespace routing::turns::lanes::test

View File

@@ -18,7 +18,6 @@
#include "base/macros.hpp" #include "base/macros.hpp"
#include <cmath>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -65,118 +64,6 @@ private:
TUnpackedPathSegments m_segments; 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) UNIT_TEST(TestFixupTurns)
{ {
double const kHalfSquareSideMeters = 10.; double const kHalfSquareSideMeters = 10.;
@@ -238,76 +125,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) UNIT_TEST(TestGetRoundaboutDirection)
{ {
// The signature of GetRoundaboutDirection function is // The signature of GetRoundaboutDirection function is

View File

@@ -5,8 +5,6 @@
#include "platform/country_file.hpp" #include "platform/country_file.hpp"
#include "base/internal/message.hpp" #include "base/internal/message.hpp"
#include "base/stl_helpers.hpp"
#include "base/string_utils.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -21,28 +19,6 @@ using namespace std;
namespace 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 = { array<pair<CarDirection, char const *>, static_cast<size_t>(CarDirection::Count)> const g_turnNames = {
{{CarDirection::None, "None"}, {{CarDirection::None, "None"},
{CarDirection::GoStraight, "GoStraight"}, {CarDirection::GoStraight, "GoStraight"},
@@ -162,12 +138,6 @@ string DebugPrint(SegmentRange const & segmentRange)
namespace turns 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) string DebugPrint(TurnItem const & turnItem)
{ {
stringstream out; stringstream out;
@@ -232,118 +202,6 @@ bool IsGoStraightOrSlightTurn(CarDirection t)
return (t == CarDirection::GoStraight || t == CarDirection::TurnSlightLeft || t == CarDirection::TurnSlightRight); return (t == CarDirection::GoStraight || t == CarDirection::TurnSlightLeft || 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(SingleLaneInfo const & 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, strings::IsASCIISpace<std::string::value_type>);
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) string DebugPrint(CarDirection const turn)
{ {
return GetTurnString(turn); return GetTurnString(turn);
@@ -368,14 +226,6 @@ string DebugPrint(PedestrianDirection const l)
return "unknown PedestrianDirection"; 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, double PiMinusTwoVectorsAngle(m2::PointD const & junctionPoint, m2::PointD const & ingoingPoint,
m2::PointD const & outgoingPoint) m2::PointD const & outgoingPoint)
{ {

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "routing/lanes/lane_info.hpp"
#include "routing/segment.hpp" #include "routing/segment.hpp"
#include "routing_common/num_mwm_id.hpp" #include "routing_common/num_mwm_id.hpp"
@@ -8,7 +9,6 @@
#include "geometry/point2d.hpp" #include "geometry/point2d.hpp"
#include <initializer_list>
#include <limits> #include <limits>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -118,41 +118,6 @@ enum class PedestrianDirection
std::string DebugPrint(PedestrianDirection const l); 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 struct TurnItem
{ {
TurnItem() TurnItem()
@@ -192,7 +157,7 @@ struct TurnItem
uint32_t m_index; /*!< Index of point on route polyline (Index of segment + 1). */ 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 */ CarDirection m_turn = CarDirection::None; /*!< The turn instruction of the TurnItem */
std::vector<SingleLaneInfo> m_lanes; /*!< Lane information on the edge before the turn. */ lanes::LanesInfo m_lanes; /*!< Lane information on the edge before the turn. */
uint32_t m_exitNum; /*!< Number of exit on roundabout. */ uint32_t m_exitNum; /*!< Number of exit on roundabout. */
/*! /*!
* \brief m_pedestrianTurn is type of corresponding direction for a pedestrian, or None * \brief m_pedestrianTurn is type of corresponding direction for a pedestrian, or None
@@ -223,39 +188,6 @@ bool IsTurnMadeFromLeft(CarDirection t);
bool IsTurnMadeFromRight(CarDirection t); bool IsTurnMadeFromRight(CarDirection t);
bool IsStayOnRoad(CarDirection t); bool IsStayOnRoad(CarDirection t);
bool IsGoStraightOrSlightTurn(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(SingleLaneInfo const & 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] * \returns pi minus angle from vector [junctionPoint, ingoingPoint]
* to vector [junctionPoint, outgoingPoint]. A counterclockwise rotation. * to vector [junctionPoint, outgoingPoint]. A counterclockwise rotation.

View File

@@ -48,6 +48,16 @@
0C81E1581F0258AA00DC66DF /* segmented_route.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C81E1561F0258AA00DC66DF /* segmented_route.hpp */; }; 0C81E1581F0258AA00DC66DF /* segmented_route.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C81E1561F0258AA00DC66DF /* segmented_route.hpp */; };
0C8705051E0182F200BCAF71 /* route_point.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C8705041E0182F200BCAF71 /* route_point.hpp */; }; 0C8705051E0182F200BCAF71 /* route_point.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0C8705041E0182F200BCAF71 /* route_point.hpp */; };
0CF709361F05172200D5067E /* checkpoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0CF709351F05172200D5067E /* checkpoints.cpp */; }; 0CF709361F05172200D5067E /* checkpoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0CF709351F05172200D5067E /* checkpoints.cpp */; };
1667FB002E3F845D00F06E59 /* lanes_parser.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1667FAFB2E3F845D00F06E59 /* lanes_parser.hpp */; };
1667FB012E3F845D00F06E59 /* lane_info.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1667FAF72E3F845D00F06E59 /* lane_info.hpp */; };
1667FB022E3F845D00F06E59 /* lanes_recommendation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1667FAFD2E3F845D00F06E59 /* lanes_recommendation.hpp */; };
1667FB032E3F845D00F06E59 /* lane_way.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1667FAF92E3F845D00F06E59 /* lane_way.hpp */; };
1667FB042E3F845D00F06E59 /* lane_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FAF82E3F845D00F06E59 /* lane_info.cpp */; };
1667FB052E3F845D00F06E59 /* lanes_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FAFC2E3F845D00F06E59 /* lanes_parser.cpp */; };
1667FB062E3F845D00F06E59 /* lane_way.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FAFA2E3F845D00F06E59 /* lane_way.cpp */; };
1667FB072E3F845D00F06E59 /* lanes_recommendation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FAFE2E3F845D00F06E59 /* lanes_recommendation.cpp */; };
1667FB0B2E3F847B00F06E59 /* lanes_recommendation_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FB092E3F847B00F06E59 /* lanes_recommendation_tests.cpp */; };
1667FB0C2E3F847B00F06E59 /* lanes_parser_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1667FB082E3F847B00F06E59 /* lanes_parser_tests.cpp */; };
349D1CE01E3F589900A878FD /* restrictions_serialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349D1CDE1E3F589900A878FD /* restrictions_serialization.cpp */; }; 349D1CE01E3F589900A878FD /* restrictions_serialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349D1CDE1E3F589900A878FD /* restrictions_serialization.cpp */; };
349D1CE11E3F589900A878FD /* restrictions_serialization.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 349D1CDF1E3F589900A878FD /* restrictions_serialization.hpp */; }; 349D1CE11E3F589900A878FD /* restrictions_serialization.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 349D1CDF1E3F589900A878FD /* restrictions_serialization.hpp */; };
40576F781F7A788B000B593B /* fake_vertex.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 40576F771F7A788B000B593B /* fake_vertex.hpp */; }; 40576F781F7A788B000B593B /* fake_vertex.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 40576F771F7A788B000B593B /* fake_vertex.hpp */; };
@@ -329,6 +339,16 @@
0C8705041E0182F200BCAF71 /* route_point.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = route_point.hpp; sourceTree = "<group>"; }; 0C8705041E0182F200BCAF71 /* route_point.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = route_point.hpp; sourceTree = "<group>"; };
0CF5E8A91E8EA7A1001ED497 /* coding_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coding_test.cpp; sourceTree = "<group>"; }; 0CF5E8A91E8EA7A1001ED497 /* coding_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coding_test.cpp; sourceTree = "<group>"; };
0CF709351F05172200D5067E /* checkpoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = checkpoints.cpp; sourceTree = "<group>"; }; 0CF709351F05172200D5067E /* checkpoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = checkpoints.cpp; sourceTree = "<group>"; };
1667FAF72E3F845D00F06E59 /* lane_info.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lane_info.hpp; sourceTree = "<group>"; };
1667FAF82E3F845D00F06E59 /* lane_info.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lane_info.cpp; sourceTree = "<group>"; };
1667FAF92E3F845D00F06E59 /* lane_way.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lane_way.hpp; sourceTree = "<group>"; };
1667FAFA2E3F845D00F06E59 /* lane_way.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lane_way.cpp; sourceTree = "<group>"; };
1667FAFB2E3F845D00F06E59 /* lanes_parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lanes_parser.hpp; sourceTree = "<group>"; };
1667FAFC2E3F845D00F06E59 /* lanes_parser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lanes_parser.cpp; sourceTree = "<group>"; };
1667FAFD2E3F845D00F06E59 /* lanes_recommendation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lanes_recommendation.hpp; sourceTree = "<group>"; };
1667FAFE2E3F845D00F06E59 /* lanes_recommendation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lanes_recommendation.cpp; sourceTree = "<group>"; };
1667FB082E3F847B00F06E59 /* lanes_parser_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lanes_parser_tests.cpp; sourceTree = "<group>"; };
1667FB092E3F847B00F06E59 /* lanes_recommendation_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lanes_recommendation_tests.cpp; sourceTree = "<group>"; };
349D1CDE1E3F589900A878FD /* restrictions_serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = restrictions_serialization.cpp; sourceTree = "<group>"; }; 349D1CDE1E3F589900A878FD /* restrictions_serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = restrictions_serialization.cpp; sourceTree = "<group>"; };
349D1CDF1E3F589900A878FD /* restrictions_serialization.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = restrictions_serialization.hpp; sourceTree = "<group>"; }; 349D1CDF1E3F589900A878FD /* restrictions_serialization.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = restrictions_serialization.hpp; sourceTree = "<group>"; };
34F558351DBF2A2600A4FC11 /* common-debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../common-debug.xcconfig"; sourceTree = "<group>"; }; 34F558351DBF2A2600A4FC11 /* common-debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../common-debug.xcconfig"; sourceTree = "<group>"; };
@@ -575,6 +595,30 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1667FAFF2E3F845D00F06E59 /* lanes */ = {
isa = PBXGroup;
children = (
1667FAF72E3F845D00F06E59 /* lane_info.hpp */,
1667FAF82E3F845D00F06E59 /* lane_info.cpp */,
1667FAF92E3F845D00F06E59 /* lane_way.hpp */,
1667FAFA2E3F845D00F06E59 /* lane_way.cpp */,
1667FAFB2E3F845D00F06E59 /* lanes_parser.hpp */,
1667FAFC2E3F845D00F06E59 /* lanes_parser.cpp */,
1667FAFD2E3F845D00F06E59 /* lanes_recommendation.hpp */,
1667FAFE2E3F845D00F06E59 /* lanes_recommendation.cpp */,
);
path = lanes;
sourceTree = "<group>";
};
1667FB0A2E3F847B00F06E59 /* lanes */ = {
isa = PBXGroup;
children = (
1667FB082E3F847B00F06E59 /* lanes_parser_tests.cpp */,
1667FB092E3F847B00F06E59 /* lanes_recommendation_tests.cpp */,
);
path = lanes;
sourceTree = "<group>";
};
56F0D7611D896DAF00045886 /* Frameworks */ = { 56F0D7611D896DAF00045886 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -601,6 +645,7 @@
6742ACA01C68A07C009CB89E /* routing_tests */ = { 6742ACA01C68A07C009CB89E /* routing_tests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1667FB0A2E3F847B00F06E59 /* lanes */,
56CA09DE1E30E73B00D05C9A /* applying_traffic_test.cpp */, 56CA09DE1E30E73B00D05C9A /* applying_traffic_test.cpp */,
6742ACA61C68A0B1009CB89E /* astar_algorithm_test.cpp */, 6742ACA61C68A0B1009CB89E /* astar_algorithm_test.cpp */,
6742ACA71C68A0B1009CB89E /* astar_progress_test.cpp */, 6742ACA71C68A0B1009CB89E /* astar_progress_test.cpp */,
@@ -695,6 +740,7 @@
675343FA1A3F640D00A0A8C3 /* routing */ = { 675343FA1A3F640D00A0A8C3 /* routing */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1667FAFF2E3F845D00F06E59 /* lanes */,
D549BEBA25765CFA009131F2 /* absent_regions_finder.hpp */, D549BEBA25765CFA009131F2 /* absent_regions_finder.hpp */,
D549BEBC25765CFB009131F2 /* absent_regions_finder.cpp */, D549BEBC25765CFB009131F2 /* absent_regions_finder.cpp */,
44A95C6F225F6A4F00C22F4F /* astar_graph.hpp */, 44A95C6F225F6A4F00C22F4F /* astar_graph.hpp */,
@@ -963,6 +1009,10 @@
56C439291E93BF8C00998E29 /* cross_mwm_graph.hpp in Headers */, 56C439291E93BF8C00998E29 /* cross_mwm_graph.hpp in Headers */,
44C56C0A22296498006C2A1D /* routing_options.hpp in Headers */, 44C56C0A22296498006C2A1D /* routing_options.hpp in Headers */,
4408A63C21F1E7F0008171B8 /* joint_segment.hpp in Headers */, 4408A63C21F1E7F0008171B8 /* joint_segment.hpp in Headers */,
1667FB002E3F845D00F06E59 /* lanes_parser.hpp in Headers */,
1667FB012E3F845D00F06E59 /* lane_info.hpp in Headers */,
1667FB022E3F845D00F06E59 /* lanes_recommendation.hpp in Headers */,
1667FB032E3F845D00F06E59 /* lane_way.hpp in Headers */,
0C81E1541F02589800DC66DF /* traffic_stash.hpp in Headers */, 0C81E1541F02589800DC66DF /* traffic_stash.hpp in Headers */,
40A111D01F2F9704005E6AD5 /* astar_weight.hpp in Headers */, 40A111D01F2F9704005E6AD5 /* astar_weight.hpp in Headers */,
0C8705051E0182F200BCAF71 /* route_point.hpp in Headers */, 0C8705051E0182F200BCAF71 /* route_point.hpp in Headers */,
@@ -1212,6 +1262,8 @@
6742AD271C68A9DF009CB89E /* astar_algorithm_test.cpp in Sources */, 6742AD271C68A9DF009CB89E /* astar_algorithm_test.cpp in Sources */,
FAA838A726BB4B1D002E54C6 /* road_access_test.cpp in Sources */, FAA838A726BB4B1D002E54C6 /* road_access_test.cpp in Sources */,
6742AD351C68A9DF009CB89E /* turns_generator_test.cpp in Sources */, 6742AD351C68A9DF009CB89E /* turns_generator_test.cpp in Sources */,
1667FB0B2E3F847B00F06E59 /* lanes_recommendation_tests.cpp in Sources */,
1667FB0C2E3F847B00F06E59 /* lanes_parser_tests.cpp in Sources */,
FAA838AE26BB4B44002E54C6 /* fake_graph_test.cpp in Sources */, FAA838AE26BB4B44002E54C6 /* fake_graph_test.cpp in Sources */,
FAA838AA26BB4B28002E54C6 /* cumulative_restriction_test.cpp in Sources */, FAA838AA26BB4B28002E54C6 /* cumulative_restriction_test.cpp in Sources */,
FAA8389E26BB4AE1002E54C6 /* opening_hours_serdes_tests.cpp in Sources */, FAA8389E26BB4AE1002E54C6 /* opening_hours_serdes_tests.cpp in Sources */,
@@ -1230,6 +1282,10 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1667FB062E3F845D00F06E59 /* lane_way.cpp in Sources */,
1667FB052E3F845D00F06E59 /* lanes_parser.cpp in Sources */,
1667FB042E3F845D00F06E59 /* lane_info.cpp in Sources */,
1667FB072E3F845D00F06E59 /* lanes_recommendation.cpp in Sources */,
D5481E4F24BF4F70008FB1D8 /* mwm_hierarchy_handler.cpp in Sources */, D5481E4F24BF4F70008FB1D8 /* mwm_hierarchy_handler.cpp in Sources */,
0C5FEC641DDE192A0017688C /* joint.cpp in Sources */, 0C5FEC641DDE192A0017688C /* joint.cpp in Sources */,
0C090C871E4E276700D52AFD /* world_graph.cpp in Sources */, 0C090C871E4E276700D52AFD /* world_graph.cpp in Sources */,