Merge commit 'eb264889e3' into traffic

This commit is contained in:
mvglasow
2025-10-10 18:40:35 +03:00
808 changed files with 31595 additions and 27751 deletions

View File

@@ -7,11 +7,11 @@
#include "base/assert.hpp"
#include "base/logging.hpp"
#include <boost/core/bit.hpp>
#include <boost/uuid/detail/sha1.hpp>
#include <algorithm>
#include <vector>
#include <bit>
namespace coding
{
@@ -22,7 +22,7 @@ SHA1::Hash ExtractHash(boost::uuids::detail::sha1 & sha1)
boost::uuids::detail::sha1::digest_type digest;
sha1.get_digest(digest);
for (auto & b : digest)
b = boost::core::byteswap(b);
b = std::byteswap(b);
SHA1::Hash result;
static_assert(result.size() == sizeof(digest));

View File

@@ -51,9 +51,7 @@ public:
uint32_t m_textureIndex = 0;
};
// TODO(AB): Remove?
class GlyphRegion : public BaseRegion
{};
using GlyphRegion = BaseRegion;
class StippleRegion : public BaseRegion
{
@@ -61,8 +59,7 @@ public:
m2::PointU GetMaskPixelSize() const;
};
class ColorRegion : public BaseRegion
{};
using ColorRegion = BaseRegion;
struct Params
{

View File

@@ -194,6 +194,8 @@ bool Metadata::TypeFromString(string_view k, Metadata::EType & outType)
outType = Metadata::FMD_OUTDOOR_SEATING;
else if (k == "network")
outType = Metadata::FMD_NETWORK;
else if (k.starts_with("socket:"))
outType = Metadata::FMD_CHARGE_SOCKETS;
else
return false;
@@ -315,6 +317,7 @@ string ToString(Metadata::EType type)
case Metadata::FMD_SELF_SERVICE: return "self_service";
case Metadata::FMD_OUTDOOR_SEATING: return "outdoor_seating";
case Metadata::FMD_NETWORK: return "network";
case Metadata::FMD_CHARGE_SOCKETS: CHECK(false, ("FMD_CHARGE_SOCKETS is a compound attribute."));
case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type."));
};

View File

@@ -124,6 +124,7 @@ public:
FMD_CHECK_DATE = 53,
FMD_CHECK_DATE_OPEN_HOURS = 54,
FMD_BRANCH = 55,
FMD_CHARGE_SOCKETS = 56,
FMD_COUNT
};

View File

@@ -459,19 +459,33 @@ OneLevelPOIChecker::OneLevelPOIChecker() : ftypes::BaseChecker(1 /* level */)
TwoLevelPOIChecker::TwoLevelPOIChecker() : ftypes::BaseChecker(2 /* level */)
{
Classificator const & c = classif();
base::StringIL arr[] = {{"aeroway", "terminal"}, {"aeroway", "gate"},
{"building", "train_station"}, {"emergency", "defibrillator"},
{"emergency", "fire_hydrant"}, {"emergency", "phone"},
{"highway", "bus_stop"}, {"highway", "elevator"},
{"highway", "ford"}, {"highway", "raceway"},
{"highway", "rest_area"}, {"highway", "services"},
{"highway", "speed_camera"}, {"man_made", "communications_tower"},
{"man_made", "cross"}, {"man_made", "lighthouse"},
{"man_made", "water_tap"}, {"man_made", "water_well"},
{"natural", "beach"}, {"natural", "cave_entrance"},
{"natural", "geyser"}, {"natural", "hot_spring"},
{"natural", "peak"}, {"natural", "saddle"},
{"natural", "spring"}, {"natural", "volcano"},
base::StringIL arr[] = {{"aeroway", "terminal"},
{"aeroway", "gate"},
{"building", "guardhouse"},
{"building", "train_station"},
{"emergency", "defibrillator"},
{"emergency", "fire_hydrant"},
{"emergency", "phone"},
{"highway", "bus_stop"},
{"highway", "elevator"},
{"highway", "ford"},
{"highway", "raceway"},
{"highway", "rest_area"},
{"highway", "services"},
{"highway", "speed_camera"},
{"man_made", "communications_tower"},
{"man_made", "cross"},
{"man_made", "lighthouse"},
{"man_made", "water_tap"},
{"man_made", "water_well"},
{"natural", "beach"},
{"natural", "cave_entrance"},
{"natural", "geyser"},
{"natural", "hot_spring"},
{"natural", "peak"},
{"natural", "saddle"},
{"natural", "spring"},
{"natural", "volcano"},
{"waterway", "waterfall"}};
for (auto const & path : arr)

View File

@@ -178,6 +178,57 @@ std::string_view MapObject::GetOpeningHours() const
return m_metadata.Get(MetadataID::FMD_OPEN_HOURS);
}
ChargeSocketDescriptors MapObject::GetChargeSockets() const
{
ChargeSocketDescriptors sockets;
auto s = std::string(m_metadata.Get(MetadataID::FMD_CHARGE_SOCKETS));
if (s.empty())
return sockets;
auto tokens = strings::Tokenize(s, ";");
for (auto token : tokens)
{
if (token.empty())
continue;
auto fields = strings::Tokenize(token, "|");
if (fields.size() < 3)
continue; // invalid entry, skip
ChargeSocketDescriptor desc;
desc.type = fields[0];
try
{
desc.count = std::stoi(std::string(fields[1]));
}
catch (...)
{
desc.count = 0;
}
if (fields.size() >= 3)
{
try
{
desc.power = std::stod(std::string(fields[2]));
}
catch (...)
{
desc.power = 0;
}
}
else
desc.power = 0;
sockets.push_back(desc);
}
return sockets;
}
feature::Internet MapObject::GetInternet() const
{
return feature::InternetFromString(m_metadata.Get(MetadataID::FMD_INTERNET));
@@ -242,6 +293,11 @@ int MapObject::GetStars() const
return count;
}
std::string MapObject::GetCapacity() const
{
return std::string(m_metadata.Get(MetadataID::FMD_CAPACITY));
}
bool MapObject::IsPointType() const
{
return m_geomType == feature::GeomType::Point;

View File

@@ -17,6 +17,17 @@ namespace osm
{
class EditableMapObject;
// struct to store the representation of a charging station socket
struct ChargeSocketDescriptor
{
std::string type; // https://wiki.openstreetmap.org/wiki/Key:socket:*
// e.g. "type1"
unsigned int count; // number of sockets; 0 means socket present, but unknown count
// (eg, OSM tag for count set to 'yes')
double power; // power output, in kW. 0 means unknown.
};
typedef std::vector<ChargeSocketDescriptor> ChargeSocketDescriptors;
class MapObject
{
public:
@@ -80,9 +91,19 @@ public:
std::string FormatRoadShields() const;
/** parses a list of charging station sockets
* stored as "<type>|<nb>|[<power>];..." into a vector of
* socket descriptors
*
* For instance:
* "type2_combo|2|150;chademo|1|50;type2|4|"
*/
ChargeSocketDescriptors GetChargeSockets() const;
std::string_view GetOpeningHours() const;
feature::Internet GetInternet() const;
int GetStars() const;
std::string GetCapacity() const;
/// @returns true if feature has ATM type.
bool HasAtm() const;

View File

@@ -1,6 +1,7 @@
#include "color_parser.hpp"
#include "coding/hex.hpp"
#include "types.hpp"
#include "base/string_utils.hpp"
@@ -31,6 +32,97 @@ std::optional<uint32_t> ParseHexColor(std::string_view c)
}
}
std::tuple<int, int, int> ExtractRGB(uint32_t rgbaColor)
{
return {(rgbaColor >> 24) & 0xFF, (rgbaColor >> 16) & 0xFF, (rgbaColor >> 8) & 0xFF};
}
static int ColorDistance(uint32_t rgbaColor1, uint32_t rgbaColor2)
{
auto const [r1, g1, b1] = ExtractRGB(rgbaColor1);
auto const [r2, g2, b2] = ExtractRGB(rgbaColor2);
return (r1 - r2) * (r1 - r2) + (g1 - g2) * (g1 - g2) + (b1 - b2) * (b1 - b2);
}
struct RGBAToGarmin
{
uint32_t rgba;
std::string_view color;
};
auto constexpr kRGBAToGarmin = std::to_array<RGBAToGarmin>({{0x000000ff, "Black"},
{0x8b0000ff, "DarkRed"},
{0x006400ff, "DarkGreen"},
{0xb5b820ff, "DarkYellow"},
{0x00008bff, "DarkBlue"},
{0x8b008bff, "DarkMagenta"},
{0x008b8bff, "DarkCyan"},
{0xccccccff, "LightGray"},
{0x444444ff, "DarkGray"},
{0xff0000ff, "Red"},
{0x00ff00ff, "Green"},
{0xffff00ff, "Yellow"},
{0x0000ffff, "Blue"},
{0xff00ffff, "Magenta"},
{0x00ffffff, "Cyan"},
{0xffffffff, "White"}});
std::string_view MapGarminColor(uint32_t rgba)
{
std::string_view closestColor = kRGBAToGarmin[0].color;
auto minDistance = std::numeric_limits<int>::max();
for (auto const & [rgbaGarmin, color] : kRGBAToGarmin)
{
auto const distance = ColorDistance(rgba, rgbaGarmin);
if (distance == 0)
return color; // Exact match.
if (distance < minDistance)
{
minDistance = distance;
closestColor = color;
}
}
return closestColor;
}
struct RGBAToPredefined
{
uint32_t rgba;
PredefinedColor predefinedColor;
};
static std::array<RGBAToPredefined, kOrderedPredefinedColors.size()> buildRGBAToPredefined()
{
auto res = std::array<RGBAToPredefined, kOrderedPredefinedColors.size()>();
for (size_t i = 0; i < kOrderedPredefinedColors.size(); ++i)
res[i] = {ColorFromPredefinedColor(kOrderedPredefinedColors[i]).GetRGBA(), kOrderedPredefinedColors[i]};
return res;
}
auto const kRGBAToPredefined = buildRGBAToPredefined();
PredefinedColor MapPredefinedColor(uint32_t rgba)
{
auto closestColor = kRGBAToPredefined[0].predefinedColor;
auto minDistance = std::numeric_limits<int>::max();
for (auto const & [rgbaGarmin, color] : kRGBAToPredefined)
{
auto const distance = ColorDistance(rgba, rgbaGarmin);
if (distance == 0)
return color; // Exact match.
if (distance < minDistance)
{
minDistance = distance;
closestColor = color;
}
}
return closestColor;
}
// Garmin extensions spec: https://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd
// Color mapping: https://help.locusmap.eu/topic/extend-garmin-gpx-compatibilty
std::optional<uint32_t> ParseGarminColor(std::string_view c)

View File

@@ -4,6 +4,8 @@
#include <optional>
#include <string_view>
#include "types.hpp"
namespace kml
{
@@ -18,4 +20,7 @@ std::optional<uint32_t> ParseHexColor(std::string_view c);
std::optional<uint32_t> ParseGarminColor(std::string_view c);
std::optional<uint32_t> ParseOSMColor(std::string_view c);
PredefinedColor MapPredefinedColor(uint32_t rgba);
std::string_view MapGarminColor(uint32_t rgba);
} // namespace kml

View File

@@ -1,5 +1,6 @@
#include "testing/testing.hpp"
#include "kml/color_parser.hpp"
#include "kml/serdes_common.hpp"
#include "kml/serdes_gpx.hpp"
@@ -38,9 +39,8 @@ static std::string ReadFile(char const * testFile)
return sourceFileText;
}
static std::string ReadFileAndSerialize(char const * testFile)
static std::string Serialize(kml::FileData const & dataFromFile)
{
kml::FileData const dataFromFile = LoadGpxFromFile(testFile);
std::string resultBuffer;
MemWriter<decltype(resultBuffer)> sink(resultBuffer);
kml::gpx::SerializerGpx ser(dataFromFile);
@@ -48,6 +48,12 @@ static std::string ReadFileAndSerialize(char const * testFile)
return resultBuffer;
}
static std::string ReadFileAndSerialize(char const * testFile)
{
kml::FileData const dataFromFile = LoadGpxFromFile(testFile);
return Serialize(dataFromFile);
}
void ImportExportCompare(char const * testFile)
{
std::string const sourceFileText = ReadFile(testFile);
@@ -305,6 +311,13 @@ UNIT_TEST(Color)
TEST_EQUAL(dataFromFile.m_tracksData.size(), 3, ());
}
UNIT_TEST(ParseExportedGpxColor)
{
kml::FileData const dataFromFile = LoadGpxFromFile("test_data/gpx/point_with_predefined_color_2.gpx");
TEST_EQUAL(0x0066CCFF, dataFromFile.m_bookmarksData[0].m_color.m_rgba, ());
TEST_EQUAL(kml::PredefinedColor::Blue, dataFromFile.m_bookmarksData[0].m_color.m_predefinedColor, ());
}
UNIT_TEST(MultiTrackNames)
{
kml::FileData dataFromFile = LoadGpxFromFile("test_data/gpx/color.gpx");
@@ -323,6 +336,20 @@ UNIT_TEST(Empty)
TEST_EQUAL(0, dataFromFile.m_tracksData.size(), ());
}
UNIT_TEST(ImportExportWptColor)
{
ImportExportCompare("test_data/gpx/point_with_predefined_color_2.gpx");
}
UNIT_TEST(PointWithPredefinedColor)
{
kml::FileData dataFromFile = LoadGpxFromFile("test_data/gpx/point_with_predefined_color_1.gpx");
dataFromFile.m_bookmarksData[0].m_color.m_predefinedColor = kml::PredefinedColor::Blue;
auto const actual = Serialize(dataFromFile);
auto const expected = ReadFile("test_data/gpx/point_with_predefined_color_2.gpx");
TEST_EQUAL(expected, actual, ());
}
UNIT_TEST(OsmandColor1)
{
kml::FileData const dataFromFile = LoadGpxFromFile("test_data/gpx/osmand1.gpx");
@@ -394,11 +421,11 @@ UNIT_TEST(ParseFromString)
UNIT_TEST(MapGarminColor)
{
TEST_EQUAL("DarkCyan", kml::gpx::MapGarminColor(0x008b8bff), ());
TEST_EQUAL("White", kml::gpx::MapGarminColor(0xffffffff), ());
TEST_EQUAL("DarkYellow", kml::gpx::MapGarminColor(0xb4b820ff), ());
TEST_EQUAL("DarkYellow", kml::gpx::MapGarminColor(0xb6b820ff), ());
TEST_EQUAL("DarkYellow", kml::gpx::MapGarminColor(0xb5b721ff), ());
TEST_EQUAL("DarkCyan", kml::MapGarminColor(0x008b8bff), ());
TEST_EQUAL("White", kml::MapGarminColor(0xffffffff), ());
TEST_EQUAL("DarkYellow", kml::MapGarminColor(0xb4b820ff), ());
TEST_EQUAL("DarkYellow", kml::MapGarminColor(0xb6b820ff), ());
TEST_EQUAL("DarkYellow", kml::MapGarminColor(0xb5b721ff), ());
}
} // namespace gpx_tests

View File

@@ -80,11 +80,10 @@ bool GpxParser::MakeValid()
// Set default name.
if (m_name.empty())
m_name = kml::PointToLineString(m_org);
// Set default pin.
if (m_predefinedColor == PredefinedColor::None)
if (m_color != kInvalidColor)
m_predefinedColor = MapPredefinedColor(m_color);
else
m_predefinedColor = PredefinedColor::Red;
return true;
}
return false;
@@ -428,61 +427,6 @@ std::string GpxParser::BuildDescription() const
return m_description + "\n\n" + m_comment;
}
std::tuple<int, int, int> ExtractRGB(uint32_t color)
{
return {(color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF};
}
int ColorDistance(uint32_t color1, uint32_t color2)
{
auto const [r1, g1, b1] = ExtractRGB(color1);
auto const [r2, g2, b2] = ExtractRGB(color2);
return (r1 - r2) * (r1 - r2) + (g1 - g2) * (g1 - g2) + (b1 - b2) * (b1 - b2);
}
struct RGBAToGarmin
{
uint32_t rgba;
std::string_view color;
};
auto constexpr kRGBAToGarmin = std::to_array<RGBAToGarmin>({{0x000000ff, "Black"},
{0x8b0000ff, "DarkRed"},
{0x006400ff, "DarkGreen"},
{0xb5b820ff, "DarkYellow"},
{0x00008bff, "DarkBlue"},
{0x8b008bff, "DarkMagenta"},
{0x008b8bff, "DarkCyan"},
{0xccccccff, "LightGray"},
{0x444444ff, "DarkGray"},
{0xff0000ff, "Red"},
{0x00ff00ff, "Green"},
{0xffff00ff, "Yellow"},
{0x0000ffff, "Blue"},
{0xff00ffff, "Magenta"},
{0x00ffffff, "Cyan"},
{0xffffffff, "White"}});
std::string_view MapGarminColor(uint32_t rgba)
{
std::string_view closestColor = kRGBAToGarmin[0].color;
auto minDistance = std::numeric_limits<int>::max();
for (auto const & [rgbaGarmin, color] : kRGBAToGarmin)
{
auto const distance = ColorDistance(rgba, rgbaGarmin);
if (distance == 0)
return color; // Exact match.
if (distance < minDistance)
{
minDistance = distance;
closestColor = color;
}
}
return closestColor;
}
namespace
{
@@ -524,6 +468,16 @@ void SaveCategoryData(Writer & writer, CategoryData const & categoryData)
writer << "</metadata>\n";
}
uint32_t BookmarkColor(BookmarkData const & bookmarkData)
{
auto const & [predefinedColor, rgba] = bookmarkData.m_color;
if (rgba != kInvalidColor)
return rgba;
if (predefinedColor != PredefinedColor::None && predefinedColor != PredefinedColor::Red)
return ColorFromPredefinedColor(predefinedColor).GetRGBA();
return kInvalidColor;
}
void SaveBookmarkData(Writer & writer, BookmarkData const & bookmarkData)
{
auto const [lat, lon] = mercator::ToLatLon(bookmarkData.m_point);
@@ -544,6 +498,14 @@ void SaveBookmarkData(Writer & writer, BookmarkData const & bookmarkData)
SaveStringWithCDATA(writer, *description);
writer << "</desc>\n";
}
if (auto const color = BookmarkColor(bookmarkData); color != kInvalidColor)
{
writer << kIndent2 << "<extensions>\n";
writer << kIndent4 << "<xsi:gpx><color>#";
SaveColorToARGB(writer, color);
writer << "</color></xsi:gpx>\n";
writer << kIndent2 << "</extensions>\n";
}
writer << "</wpt>\n";
}
@@ -583,7 +545,7 @@ void SaveTrackData(Writer & writer, TrackData const & trackData)
{
writer << kIndent2 << "<extensions>\n";
writer << kIndent4 << "<gpxx:TrackExtension><gpxx:DisplayColor>";
writer << MapGarminColor(color);
writer << kml::MapGarminColor(color);
writer << "</gpxx:DisplayColor></gpxx:TrackExtension>\n";
writer << kIndent4 << "<gpx_style:line><gpx_style:color>";
SaveColorToRGB(writer, color);

View File

@@ -106,8 +106,6 @@ private:
std::string BuildDescription() const;
};
std::string_view MapGarminColor(uint32_t rgba);
} // namespace gpx
class DeserializerGpx

View File

@@ -58,6 +58,7 @@ std::string GetBookmarkIconType(kml::BookmarkIcon const & icon)
std::string const kCustomImageProperty = "CustomImage";
std::string const kHasElevationProfileProperty = "has_elevation_profile";
int constexpr kInvalidColor = 0;
} // namespace
Bookmark::Bookmark(m2::PointD const & ptOrg) : Base(ptOrg, UserMark::BOOKMARK), m_groupId(kml::kInvalidMarkGroupId)
@@ -181,10 +182,16 @@ kml::PredefinedColor Bookmark::GetColor() const
return m_data.m_color.m_predefinedColor;
}
void Bookmark::InvalidateRGBAColor()
{
m_data.m_color.m_rgba = kInvalidColor;
}
void Bookmark::SetColor(kml::PredefinedColor color)
{
SetDirty();
m_data.m_color.m_predefinedColor = color;
InvalidateRGBAColor();
}
std::string Bookmark::GetPreferredName() const

View File

@@ -40,6 +40,7 @@ public:
void SetCustomName(std::string const & customName);
kml::PredefinedColor GetColor() const;
void InvalidateRGBAColor();
void SetColor(kml::PredefinedColor color);
m2::RectD GetViewport() const;

View File

@@ -2236,7 +2236,10 @@ void BookmarkManager::UpdateBookmark(kml::MarkId bmID, kml::BookmarkData const &
ASSERT(bookmark->GetGroupId() != kml::kInvalidMarkGroupId, ());
if (prevColor != bookmark->GetColor())
{
bookmark->InvalidateRGBAColor();
SetLastEditedBmColor(bookmark->GetColor());
}
}
void BookmarkManager::ChangeTrackColor(kml::TrackId trackId, dp::Color color)

View File

@@ -371,6 +371,27 @@ UNIT_TEST(Bookmarks_Timestamp)
DeleteCategoryFiles(arrCat);
}
UNIT_TEST(Bookmarks_ChangeColorForImportedBookmark)
{
Framework fm(kFrameworkParams);
BookmarkManager & bmManager = fm.GetBookmarkManager();
bmManager.EnableTestMode(true);
auto const cat1 = bmManager.CreateBookmarkCategory("cat1", false /* autoSave */);
kml::BookmarkData bm1;
kml::SetDefaultStr(bm1.m_name, "1");
bm1.m_point = m2::PointD(38, 20);
bm1.m_color.m_predefinedColor = kml::PredefinedColor::Blue;
bm1.m_color.m_rgba = 0x0066CCFF;
auto const * pBm1 = bmManager.GetEditSession().CreateBookmark(std::move(bm1), cat1);
bm1.m_color.m_predefinedColor = kml::PredefinedColor::Orange;
bmManager.GetEditSession().UpdateBookmark(pBm1->GetId(), bm1);
bmManager.SaveBookmarkCategory(cat1);
pBm1 = bmManager.GetBookmark(pBm1->GetId());
TEST_EQUAL(pBm1->GetData().m_color.m_predefinedColor, kml::PredefinedColor::Orange, ());
TEST_EQUAL(pBm1->GetData().m_color.m_rgba, 0, ());
}
UNIT_TEST(Bookmarks_Getting)
{
Framework fm(kFrameworkParams);

View File

@@ -284,11 +284,11 @@ UNIT_TEST(MapApiGe0)
TEST_EQUAL(p0.m_name, "Zoo Zürich", ());
}
{
ParsedMapApi api("http://comaps.app/o4B4pYZsRs/Zoo_Zürich");
ParsedMapApi api("http://comaps.at/o4B4pYZsRs/Zoo_Zürich");
TEST_EQUAL(api.GetRequestType(), UrlType::Map, ());
}
{
ParsedMapApi api("https://comaps.app/o4B4pYZsRs/Zoo_Zürich");
ParsedMapApi api("https://comaps.at/o4B4pYZsRs/Zoo_Zürich");
TEST_EQUAL(api.GetRequestType(), UrlType::Map, ());
}
{
@@ -386,7 +386,7 @@ UNIT_TEST(CrosshairApi)
TEST_EQUAL(api.GetAppName(), "Google Maps", ());
}
{
ParsedMapApi api("https://comaps.app/crosshair?cll=47.3813,8.5889&appname=Google%20Maps");
ParsedMapApi api("https://comaps.at/crosshair?cll=47.3813,8.5889&appname=Google%20Maps");
TEST_EQUAL(api.GetRequestType(), UrlType::Crosshair, ());
ms::LatLon latlon = api.GetCenterLatLon();
TEST_ALMOST_EQUAL_ABS(latlon.m_lat, 47.3813, kEps, ());

View File

@@ -97,6 +97,10 @@ void Info::SetFromFeatureType(FeatureType & ft)
m_uiSubtitle = FormatSubtitle(IsFeature() /* withTypes */, !emptyTitle /* withMainType */);
auto const branch = GetMetadata(feature::Metadata::FMD_BRANCH);
if (!branch.empty())
m_uiBranch = std::string(branch);
// apply to all types after checks
m_isHotel = ftypes::IsHotelChecker::Instance()(ft);
}

View File

@@ -121,6 +121,7 @@ public:
/// For showing in UI
std::string const & GetTitle() const { return m_uiTitle; }
std::string const & GetBranch() const { return m_uiBranch; }
/// Convenient wrapper for secondary feature name.
std::string const & GetSecondaryTitle() const { return m_uiSecondaryTitle; }
/// Convenient wrapper for type, cuisines, elevation, stars, wifi etc.
@@ -220,6 +221,7 @@ private:
/// UI
std::string m_uiTitle;
std::string m_uiBranch;
std::string m_uiSubtitle;
std::string m_uiSecondaryTitle;
std::string m_uiAddress;

View File

@@ -320,7 +320,7 @@ UNIT_TEST(ChunksDownloadStrategyDynamicChunks)
{
vector<string> const servers = {"UrlOfServer1", "UrlOfServer2"};
typedef pair<int64_t, int64_t> RangeT;
typedef std::pair<int64_t, int64_t> RangeT;
string url;
ChunksDownloadStrategy strategy(servers);

View File

@@ -16,6 +16,14 @@ set(SRC
base/routing_result.hpp
base/small_list.hpp
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.hpp
checkpoint_predictor.cpp

View File

@@ -1,5 +1,6 @@
#include "routing/car_directions.hpp"
#include "routing/lanes/lanes_recommendation.hpp"
#include "routing/turns.hpp"
#include "routing/turns_generator.hpp"
#include "routing/turns_generator_utils.hpp"
@@ -83,7 +84,7 @@ void FixupCarTurns(vector<RouteSegment> & routeSegments)
routeSegments[idx - 1].ClearTurn();
}
}
SelectRecommendedLanes(routeSegments);
turns::lanes::SelectRecommendedLanes(routeSegments);
}
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;
}
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

View File

@@ -5,7 +5,6 @@
#include "routing/route.hpp"
#include "routing_common/num_mwm_id.hpp"
#include <map>
#include <memory>
#include <vector>
@@ -24,11 +23,6 @@ protected:
virtual void FixupTurns(std::vector<RouteSegment> & routeSegments);
};
/*!
* \brief Selects lanes which are recommended for an end user.
*/
void SelectRecommendedLanes(std::vector<RouteSegment> & routeSegments);
void FixupCarTurns(std::vector<RouteSegment> & routeSegments);
/*!

View File

@@ -2,6 +2,7 @@
#include "routing/data_source.hpp"
#include "routing/fake_feature_ids.hpp"
#include "routing/lanes/lanes_parser.hpp"
#include "routing/routing_helpers.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)
{
auto tag = GetLanesMetadataTag(ft, isForward);
ParseLanes(std::string(ft.GetMetadata(tag)), pathSegment.m_lanes);
pathSegment.m_lanes = lanes::ParseLanes(ft.GetMetadata(tag));
}
} // namespace

View File

@@ -199,7 +199,7 @@ EdgeEstimator::EdgeEstimator(VehicleType vehicleType, double maxWeightSpeedKMpH,
double penalty;
};
#define N 144
#define N 288
static auto constexpr kTurnPenaltyMatrix = []
{
@@ -348,6 +348,150 @@ EdgeEstimator::EdgeEstimator(VehicleType vehicleType, double maxWeightSpeedKMpH,
{HighwayType::HighwayUnclassified, HighwayType::HighwayTrunk, VehicleType::Car, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayTrunkLink, VehicleType::Car, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayUnclassified, VehicleType::Car, 0.08},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayLivingStreet, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayLivingStreet, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayLivingStreet, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimary, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.11},
{HighwayType::HighwayPrimary, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimary, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimary, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayPrimary, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimary, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimary, HighwayType::HighwayService, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayPrimary, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayPrimary, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimary, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayPrimary, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayPrimary, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayService, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayPrimaryLink, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayResidential, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayResidential, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayResidential, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayResidential, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayResidential, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayResidential, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayResidential, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayResidential, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayResidential, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayResidential, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayResidential, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayResidential, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.1},
{HighwayType::HighwaySecondary, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.1},
{HighwayType::HighwaySecondary, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondary, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondary, HighwayType::HighwayService, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondary, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondary, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.06},
{HighwayType::HighwaySecondary, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.07},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.05},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.05},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayService, VehicleType::Bicycle, 0.06},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.05},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.05},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwaySecondaryLink, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayService, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayService, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayService, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayService, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTertiary, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTertiary, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiary, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTertiaryLink, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTrunk, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayTrunk, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.05},
{HighwayType::HighwayTrunk, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.03},
{HighwayType::HighwayTrunk, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayTrunk, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunk, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunk, HighwayType::HighwayService, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunk, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunk, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunk, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.01},
{HighwayType::HighwayTrunk, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.01},
{HighwayType::HighwayTrunk, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.11},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayTrunkLink, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayTrunkLink, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.04},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayService, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.06},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.02},
{HighwayType::HighwayTrunkLink, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.1},
{HighwayType::HighwayUnclassified, HighwayType::HighwayLivingStreet, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayUnclassified, HighwayType::HighwayPrimary, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayPrimaryLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayResidential, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayUnclassified, HighwayType::HighwaySecondary, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayUnclassified, HighwayType::HighwaySecondaryLink, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayUnclassified, HighwayType::HighwayService, VehicleType::Bicycle, 0.08},
{HighwayType::HighwayUnclassified, HighwayType::HighwayTertiary, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayUnclassified, HighwayType::HighwayTertiaryLink, VehicleType::Bicycle, 0.07},
{HighwayType::HighwayUnclassified, HighwayType::HighwayTrunk, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayTrunkLink, VehicleType::Bicycle, 0.09},
{HighwayType::HighwayUnclassified, HighwayType::HighwayUnclassified, VehicleType::Bicycle, 0.08},
}};
array<TurnPenaltyMatrix, N> result{};
@@ -507,10 +651,7 @@ public:
double GetUTurnPenalty(Purpose /* purpose */) const override { return 20.0 /* seconds */; }
double GetTurnPenalty(Purpose purpose, double angle, RoadGeometry const & from_road, RoadGeometry const & to_road,
bool is_left_hand_traffic = false) const override
{
return 0;
}
bool is_left_hand_traffic = false) const override;
double GetFerryLandingPenalty(Purpose purpose) const override
{
@@ -557,6 +698,35 @@ public:
}
};
double BicycleEstimator::GetTurnPenalty(Purpose purpose, double angle, RoadGeometry const & from_road,
RoadGeometry const & to_road, bool is_left_hand_traffic) const
{
auto penalty = m_defaultPenalty;
if (from_road.GetHighwayType().has_value() && to_road.GetHighwayType().has_value())
{
int const from_road_idx = static_cast<int>(from_road.GetHighwayType().value());
int const to_road_idx = static_cast<int>(to_road.GetHighwayType().value());
auto const pen = m_turnPenaltyMap.find(from_road_idx * 65535 + to_road_idx);
if (pen != m_turnPenaltyMap.end())
penalty = pen->second;
}
// Determine if turn crosses traffic based on driving side
// For bicycles, crossing traffic is less dangerous than for cars but still a factor
bool turn_crosses_traffic;
if (is_left_hand_traffic)
turn_crosses_traffic = angle < 0;
else
turn_crosses_traffic = angle > 0;
// Penalty multiplier for bicycles crossing traffic
auto const extra_penalty = turn_crosses_traffic ? 2.0 : 1.0;
auto const result = fabs(angle) * penalty * extra_penalty;
return result;
}
// CarEstimator ------------------------------------------------------------------------------------
class CarEstimator final : public EdgeEstimator
{

View File

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

View File

@@ -182,11 +182,11 @@ bool ReadSpeedCamsFromMwm(MwmValue const & mwmValue, SpeedCamerasMapT & camerasM
}
catch (Reader::OpenException const &)
{
LOG(LWARNING, (CAMERAS_INFO_FILE_TAG "section not found"));
LOG(LWARNING, (CAMERAS_INFO_FILE_TAG "section not found in", mwmValue.GetCountryFileName()));
}
catch (Reader::Exception const & e)
{
LOG(LERROR, ("Error while reading", CAMERAS_INFO_FILE_TAG, "section.", e.Msg()));
LOG(LERROR, ("Error while reading", CAMERAS_INFO_FILE_TAG, "section in", mwmValue.GetCountryFileName(), ":", e.Msg()));
}
return false;
}
@@ -202,11 +202,11 @@ bool ReadRoadAccessFromMwm(MwmValue const & mwmValue, VehicleType vehicleType, R
}
catch (Reader::OpenException const &)
{
LOG(LWARNING, (ROAD_ACCESS_FILE_TAG, "section not found"));
LOG(LWARNING, (ROAD_ACCESS_FILE_TAG, "section not found in", mwmValue.GetCountryFileName()));
}
catch (Reader::Exception const & e)
{
LOG(LERROR, ("Error while reading", ROAD_ACCESS_FILE_TAG, "section.", e.Msg()));
LOG(LERROR, ("Error while reading", ROAD_ACCESS_FILE_TAG, "section in", mwmValue.GetCountryFileName(), ":", e.Msg()));
}
return false;
}
@@ -236,11 +236,11 @@ bool ReadRoadPenaltyFromMwm(MwmValue const & mwmValue, VehicleType vehicleType,
catch (Reader::OpenException const &)
{
// This is expected for older mwm files - not an error
LOG(LDEBUG, (ROAD_PENALTY_FILE_TAG, "section not found - using legacy penalty system"));
LOG(LINFO, (ROAD_PENALTY_FILE_TAG, "section not found in", mwmValue.GetCountryFileName(), "- using legacy penalty system"));
}
catch (Reader::Exception const & e)
{
LOG(LERROR, ("Error while reading", ROAD_PENALTY_FILE_TAG, "section.", e.Msg()));
LOG(LERROR, ("Error while reading", ROAD_PENALTY_FILE_TAG, "section in", mwmValue.GetCountryFileName(), ":", e.Msg()));
}
return false;
}

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

View File

@@ -1,5 +1,6 @@
#pragma once
#include "routing/lanes/lane_info.hpp"
#include "routing/routing_options.hpp"
#include "routing/routing_settings.hpp"
#include "routing/segment.hpp"
@@ -139,7 +140,7 @@ public:
void SetTurnExits(uint32_t exitNum) { m_turn.m_exitNum = exitNum; }
std::vector<turns::SingleLaneInfo> & GetTurnLanes() { return m_turn.m_lanes; }
turns::lanes::LanesInfo & GetTurnLanes() { return m_turn.m_lanes; }
void SetDistancesAndTime(double distFromBeginningMeters, double distFromBeginningMerc, double timeFromBeginningS)
{

View File

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

View File

@@ -1,6 +1,8 @@
project(routing_tests)
set(SRC
lanes/lanes_parser_tests.cpp
lanes/lanes_recommendation_tests.cpp
applying_traffic_test.cpp
astar_algorithm_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 <cmath>
#include <string>
#include <vector>
@@ -65,118 +64,6 @@ private:
TUnpackedPathSegments m_segments;
};
UNIT_TEST(TestSplitLanes)
{
vector<string> result;
SplitLanes("through|through|through|through;right", '|', result);
vector<string> const expected1 = {"through", "through", "through", "through;right"};
TEST_EQUAL(result, expected1, ());
SplitLanes("adsjkddfasui8747&sxdsdlad8\"\'", '|', result);
TEST_EQUAL(result, vector<string>({"adsjkddfasui8747&sxdsdlad8\"\'"}), ());
SplitLanes("|||||||", '|', result);
vector<string> expected2 = {"", "", "", "", "", "", ""};
TEST_EQUAL(result, expected2, ());
}
UNIT_TEST(TestParseSingleLane)
{
TSingleLane result;
TEST(ParseSingleLane("through;right", ';', result), ());
TSingleLane const expected1 = {LaneWay::Through, LaneWay::Right};
TEST_EQUAL(result, expected1, ());
TEST(!ParseSingleLane("through;Right", ';', result), ());
TEST(!ParseSingleLane("through ;right", ';', result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(!ParseSingleLane("SD32kk*887;;", ';', result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(!ParseSingleLane("Что-то на кириллице", ';', result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(!ParseSingleLane("משהו בעברית", ';', result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(ParseSingleLane("left;through", ';', result), ());
TSingleLane expected2 = {LaneWay::Left, LaneWay::Through};
TEST_EQUAL(result, expected2, ());
TEST(ParseSingleLane("left", ';', result), ());
TEST_EQUAL(result.size(), 1, ());
TEST_EQUAL(result[0], LaneWay::Left, ());
TEST(ParseSingleLane("left;", ';', result), ());
TSingleLane expected3 = {LaneWay::Left, LaneWay::None};
TEST_EQUAL(result, expected3, ());
TEST(ParseSingleLane(";", ';', result), ());
TSingleLane expected4 = {LaneWay::None, LaneWay::None};
TEST_EQUAL(result, expected4, ());
TEST(ParseSingleLane("", ';', result), ());
TSingleLane expected5 = {LaneWay::None};
TEST_EQUAL(result, expected5, ());
}
UNIT_TEST(TestParseLanes)
{
vector<SingleLaneInfo> result;
TEST(ParseLanes("through|through|through|through;right", result), ());
vector<SingleLaneInfo> const expected1 = {
{LaneWay::Through}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Through, LaneWay::Right}};
TEST_EQUAL(result, expected1, ());
TEST(ParseLanes("left|left;through|through|through", result), ());
vector<SingleLaneInfo> const expected2 = {
{LaneWay::Left}, {LaneWay::Left, LaneWay::Through}, {LaneWay::Through}, {LaneWay::Through}};
TEST_EQUAL(result, expected2, ());
TEST(ParseLanes("left|through|through", result), ());
vector<SingleLaneInfo> const expected3 = {{LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}};
TEST_EQUAL(result, expected3, ());
TEST(ParseLanes("left|le ft| through|through | right", result), ());
vector<SingleLaneInfo> const expected4 = {
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
TEST_EQUAL(result, expected4, ());
TEST(ParseLanes("left|Left|through|througH|right", result), ());
vector<SingleLaneInfo> const expected5 = {
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
TEST_EQUAL(result, expected5, ());
TEST(ParseLanes("left|Left|through|througH|through;right;sharp_rIght", result), ());
vector<SingleLaneInfo> const expected6 = {{LaneWay::Left},
{LaneWay::Left},
{LaneWay::Through},
{LaneWay::Through},
{LaneWay::Through, LaneWay::Right, LaneWay::SharpRight}};
TEST_EQUAL(result, expected6, ());
TEST(!ParseLanes("left|Leftt|through|througH|right", result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(!ParseLanes("Что-то на кириллице", result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(!ParseLanes("משהו בעברית", result), ());
TEST_EQUAL(result.size(), 0, ());
TEST(ParseLanes("left |Left|through|througH|right", result), ());
vector<SingleLaneInfo> const expected7 = {
{LaneWay::Left}, {LaneWay::Left}, {LaneWay::Through}, {LaneWay::Through}, {LaneWay::Right}};
TEST_EQUAL(result, expected7, ());
TEST(ParseLanes("|||||slight_right", result), ());
vector<SingleLaneInfo> const expected8 = {{LaneWay::None}, {LaneWay::None}, {LaneWay::None},
{LaneWay::None}, {LaneWay::None}, {LaneWay::SlightRight}};
TEST_EQUAL(result, expected8, ());
}
UNIT_TEST(TestFixupTurns)
{
double const kHalfSquareSideMeters = 10.;
@@ -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)
{
// The signature of GetRoundaboutDirection function is

View File

@@ -5,8 +5,6 @@
#include "platform/country_file.hpp"
#include "base/internal/message.hpp"
#include "base/stl_helpers.hpp"
#include "base/string_utils.hpp"
#include <algorithm>
#include <array>
@@ -21,28 +19,6 @@ using namespace std;
namespace
{
/// The order is important. Starting with the most frequent tokens according to
/// taginfo.openstreetmap.org we minimize the number of the comparisons in ParseSingleLane().
///
/// A `none` lane can be represented either as "none" or as "". That means both "none" and ""
/// should be considered names, even though they refer to the same thing. As a result,
/// `LaneWay::None` appears twice in this array, which is one longer than the number of
/// enum values.
array<pair<LaneWay, char const *>, static_cast<size_t>(LaneWay::Count) + 1> const g_laneWayNames = {
{{LaneWay::None, ""},
{LaneWay::Through, "through"},
{LaneWay::Left, "left"},
{LaneWay::Right, "right"},
{LaneWay::None, "none"},
{LaneWay::SharpLeft, "sharp_left"},
{LaneWay::SlightLeft, "slight_left"},
{LaneWay::MergeToRight, "merge_to_right"},
{LaneWay::MergeToLeft, "merge_to_left"},
{LaneWay::SlightRight, "slight_right"},
{LaneWay::SharpRight, "sharp_right"},
{LaneWay::Reverse, "reverse"}}};
static_assert(g_laneWayNames.size() == static_cast<size_t>(LaneWay::Count) + 1, "Check the size of g_laneWayNames");
array<pair<CarDirection, char const *>, static_cast<size_t>(CarDirection::Count)> const g_turnNames = {
{{CarDirection::None, "None"},
{CarDirection::GoStraight, "GoStraight"},
@@ -162,12 +138,6 @@ string DebugPrint(SegmentRange const & segmentRange)
namespace turns
{
// SingleLaneInfo ---------------------------------------------------------------------------------
bool SingleLaneInfo::operator==(SingleLaneInfo const & other) const
{
return m_lane == other.m_lane && m_isRecommended == other.m_isRecommended;
}
string DebugPrint(TurnItem const & turnItem)
{
stringstream out;
@@ -232,118 +202,6 @@ bool IsGoStraightOrSlightTurn(CarDirection t)
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)
{
return GetTurnString(turn);
@@ -368,14 +226,6 @@ string DebugPrint(PedestrianDirection const l)
return "unknown PedestrianDirection";
}
string DebugPrint(SingleLaneInfo const & singleLaneInfo)
{
stringstream out;
out << "SingleLaneInfo [ m_isRecommended == " << singleLaneInfo.m_isRecommended
<< ", m_lane == " << ::DebugPrint(singleLaneInfo.m_lane) << " ]" << endl;
return out.str();
}
double PiMinusTwoVectorsAngle(m2::PointD const & junctionPoint, m2::PointD const & ingoingPoint,
m2::PointD const & outgoingPoint)
{

View File

@@ -1,5 +1,6 @@
#pragma once
#include "routing/lanes/lane_info.hpp"
#include "routing/segment.hpp"
#include "routing_common/num_mwm_id.hpp"
@@ -8,7 +9,6 @@
#include "geometry/point2d.hpp"
#include <initializer_list>
#include <limits>
#include <string>
#include <vector>
@@ -118,41 +118,6 @@ enum class PedestrianDirection
std::string DebugPrint(PedestrianDirection const l);
/*!
* \warning The values of LaneWay shall be synchronized with values of LaneWay enum in java.
*/
enum class LaneWay
{
None = 0,
Reverse,
SharpLeft,
Left,
SlightLeft,
MergeToRight,
Through,
MergeToLeft,
SlightRight,
Right,
SharpRight,
Count /**< This value is used for internals only. */
};
std::string DebugPrint(LaneWay const l);
typedef std::vector<LaneWay> TSingleLane;
struct SingleLaneInfo
{
TSingleLane m_lane;
bool m_isRecommended = false;
SingleLaneInfo() = default;
SingleLaneInfo(std::initializer_list<LaneWay> const & l) : m_lane(l) {}
bool operator==(SingleLaneInfo const & other) const;
};
std::string DebugPrint(SingleLaneInfo const & singleLaneInfo);
struct TurnItem
{
TurnItem()
@@ -192,7 +157,7 @@ struct TurnItem
uint32_t m_index; /*!< Index of point on route polyline (Index of segment + 1). */
CarDirection m_turn = CarDirection::None; /*!< The turn instruction of the TurnItem */
std::vector<SingleLaneInfo> m_lanes; /*!< Lane information on the edge before the turn. */
lanes::LanesInfo m_lanes; /*!< Lane information on the edge before the turn. */
uint32_t m_exitNum; /*!< Number of exit on roundabout. */
/*!
* \brief m_pedestrianTurn is type of corresponding direction for a pedestrian, or None
@@ -223,39 +188,6 @@ bool IsTurnMadeFromLeft(CarDirection t);
bool IsTurnMadeFromRight(CarDirection t);
bool IsStayOnRoad(CarDirection t);
bool IsGoStraightOrSlightTurn(CarDirection t);
/*!
* \param l A variant of going along a lane.
* \param t A turn direction.
* \return True if @l corresponds with @t exactly. For example it returns true
* when @l equals to LaneWay::Right and @t equals to TurnDirection::TurnRight.
* Otherwise it returns false.
*/
bool IsLaneWayConformedTurnDirection(LaneWay l, CarDirection t);
/*!
* \param l A variant of going along a lane.
* \param t A turn direction.
* \return True if @l corresponds with @t approximately. For example it returns true
* when @l equals to LaneWay::Right and @t equals to TurnDirection::TurnSlightRight.
* Otherwise it returns false.
*/
bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, CarDirection t);
bool IsLaneUnrestricted(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]
* to vector [junctionPoint, outgoingPoint]. A counterclockwise rotation.

View File

@@ -115,11 +115,16 @@ private:
static bool HouseNumbersMatch(FeatureType & feature, std::vector<house_numbers::Token> const & queryParse)
{
ASSERT(!queryParse.empty(), ());
auto const interpol = ftypes::IsAddressInterpolChecker::Instance().GetInterpolType(feature);
if (interpol != feature::InterpolType::None)
return house_numbers::HouseNumbersMatchRange(feature.GetRef(), queryParse, interpol);
auto const uniHouse = strings::MakeUniString(feature.GetHouseNumber());
if (uniHouse.empty())
return false;
if (feature.GetID().IsEqualCountry({"Czech", "Slovakia"}))
return house_numbers::HouseNumbersMatchConscription(uniHouse, queryParse);
@@ -327,6 +332,9 @@ private:
if (std::binary_search(buildings.begin(), buildings.end(), houseId))
return true;
if (!child.m_hasDelayedFeatures || queryParse.empty())
return false;
if (m_postcodes && !m_postcodes->HasBit(houseId) && !m_postcodes->HasBit(streetId))
return false;
@@ -334,9 +342,6 @@ private:
if (!feature)
return false;
if (!child.m_hasDelayedFeatures)
return false;
return HouseNumbersMatch(*feature, queryParse);
};
@@ -384,6 +389,8 @@ private:
std::vector<house_numbers::Token> queryParse;
ParseQuery(child.m_subQuery, child.m_lastTokenIsPrefix, queryParse);
if (queryParse.empty())
return;
uint32_t numFilterInvocations = 0;
auto const houseNumberFilter = [&](uint32_t houseId)

View File

@@ -47,9 +47,19 @@ void HighlightResult(QueryTokens const & tokens, strings::UniString const & pref
CombinedIter beg(tokens.begin(), tokens.end(), prefix.empty() ? nullptr : &prefix);
CombinedIter end(tokens.end() /* cur */, tokens.end() /* end */, nullptr);
// Highlight Title
// Highlight Title (potentially including branch)
std::string titleForHighlighting = res.GetString();
std::string const & branch = res.GetBranch();
// If we have a branch that is not already in the title, create combined string for highlighting
// This matches the iOS UI behavior where branch is appended as " branchText"
if (!branch.empty() && titleForHighlighting.find(branch) == std::string::npos)
{
titleForHighlighting += " " + branch;
}
SearchStringTokensIntersectionRanges(
res.GetString(), beg, end, [&](std::pair<uint16_t, uint16_t> const & range) { res.AddHighlightRange(range); });
titleForHighlighting, beg, end, [&](std::pair<uint16_t, uint16_t> const & range) { res.AddHighlightRange(range); });
// Highlight description.
SearchStringTokensIntersectionRanges(res.GetAddress(), beg, end, [&](std::pair<uint16_t, uint16_t> const & range)

View File

@@ -516,8 +516,7 @@ void ParseQuery(UniString const & query, bool queryIsPrefix, TokensT & parse)
bool HouseNumbersMatch(UniString const & houseNumber, TokensT const & queryParse)
{
if (houseNumber.empty() || queryParse.empty())
return false;
ASSERT(!houseNumber.empty() && !queryParse.empty(), ());
// Fast pre-check, helps to early exit without complex house number parsing.
if (IsASCIIDigit(houseNumber[0]) && IsASCIIDigit(queryParse[0].m_value[0]) &&
@@ -559,7 +558,7 @@ bool HouseNumbersMatchConscription(UniString const & houseNumber, TokensT const
bool HouseNumbersMatchRange(std::string_view const & hnRange, TokensT const & queryParse,
feature::InterpolType interpol)
{
ASSERT(interpol != feature::InterpolType::None, ());
ASSERT(!queryParse.empty() && interpol != feature::InterpolType::None, ());
if (queryParse[0].m_type != Token::TYPE_NUMBER)
return false;

View File

@@ -19,17 +19,37 @@ unordered_map<string, vector<string>> const kSynonyms = {
/// @todo Should process synonyms with errors like "blvrd" -> "blvd".
/// @see HouseOnStreetSynonymsWithMisprints test.
{"1", {"pierwszy", "pierwsza", "un", "una", "pierwsze", "primo"}},
{"1st", {"first"}},
{"", {"primo"}},
{"20", {"venti"}},
{"25", {"venticinque"}},
{"2", {"due", "drugi", "drugie", "druga"}},
{"2nd", {"second"}},
{"3", {"trzecia", "trzecie", "tre", "trzeci"}},
{"3rd", {"third"}},
{"4", {"quattro"}},
{"4th", {"fourth"}},
{"5", {"cinque"}},
{"5th", {"fifth"}},
{"6", {"sei"}},
{"6th", {"sixth"}},
{"7", {"sette"}},
{"7th", {"seventh"}},
{"8", {"otto"}},
{"8th", {"eighth"}},
{"9", {"nove"}},
{"9th", {"ninth"}},
{"10th", {"tenth"}},
{"11th", {"eleventh"}},
{"12th", {"twelfth"}},
{"13th", {"thirteenth"}},
{"14th", {"fourteenth"}},
{"15th", {"fifteenth"}},
{"16th", {"sixteenth"}},
{"17th", {"seventeenth"}},
{"18th", {"eighteenth"}},
{"19th", {"nineteenth"}},
{"20th", {"twentieth"}},
{"a", {"am", "auto-estrada"}},
{"abe", {"abbaye"}},
{"accs", {"access"}},
@@ -108,7 +128,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"bast", {"baston"}},
{"bat", {"batalionu", "batalion", "batalionów", "bataliony"}},
{"bayer", {"bayerische", "bayerisch", "bayerischer", "bayerisches"}},
{"b", {"berg", "barri", "bach", "barriada", "banda", "bairro", "bad"}},
{"b", {"berg", "barri", "bach", "barriada", "banda", "bairro", "bad", "bei"}},
{"bc", {"beco"}},
{"bch", {"bas chemin", "beach"}},
{"bcle", {"boucle"}},
@@ -187,7 +207,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"cal", {"calea"}},
{"cale", {"cale"}},
{"callej", {"callejón"}},
{"cam", {"caminho"}},
{"cam", {"caminho", "camino"}},
{"campg", {"camping"}},
{"cant", {"cantón", "cantonada"}},
{"canti", {"cantina"}},
@@ -203,11 +223,13 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"cav", {"cavée"}},
{"cbtiz", {"cobertizo"}},
{"cc", {"calçada", "centro comercial"}},
{"call", {"calleja", "callejón"}},
{"c", {"cesta", "carrer", "calle", "centre"}},
{"cckq", {"căn cứ không quân"}},
{"ccnh", {"calçadinha"}},
{"cct", {"circuit"}},
{"ccvcn", {"circunvalación"}},
{"cad", {"cadde"}},
{"cd", {"cadde", "caddesi"}},
{"", {"cao đẳng"}},
{"cds", {"cul-de-sac"}},
@@ -318,6 +340,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"cstan", {"costanilla"}},
{"cst", {"castel", "crest"}},
{"cswy", {"causeway"}},
{"cta", {"cuesta"}},
{"ct", {"court", "công trường"}},
{"ctcp", {"công ty cổ phần"}},
{"ctra", {"carretera"}},
@@ -395,7 +418,10 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"egl", {"église"}},
{"égl", {"église"}},
{"ehem", {"ehemalige", "ehemaliger", "ehemaliges"}},
{"eighth", {"8th"}},
{"eighteenth", {"18th"}},
{"elb", {"elbow"}},
{"eleventh", {"11th"}},
{"emb", {"embaixador"}},
{"em", {"estrada municipal"}},
{"empr", {"empresa"}},
@@ -456,6 +482,9 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"fg", {"faubourg"}},
{"fh", {"fachhochschule", "forsthaus"}},
{"fit", {"firetrail"}},
{"fifth", {"5th"}},
{"fifteenth", {"15th"}},
{"first", {"1st"}},
{"flat", {"flat"}},
{"fl", {"flat"}},
{"fnd", {"fundătura"}},
@@ -463,6 +492,8 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"fon", {"fontaine"}},
{"form", {"forum", "formation"}},
{"fos", {"fosses", "fosse"}},
{"fourth", {"4th"}},
{"fourteenth", {"14th"}},
{"foyr", {"foyer"}},
{"fr", {"father", "frontage", "frei"}},
{"frm", {"ferme", "fermes"}},
@@ -735,6 +766,8 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"ne", {"northeast"}},
{"nh", {"nhà hát"}},
{"nhs", {"national high school"}},
{"ninth", {"9th"}},
{"nineteenth", {"19th"}},
{"nk", {"nook"}},
{"nms", {"neue mittelschule"}},
{"n", {"nowy", "nowa", "nossa", "norra", "número", "north", "nosso", "nacional", "nowe"}},
@@ -823,6 +856,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"pim", {"petite impasse"}},
{"pi", {"polígon industrial"}},
{"pj", {"polícia judiciária"}},
{"pje", {"pasaje"}},
{"pkg", {"parking"}},
{"pkld", {"parklands"}},
{"pko", {"puistikko"}},
@@ -860,7 +894,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"port", {"port"}},
{"pot", {"poterne"}},
{"pour", {"pourtour"}},
{"p", {"pfad", "polku", "phường", "pater"}},
{"p", {"pfad", "polku", "phường", "pater", "paseo"}},
{"pplk", {"podplukovníka"}},
{"ppłk", {"ppułkownika", "podpułkownika", "podpułkownik"}},
{"ppor", {"podporučíka"}},
@@ -974,7 +1008,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"roc", {"roc", "rocade"}},
{"roqt", {"roquet"}},
{"rotm", {"rotmistrza", "rotmistrz"}},
{"rot", {"rotunda"}},
{"rot", {"rotunda", "rotonda"}},
{"row", {"row"}},
{"rowy", {"right of way"}},
{"rpe", {"rampe"}},
@@ -1016,11 +1050,14 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"sdn", {"sekolah dasar negeri"}},
{"sdr", {"søndre", "sønder"}},
{"sd", {"strand", "sekolah dasar"}},
{"second", {"2nd"}},
{"sect", {"sector"}},
{"sedra", {"sendera"}},
{"send", {"sendero"}},
{"sen", {"sente", "senator", "sentiers", "sentier", "sentes"}},
{"sent", {"sentier"}},
{"seventh", {"7th"}},
{"seventeenth", {"17th"}},
{"se", {"southeast"}},
{"sgda", {"sagrada"}},
{"sg", {"schottergrube", "sungai", "sandgrube"}},
@@ -1030,6 +1067,8 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"shun", {"shunt"}},
{"shwy", {"state highway"}},
{"sídl", {"sídlisko"}},
{"sixth", {"6th"}},
{"sixteenth", {"16th"}},
{"sk", {"sokak", "sokağı"}},
{"slj", {"sobre-loja"}},
{"slov", {"slovenskih"}},
@@ -1085,7 +1124,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
/// Remove "street" and "avenue" here, but should update GetNameScore.
{"st",
{"santo", "sant", "sint", "saint", "stara", "street", "stary", "stora", "sankt", "store", "stare", "stig",
"stigen"}},
"stigen", "sancta"}},
{"št", {"šent"}},
{"stwg", {"steenweg"}},
{"subdiv", {"subdivision"}},
@@ -1103,12 +1142,15 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"tct", {"tổng công ty"}},
{"tcty", {"tổng công ty"}},
{"ten", {"tenente"}},
{"tenth", {"10th"}},
{"term", {"terminal"}},
{"terr", {"terrace"}},
{"ter", {"terrace"}},
{"tg", {"tanjong"}},
{"thcs", {"trung học cơ sở"}},
{"thfr", {"thoroughfare"}},
{"third", {"3rd"}},
{"thirteenth", {"13th"}},
{"thor", {"thoroughfare"}},
{"thpt", {"trung học phổ thông"}},
{"th", {"tiểu học", "tienhaara"}},
@@ -1150,6 +1192,8 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"tu", {"technische universität"}},
{"tvl", {"taival"}},
{"tv", {"travessa"}},
{"twelfth", {"12th"}},
{"twentieth", {"20th"}},
{"twp", {"township"}},
{"twrs", {"towers"}},
{"twy", {"tollway"}},
@@ -1256,7 +1300,7 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"yd", {"yard"}},
{"zac", {"zone d'aménagement concerté"}},
{"zad", {"zone d'aménagement différé"}},
{"za", {"zone artisanale"}},
{"za", {"zone artisanale", "zone d'activité"}},
{"zd", {"zdravstveni dom"}},
{"zg", {"zgornja", "zgornji", "zgornje"}},
{"zi", {"zone industrielle"}},
@@ -1358,7 +1402,9 @@ unordered_map<string, vector<string>> const kSynonyms = {
{"", {"高等学校"}},
{"ش", {"شارع"}},
{"ط", {"طريق"}},
{"م", {"ميدان"}}
{"م", {"ميدان"}},
{"د", {"دكتور"}},
{"ج", {"جامعة"}}
/*
These synonyms are currently unreachable per https://codeberg.org/comaps/comaps/issues/1242
{"arm. gen", {"armádneho generála"}},

View File

@@ -732,6 +732,16 @@ Result Ranker::MakeResult(RankerResult const & rankerResult, bool needAddress, b
case RankerResult::Type::Building:
res.FromFeature(rankerResult.GetID(), rankerResult.GetBestType(),
rankerResult.GetBestType(&m_params.m_preferredTypes), rankerResult.m_details);
// Extract branch metadata if available
if (rankerResult.GetID().IsValid())
{
m_dataSource.ReadFeature([&](FeatureType & ft) {
auto const branch = ft.GetMetadata(feature::Metadata::FMD_BRANCH);
if (!branch.empty())
res.SetBranch(std::string(branch));
}, rankerResult.GetID());
}
break;
case RankerResult::Type::LatLon: res.SetType(Result::Type::LatLon); break;
case RankerResult::Type::Postcode: res.SetType(Result::Type::Postcode); break;

View File

@@ -62,6 +62,7 @@ public:
void FromFeature(FeatureID const & id, uint32_t mainType, uint32_t matchedType, Details const & details);
void SetAddress(std::string && address) { m_address = std::move(address); }
void SetBranch(std::string && branch) { m_branch = std::move(branch); }
void SetType(Result::Type type) { m_resultType = type; }
// For Type::PureSuggest.
@@ -75,6 +76,7 @@ public:
std::string const & GetString() const { return m_str; }
std::string const & GetAddress() const { return m_address; }
std::string const & GetDescription() const { return m_details.m_description; }
std::string const & GetBranch() const { return m_branch; }
osm::YesNoUnknown IsOpenNow() const { return m_details.m_isOpenNow; }
uint16_t GetMinutesUntilOpen() const { return m_details.m_minutesUntilOpen; }
@@ -152,6 +154,7 @@ private:
m2::PointD m_center;
std::string m_str;
std::string m_address;
std::string m_branch;
uint32_t m_mainType = 0;
uint32_t m_matchedType = 0;
std::string m_suggestionStr;

View File

@@ -26,6 +26,8 @@ set(shader_files
GL/debug_rect.vsh.glsl
GL/hatching_area.fsh.glsl
GL/hatching_area.vsh.glsl
GL/imgui.fsh.glsl
GL/imgui.vsh.glsl
GL/line.fsh.glsl
GL/line.vsh.glsl
GL/masked_texturing.fsh.glsl
@@ -41,7 +43,9 @@ set(shader_files
GL/route_dash.fsh.glsl
GL/route_marker.fsh.glsl
GL/route_marker.vsh.glsl
GL/ruler.fsh.glsl
GL/ruler.vsh.glsl
GL/screen_quad.fsh.glsl
GL/screen_quad.vsh.glsl
GL/selection_line.fsh.glsl
GL/selection_line.vsh.glsl
@@ -58,12 +62,15 @@ set(shader_files
GL/text_billboard.vsh.glsl
GL/text_outlined.vsh.glsl
GL/text_outlined_billboard.vsh.glsl
GL/text_outlined_gui.fsh.glsl
GL/text_outlined_gui.vsh.glsl
GL/texturing.fsh.glsl
GL/texturing.vsh.glsl
GL/texturing3d.fsh.glsl
GL/texturing_billboard.vsh.glsl
GL/texturing_gui.fsh.glsl
GL/texturing_gui.vsh.glsl
GL/texturing_position.fsh.glsl
GL/traffic.fsh.glsl
GL/traffic.vsh.glsl
GL/traffic_circle.fsh.glsl

View File

@@ -50,12 +50,10 @@ class GLTypeWrapper;
BIND_GL_TYPE(float, gl_const::GLFloatType)
BIND_GL_TYPE(glsl::vec2, gl_const::GLFloatVec2)
BIND_GL_TYPE(glsl::vec3, gl_const::GLFloatVec4)
BIND_GL_TYPE(glsl::vec4, gl_const::GLFloatVec4)
BIND_GL_TYPE(glsl::mat4, gl_const::GLFloatMat4)
BIND_GL_TYPE(int, gl_const::GLIntType)
BIND_GL_TYPE(glsl::ivec2, gl_const::GLIntVec2)
BIND_GL_TYPE(glsl::ivec3, gl_const::GLIntVec4)
BIND_GL_TYPE(glsl::ivec4, gl_const::GLIntVec4)
class Parameter

View File

@@ -1,13 +1,5 @@
project(shaders_tests)
execute_process(
COMMAND rm -rf ${CMAKE_BINARY_DIR}/shaders_compiler
)
execute_process(
COMMAND cp -r ${OMIM_ROOT}/tools/shaders_compiler ${CMAKE_BINARY_DIR}/shaders_compiler
)
set(SRC
gl_shaders_desktop_compile_tests.cpp
gl_program_params_tests.cpp