Merge pull request #5919 from mpimenov/road-access-serialization

[routing] [generator] Better road access.
This commit is contained in:
burivuh 2017-04-28 20:20:47 +03:00 committed by GitHub
commit 2e4605f378
18 changed files with 638 additions and 138 deletions

View file

@ -7,25 +7,34 @@
#include "generator/road_access_generator.hpp"
#include "routing/road_access_serialization.hpp"
#include "routing/segment.hpp"
#include "platform/country_file.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "indexer/classificator_loader.hpp"
#include "geometry/point2d.hpp"
#include "coding/file_container.hpp"
#include "coding/file_name_utils.hpp"
#include "base/logging.hpp"
#include "base/string_utils.hpp"
#include <map>
#include <string>
#include <vector>
using namespace feature;
using namespace generator::tests_support;
using namespace generator;
using namespace platform::tests_support;
using namespace platform;
using namespace routing;
using std::string;
using namespace std;
namespace
{
@ -34,12 +43,23 @@ string const kTestMwm = "test";
string const kRoadAccessFilename = "road_access_in_osm_ids.csv";
string const kOsmIdsToFeatureIdsName = "osm_ids_to_feature_ids" OSM2FEATURE_FILE_EXTENSION;
void BuildEmptyMwm(LocalCountryFile & country)
void BuildTestMwmWithRoads(LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::country);
for (size_t i = 0; i < 10; ++i)
{
string const name = "road " + strings::to_string(i);
string const lang = "en";
vector<m2::PointD> points;
for (size_t j = 0; j < 10; ++j)
points.emplace_back(static_cast<double>(i), static_cast<double>(j));
builder.Add(TestRoad(points, name, lang));
}
}
void LoadRoadAccess(string const & mwmFilePath, RoadAccess & roadAccess)
void LoadRoadAccess(string const & mwmFilePath, VehicleType vehicleType, RoadAccess & roadAccess)
{
FilesContainerR const cont(mwmFilePath);
TEST(cont.IsExist(ROAD_ACCESS_FILE_TAG), ());
@ -49,7 +69,7 @@ void LoadRoadAccess(string const & mwmFilePath, RoadAccess & roadAccess)
FilesContainerR::TReader const reader = cont.GetReader(ROAD_ACCESS_FILE_TAG);
ReaderSource<FilesContainerR::TReader> src(reader);
RoadAccessSerializer::Deserialize(src, roadAccess);
RoadAccessSerializer::Deserialize(src, vehicleType, roadAccess);
}
catch (Reader::OpenException const & e)
{
@ -58,8 +78,11 @@ void LoadRoadAccess(string const & mwmFilePath, RoadAccess & roadAccess)
}
// todo(@m) This helper function is almost identical to the one in restriction_test.cpp.
void TestRoadAccess(string const & roadAccessContent, string const & mappingContent)
RoadAccessCollector::RoadAccessByVehicleType SaveAndLoadRoadAccess(string const & roadAccessContent,
string const & mappingContent)
{
classificator::Load();
Platform & platform = GetPlatform();
string const writableDir = platform.WritableDir();
@ -69,7 +92,7 @@ void TestRoadAccess(string const & roadAccessContent, string const & mappingCont
ScopedDir const scopedDir(kTestDir);
string const mwmRelativePath = my::JoinPath(kTestDir, kTestMwm + DATA_FILE_EXTENSION);
ScopedFile const scopedMwm(mwmRelativePath);
BuildEmptyMwm(country);
BuildTestMwmWithRoads(country);
// Creating a file with road access.
string const roadAccessRelativePath = my::JoinPath(kTestDir, kRoadAccessFilename);
@ -87,37 +110,54 @@ void TestRoadAccess(string const & roadAccessContent, string const & mappingCont
BuildRoadAccessInfo(mwmFullPath, roadAccessFullPath, mappingFullPath);
// Reading from mwm section and testing road access.
RoadAccess roadAccessFromMwm;
LoadRoadAccess(mwmFullPath, roadAccessFromMwm);
RoadAccessCollector const collector(roadAccessFullPath, mappingFullPath);
RoadAccessCollector::RoadAccessByVehicleType roadAccessFromMwm;
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
{
auto const vehicleType = static_cast<VehicleType>(i);
LoadRoadAccess(mwmFullPath, vehicleType, roadAccessFromMwm[i]);
}
RoadAccessCollector const collector(mwmFullPath, roadAccessFullPath, mappingFullPath);
TEST(collector.IsValid(), ());
TEST_EQUAL(roadAccessFromMwm, collector.GetRoadAccess(), ());
TEST_EQUAL(roadAccessFromMwm, collector.GetRoadAccessAllTypes(), ());
return roadAccessFromMwm;
}
UNIT_TEST(RoadAccess_Smoke)
{
string const roadAccessContent = "";
string const osmIdsToFeatureIdsContent = "";
TestRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
}
UNIT_TEST(RoadAccess_AccessPrivate)
{
string const roadAccessContent = R"(access=private 0)";
string const roadAccessContent = R"(Car Private 0)";
string const osmIdsToFeatureIdsContent = R"(0, 0,)";
TestRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const roadAccessAllTypes =
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const carRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Car)];
TEST_EQUAL(carRoadAccess.GetSegmentType(Segment(0, 0, 0, false)), RoadAccess::Type::Private, ());
}
UNIT_TEST(RoadAccess_Access_And_Barriers)
UNIT_TEST(RoadAccess_Access_Multiple_Vehicle_Types)
{
string const roadAccessContent = R"(access=private 10
access=private 20
barrier=gate 30
barrier=gate 40)";
string const roadAccessContent = R"(Car Private 10
Car Private 20
Bicycle No 30
Car Destination 40)";
string const osmIdsToFeatureIdsContent = R"(10, 1,
20, 2,
30, 3,
40, 4,)";
TestRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const roadAccessAllTypes =
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const carRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Car)];
auto const bicycleRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Bicycle)];
TEST_EQUAL(carRoadAccess.GetSegmentType(Segment(0, 1, 0, false)), RoadAccess::Type::Private, ());
TEST_EQUAL(carRoadAccess.GetSegmentType(Segment(0, 2, 2, true)), RoadAccess::Type::Private, ());
TEST_EQUAL(carRoadAccess.GetSegmentType(Segment(0, 3, 1, true)), RoadAccess::Type::Yes, ());
TEST_EQUAL(carRoadAccess.GetSegmentType(Segment(0, 4, 3, false)), RoadAccess::Type::Destination,
());
TEST_EQUAL(bicycleRoadAccess.GetSegmentType(Segment(0, 3, 0, false)), RoadAccess::Type::No, ());
}
} // namespace

View file

@ -289,6 +289,32 @@ string TestPark::ToString() const
return os.str();
}
// TestRoad ----------------------------------------------------------------------------------------
TestRoad::TestRoad(vector<m2::PointD> const & points, string const & name, string const & lang)
: TestFeature(name, lang), m_points(points)
{
}
void TestRoad::Serialize(FeatureBuilder1 & fb) const
{
TestFeature::Serialize(fb);
auto const & classificator = classif();
fb.AddType(classificator.GetTypeByPath({"highway", "road"}));
for (auto const & point : m_points)
fb.AddPoint(point);
fb.SetLinear(false /* reverseGeometry */);
}
string TestRoad::ToString() const
{
ostringstream os;
os << "TestRoad [" << m_name << ", " << m_lang << "]";
return os.str();
}
// Functions ---------------------------------------------------------------------------------------
string DebugPrint(TestFeature const & feature) { return feature.ToString(); }
} // namespace tests_support
} // namespace generator

View file

@ -150,6 +150,19 @@ private:
vector<m2::PointD> m_boundary;
};
class TestRoad : public TestFeature
{
public:
TestRoad(vector<m2::PointD> const & points, string const & name, string const & lang);
// TestFeature overrides:
void Serialize(FeatureBuilder1 & fb) const override;
string ToString() const override;
private:
vector<m2::PointD> m_points;
};
string DebugPrint(TestFeature const & feature);
} // namespace tests_support
} // namespace generator

View file

@ -312,7 +312,7 @@ class OsmToFeatureTranslator
if (!params.IsValid())
return false;
m_routingTagsProcessor.m_roadAccessWriter.Process(*p, params);
m_routingTagsProcessor.m_roadAccessWriter.Process(*p);
return true;
}

View file

@ -1,6 +1,5 @@
#include "generator/road_access_generator.hpp"
#include "generator/osm_element.hpp"
#include "generator/osm_id.hpp"
#include "generator/routing_helpers.hpp"
@ -8,7 +7,9 @@
#include "routing/road_access_serialization.hpp"
#include "indexer/classificator.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_data.hpp"
#include "indexer/features_vector.hpp"
#include "coding/file_container.hpp"
#include "coding/file_writer.hpp"
@ -21,23 +22,39 @@
#include "defines.hpp"
#include <algorithm>
#include <fstream>
#include <map>
#include <ostream>
#include <string>
#include <vector>
using namespace routing;
using namespace std;
namespace
{
char constexpr kAccessPrivate[] = "access=private";
char constexpr kBarrierGate[] = "barrier=gate";
char constexpr kDelim[] = " \t\r\n";
bool ParseRoadAccess(string const & roadAccessPath,
map<osm::Id, uint32_t> const & osmIdToFeatureId,
routing::RoadAccess & roadAccess)
using TagMapping = routing::RoadAccessTagProcessor::TagMapping;
TagMapping const kCarTagMapping = {
{OsmElement::Tag("access", "no"), RoadAccess::Type::No},
{OsmElement::Tag("vehicle", "no"), RoadAccess::Type::No},
{OsmElement::Tag("access", "private"), RoadAccess::Type::Private},
{OsmElement::Tag("access", "destination"), RoadAccess::Type::Destination},
};
TagMapping const kPedestrianTagMapping = {
{OsmElement::Tag("access", "no"), RoadAccess::Type::No},
{OsmElement::Tag("foot", "no"), RoadAccess::Type::No},
};
TagMapping const kBicycleTagMapping = {
{OsmElement::Tag("access", "no"), RoadAccess::Type::No},
{OsmElement::Tag("bicycle", "no"), RoadAccess::Type::No},
};
bool ParseRoadAccess(string const & roadAccessPath, map<osm::Id, uint32_t> const & osmIdToFeatureId,
FeaturesVector const & featuresVector,
RoadAccessCollector::RoadAccessByVehicleType & roadAccessByVehicleType)
{
ifstream stream(roadAccessPath);
if (!stream)
@ -48,6 +65,16 @@ bool ParseRoadAccess(string const & roadAccessPath,
vector<uint32_t> privateRoads;
map<Segment, RoadAccess::Type> segmentType[static_cast<size_t>(VehicleType::Count)];
auto addSegment = [&](Segment const & segment, VehicleType vehicleType,
RoadAccess::Type roadAccessType, uint64_t osmId) {
auto & m = segmentType[static_cast<size_t>(vehicleType)];
auto const emplaceRes = m.emplace(segment, roadAccessType);
if (!emplaceRes.second)
LOG(LERROR, ("Duplicate road access info for", osmId));
};
string line;
for (uint32_t lineNo = 1;; ++lineNo)
{
@ -58,32 +85,46 @@ bool ParseRoadAccess(string const & roadAccessPath,
if (!iter)
{
LOG(LWARNING, ("Error when parsing road access: empty line", lineNo));
LOG(LERROR, ("Error when parsing road access: empty line", lineNo));
return false;
}
VehicleType vehicleType;
FromString(*iter, vehicleType);
++iter;
string const s = *iter;
if (!iter)
{
LOG(LERROR, ("Error when parsing road access: no road access type", lineNo));
return false;
}
RoadAccess::Type roadAccessType;
FromString(*iter, roadAccessType);
++iter;
uint64_t osmId;
if (!iter || !strings::to_uint64(*iter, osmId))
{
LOG(LWARNING, ("Error when parsing road access: bad osm id at line", lineNo));
LOG(LERROR, ("Error when parsing road access: bad osm id at line", lineNo));
return false;
}
++iter;
auto const it = osmIdToFeatureId.find(osm::Id::Way(osmId));
// Even though this osm element has a tag that is interesting for us,
// we have not created a feature from it. Possible reasons:
// no primary tag, unsupported type, etc.
if (it == osmIdToFeatureId.cend())
{
LOG(LWARNING, ("Error when parsing road access: unknown osm id", osmId, "at line", lineNo));
return false;
}
continue;
uint32_t const featureId = it->second;
privateRoads.emplace_back(featureId);
addSegment(Segment(kFakeNumMwmId, featureId, 0 /* wildcard segment idx */,
true /* wildcard isForward */),
vehicleType, roadAccessType, osmId);
}
roadAccess.SetPrivateRoads(move(privateRoads));
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
roadAccessByVehicleType[i].SetSegmentTypes(move(segmentType[i]));
return true;
}
@ -91,7 +132,41 @@ bool ParseRoadAccess(string const & roadAccessPath,
namespace routing
{
// RoadAccessTagProcessor --------------------------------------------------------------------------
RoadAccessTagProcessor::RoadAccessTagProcessor(VehicleType vehicleType)
: m_vehicleType(vehicleType), m_tagMapping(nullptr)
{
switch (vehicleType)
{
case VehicleType::Car: m_tagMapping = &kCarTagMapping; break;
case VehicleType::Pedestrian: m_tagMapping = &kPedestrianTagMapping; break;
case VehicleType::Bicycle: m_tagMapping = &kBicycleTagMapping; break;
case VehicleType::Count: CHECK(false, ("Bad vehicle type")); break;
}
}
void RoadAccessTagProcessor::Process(OsmElement const & elem, ofstream & oss) const
{
// todo(@m) Add support for non-way elements, such as barrier=gate.
if (elem.type != OsmElement::EntityType::Way)
return;
for (auto const & tag : elem.m_tags)
{
auto const it = m_tagMapping->find(tag);
if (it == m_tagMapping->cend())
continue;
oss << ToString(m_vehicleType) << " " << ToString(it->second) << " " << elem.id << endl;
}
}
// RoadAccessWriter ------------------------------------------------------------
RoadAccessWriter::RoadAccessWriter()
{
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
m_tagProcessors.emplace_back(static_cast<VehicleType>(i));
}
void RoadAccessWriter::Open(string const & filePath)
{
LOG(LINFO,
@ -102,7 +177,7 @@ void RoadAccessWriter::Open(string const & filePath)
LOG(LINFO, ("Cannot open file", filePath));
}
void RoadAccessWriter::Process(OsmElement const & elem, FeatureParams const & params)
void RoadAccessWriter::Process(OsmElement const & elem)
{
if (!IsOpened())
{
@ -110,28 +185,14 @@ void RoadAccessWriter::Process(OsmElement const & elem, FeatureParams const & pa
return;
}
auto const & c = classif();
StringIL const forbiddenRoadTypes[] = {
{"hwtag", "private"}
};
for (auto const & f : forbiddenRoadTypes)
{
auto const t = c.GetTypeByPath(f);
if (params.IsTypeExist(t) && elem.type == OsmElement::EntityType::Way)
m_stream << kAccessPrivate << " " << elem.id << endl;
}
auto t = c.GetTypeByPath({"barrier", "gate"});
if (params.IsTypeExist(t))
m_stream << kBarrierGate << " " << elem.id << endl;
for (auto const & p : m_tagProcessors)
p.Process(elem, m_stream);
}
bool RoadAccessWriter::IsOpened() const { return m_stream && m_stream.is_open(); }
// RoadAccessCollector ----------------------------------------------------------
RoadAccessCollector::RoadAccessCollector(string const & roadAccessPath,
RoadAccessCollector::RoadAccessCollector(string const & dataFilePath, string const & roadAccessPath,
string const & osmIdsToFeatureIdsPath)
{
map<osm::Id, uint32_t> osmIdToFeatureId;
@ -143,8 +204,11 @@ RoadAccessCollector::RoadAccessCollector(string const & roadAccessPath,
return;
}
RoadAccess roadAccess;
if (!ParseRoadAccess(roadAccessPath, osmIdToFeatureId, roadAccess))
FeaturesVectorTest featuresVector(dataFilePath);
RoadAccessCollector::RoadAccessByVehicleType roadAccessByVehicleType;
if (!ParseRoadAccess(roadAccessPath, osmIdToFeatureId, featuresVector.GetVector(),
roadAccessByVehicleType))
{
LOG(LWARNING, ("An error happened while parsing road access from file:", roadAccessPath));
m_valid = false;
@ -152,7 +216,7 @@ RoadAccessCollector::RoadAccessCollector(string const & roadAccessPath,
}
m_valid = true;
m_roadAccess.Swap(roadAccess);
m_roadAccessByVehicleType.swap(roadAccessByVehicleType);
}
// Functions ------------------------------------------------------------------
@ -161,7 +225,7 @@ void BuildRoadAccessInfo(string const & dataFilePath, string const & roadAccessP
{
LOG(LINFO, ("Generating road access info for", dataFilePath));
RoadAccessCollector collector(roadAccessPath, osmIdsToFeatureIdsPath);
RoadAccessCollector collector(dataFilePath, roadAccessPath, osmIdsToFeatureIdsPath);
if (!collector.IsValid())
{
@ -172,6 +236,6 @@ void BuildRoadAccessInfo(string const & dataFilePath, string const & roadAccessP
FilesContainerW cont(dataFilePath, FileWriter::OP_WRITE_EXISTING);
FileWriter writer = cont.GetWriter(ROAD_ACCESS_FILE_TAG);
RoadAccessSerializer::Serialize(writer, collector.GetRoadAccess());
RoadAccessSerializer::Serialize(writer, collector.GetRoadAccessAllTypes());
}
} // namespace routing

View file

@ -1,12 +1,18 @@
#pragma once
#include "generator/intermediate_elements.hpp"
#include "generator/osm_element.hpp"
#include "routing/road_access.hpp"
#include "routing/vehicle_mask.hpp"
#include <array>
#include <cstdint>
#include <fstream>
#include <map>
#include <ostream>
#include <string>
#include <vector>
struct OsmElement;
class FeatureParams;
@ -16,30 +22,53 @@ class FeatureParams;
// See generator/restriction_generator.hpp for details.
namespace routing
{
class RoadAccessTagProcessor
{
public:
using TagMapping = std::map<OsmElement::Tag, RoadAccess::Type>;
explicit RoadAccessTagProcessor(VehicleType vehicleType);
void Process(OsmElement const & elem, std::ofstream & oss) const;
private:
VehicleType m_vehicleType;
TagMapping const * m_tagMapping;
};
class RoadAccessWriter
{
public:
RoadAccessWriter();
void Open(std::string const & filePath);
void Process(OsmElement const & elem, FeatureParams const & params);
void Process(OsmElement const & elem);
private:
bool IsOpened() const;
std::ofstream m_stream;
std::vector<RoadAccessTagProcessor> m_tagProcessors;
};
class RoadAccessCollector
{
public:
RoadAccessCollector(std::string const & roadAccessPath, std::string const & osmIdsToFeatureIdsPath);
using RoadAccessByVehicleType = std::array<RoadAccess, static_cast<size_t>(VehicleType::Count)>;
RoadAccess const & GetRoadAccess() const { return m_roadAccess; }
RoadAccessCollector(std::string const & dataFilePath, std::string const & roadAccessPath,
std::string const & osmIdsToFeatureIdsPath);
RoadAccessByVehicleType const & GetRoadAccessAllTypes() const
{
return m_roadAccessByVehicleType;
}
bool IsValid() const { return m_valid; }
private:
RoadAccess m_roadAccess;
RoadAccessByVehicleType m_roadAccessByVehicleType;
bool m_valid = true;
};

View file

@ -41,12 +41,9 @@ namespace routing
{
void AddFeatureId(osm::Id osmId, uint32_t featureId, map<osm::Id, uint32_t> &osmIdToFeatureId)
{
auto const result = osmIdToFeatureId.insert(make_pair(osmId, featureId));
if (!result.second)
{
LOG(LERROR, ("Osm id", osmId, "is included in two feature ids:", featureId,
osmIdToFeatureId.find(osmId)->second));
}
// Failing to insert here usually means that two features were created
// from one osm id, for example an area and its boundary.
osmIdToFeatureId.insert(make_pair(osmId, featureId));
}
bool ParseOsmIdToFeatureIdMapping(string const & osmIdsToFeatureIdPath,

View file

@ -2585,6 +2585,8 @@ void Framework::BuildRoute(m2::PointD const & start, m2::PointD const & finish,
case RouterType::Taxi:
tag = isP2P ? marketing::kRoutingP2PTaxiDiscovered : marketing::kRoutingTaxiDiscovered;
break;
case RouterType::Count:
CHECK(false, ("Bad router type", m_currentRouterType));
}
GetPlatform().GetMarketingService().SendPushWooshTag(tag);
}
@ -2862,9 +2864,11 @@ RouterType Framework::GetBestRouter(m2::PointD const & startPoint, m2::PointD co
case RouterType::Bicycle:
return lastUsedRouter;
case RouterType::Taxi:
ASSERT(false, ("GetLastUsedRouter sould not to return RouterType::Taxi"));
ASSERT(false, ("GetLastUsedRouter should not return RouterType::Taxi"));
case RouterType::Vehicle:
; // fall through
break;
case RouterType::Count:
CHECK(false, ("Bad router type", lastUsedRouter));
}
// Return on a short distance the vehicle router flag only if we are already have routing files.

View file

@ -127,6 +127,7 @@ set(
turns_sound_settings.hpp
turns_tts_text.cpp
turns_tts_text.hpp
vehicle_mask.cpp
vehicle_mask.hpp
world_graph.cpp
world_graph.hpp

View file

@ -1,26 +1,92 @@
#include "routing/road_access.hpp"
#include "base/assert.hpp"
#include <algorithm>
#include <sstream>
using namespace std;
namespace
{
string const kNames[] = {"No", "Private", "Destination", "Yes", "Count"};
} // namespace
namespace routing
{
std::string DebugPrint(RoadAccess const & r)
// RoadAccess --------------------------------------------------------------------------------------
RoadAccess::Type const RoadAccess::GetSegmentType(Segment const & segment) const
{
// todo(@m) This may or may not be too slow. Consider profiling this and using
// a Bloom filter or anything else that is faster than std::map.
{
Segment key(kFakeNumMwmId, segment.GetFeatureId(), 0 /* wildcard segment idx */,
true /* wildcard isForward */);
auto const it = m_segmentTypes.find(key);
if (it != m_segmentTypes.end())
return it->second;
}
{
Segment key(kFakeNumMwmId, segment.GetFeatureId(), segment.GetSegmentIdx() + 1,
segment.IsForward());
auto const it = m_segmentTypes.find(key);
if (it != m_segmentTypes.end())
return it->second;
}
return RoadAccess::Type::Yes;
}
bool RoadAccess::operator==(RoadAccess const & rhs) const
{
return m_segmentTypes == rhs.m_segmentTypes;
}
// Functions ---------------------------------------------------------------------------------------
string ToString(RoadAccess::Type type)
{
if (type <= RoadAccess::Type::Count)
return kNames[static_cast<size_t>(type)];
ASSERT(false, ("Bad road access type", static_cast<size_t>(type)));
return "Bad RoadAccess::Type";
}
void FromString(string const & s, RoadAccess::Type & result)
{
for (size_t i = 0; i <= static_cast<size_t>(RoadAccess::Type::Count); ++i)
{
if (s == kNames[i])
{
result = static_cast<RoadAccess::Type>(i);
return;
}
}
result = RoadAccess::Type::Count;
ASSERT(false, ("Could not read RoadAccess from the string", s));
}
string DebugPrint(RoadAccess::Type type) { return ToString(type); }
string DebugPrint(RoadAccess const & r)
{
size_t const kMaxIdsToShow = 10;
std::ostringstream oss;
oss << "RoadAccess: Private roads [";
auto const & privateRoads = r.GetPrivateRoads();
for (size_t i = 0; i < privateRoads.size(); ++i)
ostringstream oss;
oss << "RoadAccess [";
size_t id = 0;
for (auto const & kv : r.GetSegmentTypes())
{
if (i == kMaxIdsToShow)
{
oss << "...";
if (id > 0)
oss << ", ";
oss << DebugPrint(kv.first) << " " << DebugPrint(kv.second);
++id;
if (id == kMaxIdsToShow)
break;
}
if (i > 0)
oss << " ";
oss << privateRoads[i];
}
if (r.GetSegmentTypes().size() > kMaxIdsToShow)
oss << ", ...";
oss << "]";
return oss.str();
}

View file

@ -1,32 +1,74 @@
#pragma once
#include "routing/segment.hpp"
#include "routing/vehicle_mask.hpp"
#include "base/assert.hpp"
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace routing
{
// This class provides information about road access classes.
// For now, only restrictive types (such as barrier=gate and access=private)
// and only car routing are supported.
// One instance of RoadAccess holds information about one
// mwm and one router type (also known as VehicleType).
class RoadAccess final
{
public:
std::vector<uint32_t> const & GetPrivateRoads() const { return m_privateRoads; }
// The road access types are selected by analyzing the most
// popular tags used when mapping roads in OSM.
enum class Type : uint8_t
{
// Moving through the road is prohibited.
No,
// Moving through the road requires a special permission.
Private,
// No transit through the road is allowed; however, it can
// be used if it is close enough to the destination point
// of the route.
Destination,
// No restrictions, as in "access=yes".
Yes,
// The number of different road types.
Count
};
std::map<Segment, RoadAccess::Type> const & GetSegmentTypes() const { return m_segmentTypes; }
Type const GetSegmentType(Segment const & segment) const;
template <typename V>
void SetPrivateRoads(V && v) { m_privateRoads = std::forward<V>(v); }
void SetSegmentTypes(V && v)
{
m_segmentTypes = std::forward<V>(v);
}
void Clear() { m_privateRoads.clear(); }
void Clear();
void Swap(RoadAccess & rhs) { m_privateRoads.swap(rhs.m_privateRoads); }
void Swap(RoadAccess & rhs);
bool operator==(RoadAccess const & rhs) const { return m_privateRoads == rhs.m_privateRoads; }
bool operator==(RoadAccess const & rhs) const;
private:
// Feature ids of blocked features in the corresponding mwm.
std::vector<uint32_t> m_privateRoads;
// todo(@m) Segment's NumMwmId is not used here. Decouple it from
// segment and use only (fid, idx, forward) in the map.
//
// If segmentIdx of a key in this map is 0, it means the
// entire feature has the corresponding access type.
// Otherwise, the information is about the segment with number (segmentIdx-1).
std::map<Segment, RoadAccess::Type> m_segmentTypes;
};
std::string ToString(RoadAccess::Type type);
void FromString(std::string const & s, RoadAccess::Type & result);
std::string DebugPrint(RoadAccess::Type type);
std::string DebugPrint(RoadAccess const & r);
} // namespace routing

View file

@ -1,7 +1,12 @@
#pragma once
#include "routing/coding.hpp"
#include "routing/num_mwm_id.hpp"
#include "routing/road_access.hpp"
#include "routing/segment.hpp"
#include "routing/vehicle_mask.hpp"
#include "coding/bit_streams.hpp"
#include "coding/reader.hpp"
#include "coding/varint.hpp"
#include "coding/write_to_sink.hpp"
@ -10,7 +15,9 @@
#include "base/checked_cast.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <map>
#include <vector>
namespace routing
@ -18,44 +25,169 @@ namespace routing
class RoadAccessSerializer final
{
public:
using RoadAccessTypesMap = std::map<Segment, RoadAccess::Type>;
using RoadAccessByVehicleType = std::array<RoadAccess, static_cast<size_t>(VehicleType::Count)>;
RoadAccessSerializer() = delete;
template <class Sink>
static void Serialize(Sink & sink, RoadAccess const & roadAccess)
static void Serialize(Sink & sink, RoadAccessByVehicleType const & roadAccessByType)
{
uint32_t const header = kLatestVersion;
WriteToSink(sink, header);
auto const & privateRoads = roadAccess.GetPrivateRoads();
ASSERT(std::is_sorted(privateRoads.begin(), privateRoads.end()), ());
WriteToSink(sink, base::checked_cast<uint32_t>(privateRoads.size()));
if (!privateRoads.empty())
WriteVarUint(sink, privateRoads[0]);
for (size_t i = 1; i < privateRoads.size(); ++i)
auto const sectionSizesPos = sink.Pos();
std::array<uint32_t, static_cast<size_t>(VehicleType::Count)> sectionSizes;
for (size_t i = 0; i < sectionSizes.size(); ++i)
{
uint32_t const delta = privateRoads[i] - privateRoads[i - 1];
WriteVarUint(sink, delta);
sectionSizes[i] = 0;
WriteToSink(sink, sectionSizes[i]);
}
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
{
auto const pos = sink.Pos();
SerializeOneVehicleType(sink, roadAccessByType[i].GetSegmentTypes());
sectionSizes[i] = base::checked_cast<uint32_t>(sink.Pos() - pos);
}
auto const endPos = sink.Pos();
sink.Seek(sectionSizesPos);
for (size_t i = 0; i < sectionSizes.size(); ++i)
WriteToSink(sink, sectionSizes[i]);
sink.Seek(endPos);
}
template <class Source>
static void Deserialize(Source & src, RoadAccess & roadAccess)
static void Deserialize(Source & src, VehicleType vehicleType, RoadAccess & roadAccess)
{
uint32_t const header = ReadPrimitiveFromSource<uint32_t>(src);
CHECK_EQUAL(header, kLatestVersion, ());
size_t numPrivateRoads = base::checked_cast<size_t>(ReadPrimitiveFromSource<uint32_t>(src));
std::vector<uint32_t> privateRoads(numPrivateRoads);
if (numPrivateRoads > 0)
privateRoads[0] = ReadVarUint<uint32_t>(src);
for (size_t i = 1; i < numPrivateRoads; ++i)
std::array<uint32_t, static_cast<size_t>(VehicleType::Count)> sectionSizes;
for (size_t i = 0; i < sectionSizes.size(); ++i)
sectionSizes[i] = ReadPrimitiveFromSource<uint32_t>(src);
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
{
uint32_t delta = ReadVarUint<uint32_t>(src);
privateRoads[i] = privateRoads[i - 1] + delta;
if (vehicleType != static_cast<VehicleType>(i))
{
src.Skip(sectionSizes[i]);
continue;
}
RoadAccessTypesMap m;
DeserializeOneVehicleType(src, m);
roadAccess.SetSegmentTypes(std::move(m));
}
roadAccess.SetPrivateRoads(move(privateRoads));
}
private:
template <typename Sink>
static void SerializeOneVehicleType(Sink & sink, RoadAccessTypesMap const & m)
{
std::array<std::vector<Segment>, static_cast<size_t>(RoadAccess::Type::Count)>
segmentsByRoadAccessType;
for (auto const & kv : m)
segmentsByRoadAccessType[static_cast<size_t>(kv.second)].push_back(kv.first);
for (auto & segs : segmentsByRoadAccessType)
{
std::sort(segs.begin(), segs.end());
SerializeSegments(sink, segs);
}
}
template <typename Source>
static void DeserializeOneVehicleType(Source & src, RoadAccessTypesMap & m)
{
m.clear();
for (size_t i = 0; i < static_cast<size_t>(RoadAccess::Type::Count); ++i)
{
std::vector<Segment> segs;
DeserializeSegments(src, segs);
for (auto const & seg : segs)
m[seg] = static_cast<RoadAccess::Type>(i);
}
}
// todo(@m) This code borrows heavily from traffic/traffic_info.hpp:SerializeTrafficKeys.
template <typename Sink>
static void SerializeSegments(Sink & sink, std::vector<Segment> const & segments)
{
std::vector<uint32_t> featureIds(segments.size());
std::vector<uint32_t> segmentIndices(segments.size());
std::vector<bool> isForward(segments.size());
for (size_t i = 0; i < segments.size(); ++i)
{
auto const & seg = segments[i];
CHECK_EQUAL(seg.GetMwmId(), kFakeNumMwmId,
("Numeric mwm ids are temporary and must not be serialized."));
featureIds[i] = seg.GetFeatureId();
segmentIndices[i] = seg.GetSegmentIdx();
isForward[i] = seg.IsForward();
}
WriteVarUint(sink, segments.size());
{
BitWriter<Sink> bitWriter(sink);
uint32_t prevFid = 0;
for (auto const fid : featureIds)
{
CHECK_GREATER_OR_EQUAL(fid, prevFid, ());
uint64_t const fidDiff = static_cast<uint64_t>(fid - prevFid);
WriteGamma(bitWriter, fidDiff + 1);
prevFid = fid;
}
for (auto const idx : segmentIndices)
WriteGamma(bitWriter, idx + 1);
for (auto const val : isForward)
bitWriter.Write(val ? 1 : 0, 1 /* numBits */);
}
}
template <typename Source>
static void DeserializeSegments(Source & src, std::vector<Segment> & segments)
{
auto const n = static_cast<size_t>(ReadVarUint<uint64_t>(src));
std::vector<uint32_t> featureIds(n);
std::vector<uint32_t> segmentIndices(n);
std::vector<bool> isForward(n);
BitReader<Source> bitReader(src);
uint32_t prevFid = 0;
for (size_t i = 0; i < n; ++i)
{
prevFid += ReadGamma<uint64_t>(bitReader) - 1;
featureIds[i] = prevFid;
}
for (size_t i = 0; i < n; ++i)
segmentIndices[i] = ReadGamma<uint32_t>(bitReader) - 1;
for (size_t i = 0; i < n; ++i)
isForward[i] = bitReader.Read(1) > 0;
// Read the padding bits.
auto bitsRead = bitReader.BitsRead();
while (bitsRead % CHAR_BIT != 0)
{
bitReader.Read(1);
++bitsRead;
}
segments.clear();
segments.reserve(n);
for (size_t i = 0; i < n; ++i)
segments.emplace_back(kFakeNumMwmId, featureIds[i], segmentIndices[i], isForward[i]);
}
uint32_t static const kLatestVersion;
};
} // namespace routing

View file

@ -6,10 +6,11 @@ std::string ToString(RouterType type)
{
switch(type)
{
case RouterType::Vehicle: return "Vehicle";
case RouterType::Pedestrian: return "Pedestrian";
case RouterType::Bicycle: return "Bicycle";
case RouterType::Taxi: return "Taxi";
case RouterType::Vehicle: return "vehicle";
case RouterType::Pedestrian: return "pedestrian";
case RouterType::Bicycle: return "bicycle";
case RouterType::Taxi: return "taxi";
case RouterType::Count: return "count";
}
ASSERT(false, ());
return "Error";
@ -29,4 +30,6 @@ RouterType FromString(std::string const & str)
ASSERT(false, ("Incorrect routing string:", str));
return RouterType::Vehicle;
}
std::string DebugPrint(RouterType type) { return ToString(type); }
} // namespace routing

View file

@ -22,14 +22,16 @@ class Route;
enum class RouterType
{
// @TODO It's necessary to rename Vehicle value to Car.
Vehicle = 0, /// For Car routing (OSRM or AStar)
Pedestrian, /// For A star pedestrian routing
Bicycle, /// For A star bicycle routing
Vehicle = 0, /// For Car routing (OSRM or AStar).
Pedestrian, /// For A star pedestrian routing.
Bicycle, /// For A star bicycle routing.
Taxi, /// For taxi route calculation Vehicle routing is used.
Count /// Number of router types.
};
std::string ToString(RouterType type);
RouterType FromString(std::string const & str);
std::string DebugPrint(RouterType type);
class IRouter
{

View file

@ -65,6 +65,7 @@ SOURCES += \
turns_notification_manager.cpp \
turns_sound_settings.cpp \
turns_tts_text.cpp \
vehicle_mask.cpp \
world_graph.cpp \
HEADERS += \

View file

@ -9,6 +9,7 @@
#include <algorithm>
#include <cstdint>
#include <map>
#include <vector>
using namespace routing;
@ -19,28 +20,53 @@ namespace
{
UNIT_TEST(RoadAccess_Serialization)
{
vector<uint32_t> privateRoads = {1, 2, 3, 100};
RoadAccess roadAccess;
roadAccess.SetPrivateRoads(move(privateRoads));
// Segment is (numMwmId, featureId, segmentIdx, isForward).
map<Segment, RoadAccess::Type> const m0 = {
{Segment(kFakeNumMwmId, 1, 0, false), RoadAccess::Type::No},
{Segment(kFakeNumMwmId, 2, 2, false), RoadAccess::Type::Private},
};
map<Segment, RoadAccess::Type> const m1 = {
{Segment(kFakeNumMwmId, 1, 1, false), RoadAccess::Type::Private},
{Segment(kFakeNumMwmId, 2, 0, true), RoadAccess::Type::Destination},
};
RoadAccess roadAccessCar;
roadAccessCar.SetSegmentTypes(m0);
RoadAccess roadAccessPedestrian;
roadAccessPedestrian.SetSegmentTypes(m1);
RoadAccessSerializer::RoadAccessByVehicleType roadAccessAllTypes;
roadAccessAllTypes[static_cast<size_t>(VehicleType::Car)] = roadAccessCar;
roadAccessAllTypes[static_cast<size_t>(VehicleType::Pedestrian)] = roadAccessPedestrian;
vector<uint8_t> buf;
{
MemWriter<decltype(buf)> writer(buf);
RoadAccessSerializer::Serialize(writer, roadAccess);
RoadAccessSerializer::Serialize(writer, roadAccessAllTypes);
}
RoadAccess deserializedRoadAccess;
{
RoadAccess deserializedRoadAccess;
MemReader memReader(buf.data(), buf.size());
ReaderSource<MemReader> src(memReader);
RoadAccessSerializer::Deserialize(src, deserializedRoadAccess);
RoadAccessSerializer::Deserialize(src, VehicleType::Car, deserializedRoadAccess);
TEST_EQUAL(src.Size(), 0, ());
TEST_EQUAL(roadAccessCar, deserializedRoadAccess, ());
}
TEST_EQUAL(roadAccess, deserializedRoadAccess, ());
{
auto const & b = deserializedRoadAccess.GetPrivateRoads();
TEST(is_sorted(b.begin(), b.end()), ());
RoadAccess deserializedRoadAccess;
MemReader memReader(buf.data(), buf.size());
ReaderSource<MemReader> src(memReader);
RoadAccessSerializer::Deserialize(src, VehicleType::Pedestrian, deserializedRoadAccess);
TEST_EQUAL(src.Size(), 0, ());
TEST_EQUAL(roadAccessPedestrian, deserializedRoadAccess, ());
}
}
} // namespace

60
routing/vehicle_mask.cpp Normal file
View file

@ -0,0 +1,60 @@
#include "routing/vehicle_mask.hpp"
#include <sstream>
#include <string>
#include "base/assert.hpp"
using namespace std;
namespace routing
{
string DebugPrint(VehicleType vehicleType)
{
switch (vehicleType)
{
case VehicleType::Pedestrian: return "Pedestrian";
case VehicleType::Bicycle: return "Bicycle";
case VehicleType::Car: return "Car";
case VehicleType::Count: return "Count";
}
}
string ToString(VehicleType vehicleType) { return DebugPrint(vehicleType); }
void FromString(string const & s, VehicleType & vehicleType)
{
if (s == "Pedestrian")
vehicleType = VehicleType::Pedestrian;
else if (s == "Bicycle")
vehicleType = VehicleType::Bicycle;
else if (s == "Car")
vehicleType = VehicleType::Car;
else
{
ASSERT(false, ("Could not read VehicleType from string", s));
vehicleType = VehicleType::Count;
}
}
string DebugPrint(VehicleMask vehicleMask)
{
ostringstream oss;
oss << "VehicleMask [";
bool first = true;
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
{
auto const vt = static_cast<VehicleType>(i);
if ((vehicleMask & GetVehicleMask(vt)) == 0)
continue;
if (!first)
oss << ", ";
first = false;
oss << DebugPrint(vt);
}
oss << "]";
return oss.str();
}
} // namespace routing

View file

@ -27,14 +27,8 @@ VehicleMask constexpr kPedestrianMask = GetVehicleMask(VehicleType::Pedestrian);
VehicleMask constexpr kBicycleMask = GetVehicleMask(VehicleType::Bicycle);
VehicleMask constexpr kCarMask = GetVehicleMask(VehicleType::Car);
inline std::string DebugPrint(VehicleType vehicleType)
{
switch (vehicleType)
{
case VehicleType::Pedestrian: return "Pedestrian";
case VehicleType::Bicycle: return "Bicycle";
case VehicleType::Car: return "Car";
case VehicleType::Count: return "Count";
}
}
std::string DebugPrint(VehicleType vehicleType);
std::string ToString(VehicleType vehicleType);
void FromString(std::string const & s, VehicleType & vehicleType);
std::string DebugPrint(VehicleMask vehicleMask);
} // namespace routing