diff --git a/generator/generator_tests/restriction_collector_test.cpp b/generator/generator_tests/restriction_collector_test.cpp index be561e4867..7dd9556b32 100644 --- a/generator/generator_tests/restriction_collector_test.cpp +++ b/generator/generator_tests/restriction_collector_test.cpp @@ -5,7 +5,7 @@ #include "generator/osm_id.hpp" #include "generator/restriction_collector.hpp" -#include "routing/routing_serialization.hpp" +#include "routing/restrictions_serialization.hpp" #include "platform/platform_tests_support/scoped_dir.hpp" #include "platform/platform_tests_support/scoped_file.hpp" diff --git a/generator/generator_tests/restriction_test.cpp b/generator/generator_tests/restriction_test.cpp index d0e4d2d03a..e6b76a88f2 100644 --- a/generator/generator_tests/restriction_test.cpp +++ b/generator/generator_tests/restriction_test.cpp @@ -45,6 +45,28 @@ void BuildEmptyMwm(LocalCountryFile & country) generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::country); } +void LoadRestrictions(MwmValue const & mwmValue, RestrictionVec & restrictions) +{ + if (!mwmValue.m_cont.IsExist(RESTRICTIONS_FILE_TAG)) + return; + + try + { + FilesContainerR::TReader const reader = mwmValue.m_cont.GetReader(RESTRICTIONS_FILE_TAG); + ReaderSource src(reader); + RestrictionHeader header; + header.Deserialize(src); + + RestrictionVec restrictionsOnly; + RestrictionSerializer::Deserialize(header, restrictions, restrictionsOnly, src); + restrictions.insert(restrictions.end(), restrictionsOnly.cbegin(), restrictionsOnly.cend()); + } + catch (Reader::OpenException const & e) + { + LOG(LERROR, ("Error while reading", RESTRICTIONS_FILE_TAG, "section.", e.Msg())); + } +} + /// \brief Generates a restriction section, adds it to an empty mwm, /// loads the restriction section and test loaded restrictions. /// \param restrictionContent comma separated text with restrictions in osm id terms. @@ -83,10 +105,12 @@ void TestRestrictionBuilding(string const & restrictionContent, string const & m MwmSet::MwmHandle mwmHandle = index.GetMwmHandleById(regResult.first); TEST(mwmHandle.IsAlive(), ()); - RestrictionLoader const restrictionLoader(*mwmHandle.GetValue()); + + RestrictionVec restrictionsFromMwm; + LoadRestrictions(*mwmHandle.GetValue(), restrictionsFromMwm); RestrictionCollector const restrictionCollector(restrictionFullPath, mappingFullPath); - TEST_EQUAL(restrictionLoader.GetRestrictions(), restrictionCollector.GetRestrictions(), ()); + TEST_EQUAL(restrictionsFromMwm, restrictionCollector.GetRestrictions(), ()); } UNIT_TEST(RestrictionGenerationTest_NoRestriction) diff --git a/generator/restriction_collector.hpp b/generator/restriction_collector.hpp index 432acdc159..e9df69a428 100644 --- a/generator/restriction_collector.hpp +++ b/generator/restriction_collector.hpp @@ -1,6 +1,6 @@ #pragma once -#include "routing/routing_serialization.hpp" +#include "routing/restrictions_serialization.hpp" #include "std/functional.hpp" #include "std/limits.hpp" diff --git a/generator/restriction_generator.cpp b/generator/restriction_generator.cpp index 6613e12cd3..5ab6d9b395 100644 --- a/generator/restriction_generator.cpp +++ b/generator/restriction_generator.cpp @@ -33,9 +33,11 @@ bool BuildRoadRestrictions(string const & mwmPath, string const & restrictionPat auto const firstOnlyIt = lower_bound(restrictions.cbegin(), restrictions.cend(), Restriction(Restriction::Type::Only, {} /* links */), my::LessBy(&Restriction::m_type)); - RoutingHeader header; + + RestrictionHeader header; header.m_noRestrictionCount = base::checked_cast(distance(restrictions.cbegin(), firstOnlyIt)); header.m_onlyRestrictionCount = base::checked_cast(restrictions.size() - header.m_noRestrictionCount); + LOG(LINFO, ("Header info. There are", header.m_noRestrictionCount, "of type No restrictions and", header.m_onlyRestrictionCount, "of type Only restrictions")); diff --git a/generator/restriction_writer.cpp b/generator/restriction_writer.cpp index 604bc09e1e..97aee60ada 100644 --- a/generator/restriction_writer.cpp +++ b/generator/restriction_writer.cpp @@ -4,7 +4,7 @@ #include "generator/osm_id.hpp" #include "generator/restriction_collector.hpp" -#include "routing/routing_serialization.hpp" +#include "routing/restrictions_serialization.hpp" #include "base/logging.hpp" diff --git a/routing/CMakeLists.txt b/routing/CMakeLists.txt index eeaa1a7edc..95cd367703 100644 --- a/routing/CMakeLists.txt +++ b/routing/CMakeLists.txt @@ -70,6 +70,8 @@ set( pedestrian_model.hpp restriction_loader.cpp restriction_loader.hpp + restrictions_serialization.cpp + restrictions_serialization.hpp road_graph.cpp road_graph.hpp road_graph_router.cpp @@ -92,8 +94,6 @@ set( routing_mapping.cpp routing_mapping.hpp routing_result_graph.hpp - routing_serialization.cpp - routing_serialization.hpp routing_session.cpp routing_session.hpp routing_settings.hpp diff --git a/routing/edge_estimator.cpp b/routing/edge_estimator.cpp index 354b1283d1..31bbe0107a 100644 --- a/routing/edge_estimator.cpp +++ b/routing/edge_estimator.cpp @@ -43,6 +43,7 @@ public: void Finish() override; double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road) const override; double CalcHeuristic(m2::PointD const & from, m2::PointD const & to) const override; + double GetUTurnPenalty() const override; private: TrafficCache const & m_trafficCache; @@ -101,6 +102,14 @@ double CarEdgeEstimator::CalcHeuristic(m2::PointD const & from, m2::PointD const { return TimeBetweenSec(from, to, m_maxSpeedMPS); } + +double CarEdgeEstimator::GetUTurnPenalty() const +{ + // Adds 2 minutes penalty for U-turn. The value is quite arbitrary + // and needs to be properly selected after a number of real-world + // experiments. + return 2 * 60; // seconds +} } // namespace namespace routing diff --git a/routing/edge_estimator.hpp b/routing/edge_estimator.hpp index 1b64e84fa0..8c4f59f1e8 100644 --- a/routing/edge_estimator.hpp +++ b/routing/edge_estimator.hpp @@ -24,6 +24,7 @@ public: virtual void Finish() = 0; virtual double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road) const = 0; virtual double CalcHeuristic(m2::PointD const & from, m2::PointD const & to) const = 0; + virtual double GetUTurnPenalty() const = 0; static shared_ptr CreateForCar(IVehicleModel const & vehicleModel, traffic::TrafficCache const & trafficCache); diff --git a/routing/geometry.hpp b/routing/geometry.hpp index c172f4cf88..818290e54f 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -42,6 +42,12 @@ public: // In such cases RoadGeometry is not valid. bool IsValid() const { return m_valid; } + bool IsEndPointId(uint32_t pointId) const + { + ASSERT_LESS(pointId, m_points.size(), ()); + return pointId == 0 || pointId + 1 == GetPointsCount(); + } + private: Points m_points; double m_speed = 0.0; diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index cba0dd3b56..4ae83899a5 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -1,9 +1,59 @@ -#include "index_graph.hpp" +#include "routing/index_graph.hpp" + +#include "routing/restrictions_serialization.hpp" #include "base/assert.hpp" +#include "base/checked_cast.hpp" #include "base/exception.hpp" -#include "std/limits.hpp" +#include +#include + +namespace +{ +using namespace base; +using namespace routing; +using namespace std; + +bool IsUTurn(Segment const & u, Segment const & v) +{ + return u.GetFeatureId() == v.GetFeatureId() && u.GetSegmentIdx() == v.GetSegmentIdx() && + u.IsForward() != v.IsForward(); +} + +bool IsRestricted(RestrictionVec const & restrictions, Segment const & u, Segment const & v, + bool isOutgoing) +{ + uint32_t const featureIdFrom = isOutgoing ? u.GetFeatureId() : v.GetFeatureId(); + uint32_t const featureIdTo = isOutgoing ? v.GetFeatureId() : u.GetFeatureId(); + + if (!binary_search(restrictions.cbegin(), restrictions.cend(), + Restriction(Restriction::Type::No, {featureIdFrom, featureIdTo}))) + { + return false; + } + + if (featureIdFrom != featureIdTo) + return true; + + // @TODO(bykoianko) According to current code if a feature id is marked as a feature with + // restrictricted U-turn it's restricted to make a U-turn on the both ends of the feature. + // Generally speaking it's wrong. In osm there's information about the end of the feature + // where the U-turn is restricted. It's necessary to pass the data to mwm and to use it here. + // Please see test LineGraph_RestrictionF1F1No for details. + // + // Another example when it's necessary to be aware about feature end participated in restriction + // is + // *---F1---* + // | | + // *--F3--A B--F4--* + // | | + // *---F2---* + // In case of restriction F1-A-F2 or F1-B-F2 of any type (No, Only) the important information + // is lost. + return IsUTurn(u, v); +} +} // namespace namespace routing { @@ -36,7 +86,13 @@ void IndexGraph::Import(vector const & joints) { m_roadIndex.Import(joints); CHECK_LESS_OR_EQUAL(joints.size(), numeric_limits::max(), ()); - Build(static_cast(joints.size())); + Build(checked_cast(joints.size())); +} + +void IndexGraph::SetRestrictions(RestrictionVec && restrictions) +{ + ASSERT(is_sorted(restrictions.cbegin(), restrictions.cend()), ()); + m_restrictions = move(restrictions); } double IndexGraph::CalcSegmentWeight(Segment const & segment) @@ -70,17 +126,27 @@ void IndexGraph::GetNeighboringEdges(Segment const & from, RoadPoint const & rp, void IndexGraph::GetNeighboringEdge(Segment const & from, Segment const & to, bool isOutgoing, vector & edges) { + // Blocking U-turns on internal feature points. RoadPoint const rp = from.GetRoadPoint(isOutgoing); - if (from.GetFeatureId() == to.GetFeatureId() && from.GetSegmentIdx() == to.GetSegmentIdx() - && from.IsForward() != to.IsForward() - && m_roadIndex.GetJointId(rp) == Joint::kInvalidId - && rp.GetPointId() != 0 - && rp.GetPointId() + 1 != m_geometry.GetRoad(from.GetFeatureId()).GetPointsCount()) + if (IsUTurn(from, to) && m_roadIndex.GetJointId(rp) == Joint::kInvalidId + && !m_geometry.GetRoad(from.GetFeatureId()).IsEndPointId(rp.GetPointId())) { return; } - double const weight = CalcSegmentWeight(isOutgoing ? to : from); + if (IsRestricted(m_restrictions, from, to, isOutgoing)) + return; + + double const weight = CalcSegmentWeight(isOutgoing ? to : from) + + GetPenalties(isOutgoing ? from : to, isOutgoing ? to : from); edges.emplace_back(to, weight); } + +double IndexGraph::GetPenalties(Segment const & u, Segment const & v) const +{ + if (IsUTurn(u, v)) + return GetEstimator().GetUTurnPenalty(); + + return 0.0; +} } // namespace routing diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp index e9b441dc02..ddcf96e688 100644 --- a/routing/index_graph.hpp +++ b/routing/index_graph.hpp @@ -4,6 +4,7 @@ #include "routing/geometry.hpp" #include "routing/joint.hpp" #include "routing/joint_index.hpp" +#include "routing/restrictions_serialization.hpp" #include "routing/road_index.hpp" #include "routing/road_point.hpp" #include "routing/segment.hpp" @@ -31,6 +32,7 @@ public: Geometry & GetGeometry() { return m_geometry; } EdgeEstimator const & GetEstimator() const { return *m_estimator; } + bool IsRoad(uint32_t featureId) const { return m_roadIndex.IsRoad(featureId); } RoadJointIds const & GetRoad(uint32_t featureId) const { return m_roadIndex.GetRoad(featureId); } uint32_t GetNumRoads() const { return m_roadIndex.GetSize(); } @@ -40,6 +42,8 @@ public: void Build(uint32_t numJoints); void Import(vector const & joints); + void SetRestrictions(RestrictionVec && restrictions); + void PushFromSerializer(Joint::Id jointId, RoadPoint const & rp) { m_roadIndex.PushFromSerializer(jointId, rp); @@ -51,16 +55,24 @@ public: m_roadIndex.ForEachRoad(forward(f)); } + template + void ForEachPoint(Joint::Id jointId, F && f) const + { + m_jointIndex.ForEachPoint(jointId, forward(f)); + } + private: double CalcSegmentWeight(Segment const & segment); void GetNeighboringEdges(Segment const & from, RoadPoint const & rp, bool isOutgoing, vector & edges); void GetNeighboringEdge(Segment const & from, Segment const & to, bool isOutgoing, vector & edges); + double GetPenalties(Segment const & u, Segment const & v) const; Geometry m_geometry; shared_ptr m_estimator; RoadIndex m_roadIndex; JointIndex m_jointIndex; + RestrictionVec m_restrictions; }; } // namespace routing diff --git a/routing/index_road_graph.cpp b/routing/index_road_graph.cpp index 6cfadc578c..ab5d45b81d 100644 --- a/routing/index_road_graph.cpp +++ b/routing/index_road_graph.cpp @@ -88,8 +88,9 @@ const Segment & IndexRoadGraph::GetSegment(Junction const & junction, bool isOut { auto const & junctionToSegment = isOutgoing ? m_endToSegment : m_beginToSegment; - auto it = junctionToSegment.find(junction); - CHECK(it != junctionToSegment.cend(), ("junctionToSegment doesn't contains", junction, ", isOutgoing =" + isOutgoing)); + auto const it = junctionToSegment.find(junction); + CHECK(it != junctionToSegment.cend(), + ("junctionToSegment doesn't contain", junction, ", isOutgoing =", isOutgoing)); return it->second; } } // namespace routing diff --git a/routing/restriction_loader.cpp b/routing/restriction_loader.cpp index caba4ea903..44efd6aace 100644 --- a/routing/restriction_loader.cpp +++ b/routing/restriction_loader.cpp @@ -1,9 +1,40 @@ #include "routing/restriction_loader.hpp" -#include "routing/routing_serialization.hpp" +#include "routing/restrictions_serialization.hpp" +#include "routing/road_index.hpp" + +#include "base/stl_helpers.hpp" + +namespace +{ +using namespace routing; + +/// \returns if features |r1| and |r2| have a common end returns its joint id. +/// If not, returns Joint::kInvalidId. +/// \note It's possible that the both ends of |r1| and |r2| have common joint ids. +/// In that case returns any of them. +/// \note In general case ends of features don't have to be joints. For example all +/// loose feature ends aren't joints. But if ends of r1 and r2 are connected at this +/// point there has to be a joint. So the method is valid. +Joint::Id GetCommonEndJoint(RoadJointIds const & r1, RoadJointIds const & r2) +{ + auto const & j11 = r1.GetJointId(0 /* point id */); + auto const & j12 = r1.GetEndingJointId(); + auto const & j21 = r2.GetJointId(0 /* point id */); + auto const & j22 = r2.GetEndingJointId(); + + if (j11 == j21 || j11 == j22) + return j11; + + if (j12 == j21 || j12 == j22) + return j12; + + return Joint::kInvalidId; +} +} // namespace namespace routing { -RestrictionLoader::RestrictionLoader(MwmValue const & mwmValue) +RestrictionLoader::RestrictionLoader(MwmValue const & mwmValue, IndexGraph const & graph) : m_countryFileName(mwmValue.GetCountryFileName()) { if (!mwmValue.m_cont.IsExist(RESTRICTIONS_FILE_TAG)) @@ -15,13 +46,48 @@ RestrictionLoader::RestrictionLoader(MwmValue const & mwmValue) ReaderSource src(*m_reader); m_header.Deserialize(src); - RestrictionSerializer::Deserialize(m_header, m_restrictions, src); + RestrictionVec restrictionsOnly; + RestrictionSerializer::Deserialize(m_header, m_restrictions /* restriction No */, + restrictionsOnly, src); + ConvertRestrictionsOnlyToNoAndSort(graph, restrictionsOnly, m_restrictions); } catch (Reader::OpenException const & e) { m_header.Reset(); LOG(LERROR, ("File", m_countryFileName, "Error while reading", RESTRICTIONS_FILE_TAG, "section.", e.Msg())); + throw; } } + +void ConvertRestrictionsOnlyToNoAndSort(IndexGraph const & graph, + RestrictionVec const & restrictionsOnly, + RestrictionVec & restrictionsNo) +{ + for (Restriction const & o : restrictionsOnly) + { + if (o.m_featureIds.size() != 2) + continue; + + if (!graph.IsRoad(o.m_featureIds[0]) || !graph.IsRoad(o.m_featureIds[1])) + continue; + + // Looking for a joint of an intersection of |o| features. + Joint::Id const common = + GetCommonEndJoint(graph.GetRoad(o.m_featureIds[0]), graph.GetRoad(o.m_featureIds[1])); + if (common == Joint::kInvalidId) + continue; + + // Adding restriction of type No for all features of joint |common| except for + // the second feature of restriction |o|. + graph.ForEachPoint(common, [&](RoadPoint const & rp) { + if (rp.GetFeatureId() != o.m_featureIds[1 /* to */]) + { + restrictionsNo.emplace_back(Restriction::Type::No, + vector({o.m_featureIds[0 /* from */], rp.GetFeatureId()})); + } + }); + } + my::SortUnique(restrictionsNo); +} } // namespace routing diff --git a/routing/restriction_loader.hpp b/routing/restriction_loader.hpp index 5cb2a8987a..6c4e6adbfa 100644 --- a/routing/restriction_loader.hpp +++ b/routing/restriction_loader.hpp @@ -1,6 +1,7 @@ #pragma once -#include "routing/routing_serialization.hpp" +#include "routing/index_graph.hpp" +#include "routing/restrictions_serialization.hpp" #include "indexer/index.hpp" @@ -14,15 +15,19 @@ namespace routing class RestrictionLoader { public: - explicit RestrictionLoader(MwmValue const & mwmValue); + explicit RestrictionLoader(MwmValue const & mwmValue, IndexGraph const & graph); bool HasRestrictions() const { return !m_restrictions.empty(); } - RestrictionVec const & GetRestrictions() const { return m_restrictions; } + RestrictionVec && StealRestrictions() { return move(m_restrictions); } private: unique_ptr m_reader; - RoutingHeader m_header; + RestrictionHeader m_header; RestrictionVec m_restrictions; string const m_countryFileName; }; + +void ConvertRestrictionsOnlyToNoAndSort(IndexGraph const & graph, + RestrictionVec const & restrictionsOnly, + RestrictionVec & restrictionsNo); } // namespace routing diff --git a/routing/routing_serialization.cpp b/routing/restrictions_serialization.cpp similarity index 86% rename from routing/routing_serialization.cpp rename to routing/restrictions_serialization.cpp index a64aa441ad..a9efc8d6f3 100644 --- a/routing/routing_serialization.cpp +++ b/routing/restrictions_serialization.cpp @@ -1,4 +1,4 @@ -#include "routing/routing_serialization.hpp" +#include "routing/restrictions_serialization.hpp" #include "std/sstream.hpp" @@ -15,8 +15,8 @@ uint32_t const Restriction::kInvalidFeatureId = numeric_limits::max(); bool Restriction::IsValid() const { - return !m_featureIds.empty() && find(begin(m_featureIds), end(m_featureIds), kInvalidFeatureId) - == end(m_featureIds); + return !m_featureIds.empty() && + find(begin(m_featureIds), end(m_featureIds), kInvalidFeatureId) == end(m_featureIds); } bool Restriction::operator==(Restriction const & restriction) const diff --git a/routing/routing_serialization.hpp b/routing/restrictions_serialization.hpp similarity index 79% rename from routing/routing_serialization.hpp rename to routing/restrictions_serialization.hpp index 8e1b747a7c..235645e1f5 100644 --- a/routing/routing_serialization.hpp +++ b/routing/restrictions_serialization.hpp @@ -37,7 +37,6 @@ struct Restriction }; Restriction(Type type, vector const & links) : m_featureIds(links), m_type(type) {} - bool IsValid() const; bool operator==(Restriction const & restriction) const; bool operator<(Restriction const & restriction) const; @@ -53,9 +52,9 @@ string ToString(Restriction::Type const & type); string DebugPrint(Restriction::Type const & type); string DebugPrint(Restriction const & restriction); -struct RoutingHeader +struct RestrictionHeader { - RoutingHeader() { Reset(); } + RestrictionHeader() { Reset(); } template void Serialize(Sink & sink) const @@ -89,13 +88,13 @@ struct RoutingHeader uint32_t m_onlyRestrictionCount; }; -static_assert(sizeof(RoutingHeader) == 12, "Wrong header size of routing section."); +static_assert(sizeof(RestrictionHeader) == 12, "Wrong header size of routing section."); class RestrictionSerializer { public: template - static void Serialize(RoutingHeader const & header, + static void Serialize(RestrictionHeader const & header, routing::RestrictionVec::const_iterator begin, routing::RestrictionVec::const_iterator end, Sink & sink) { @@ -105,13 +104,14 @@ public: } template - static void Deserialize(RoutingHeader const & header, routing::RestrictionVec & restrictions, - Source & src) + static void Deserialize(RestrictionHeader const & header, + routing::RestrictionVec & restrictionsNo, + routing::RestrictionVec & restrictionsOnly, Source & src) { DeserializeSingleType(routing::Restriction::Type::No, header.m_noRestrictionCount, - restrictions, src); + restrictionsNo, src); DeserializeSingleType(routing::Restriction::Type::Only, header.m_onlyRestrictionCount, - restrictions, src); + restrictionsOnly, src); } private: @@ -139,17 +139,20 @@ private: routing::Restriction const & restriction = *begin; CHECK(restriction.IsValid(), ()); - CHECK_LESS(1, restriction.m_featureIds.size(), ("No sense in zero or one link restrictions.")); + CHECK_LESS(1, restriction.m_featureIds.size(), + ("No sense in zero or one link restrictions.")); - coding::DeltaCoder::Encode(bits, restriction.m_featureIds.size() - 1 /* number of link is two or more */); + coding::DeltaCoder::Encode( + bits, restriction.m_featureIds.size() - 1 /* number of link is two or more */); CHECK_LESS_OR_EQUAL(prevFirstLinkFeatureId, restriction.m_featureIds[0], ()); - coding::DeltaCoder::Encode(bits, - restriction.m_featureIds[0] - prevFirstLinkFeatureId + 1 /* making it greater than zero */); + coding::DeltaCoder::Encode(bits, restriction.m_featureIds[0] - prevFirstLinkFeatureId + + 1 /* making it greater than zero */); for (size_t i = 1; i < restriction.m_featureIds.size(); ++i) { - uint32_t const delta = bits::ZigZagEncode(static_cast(restriction.m_featureIds[i]) - - static_cast(restriction.m_featureIds[i - 1])); + uint32_t const delta = + bits::ZigZagEncode(static_cast(restriction.m_featureIds[i]) - + static_cast(restriction.m_featureIds[i - 1])); coding::DeltaCoder::Encode(bits, delta + 1 /* making it greater than zero */); } prevFirstLinkFeatureId = restriction.m_featureIds[0]; @@ -172,7 +175,7 @@ private: } size_t const numLinks = biasedLinkNumber + 1 /* number of link is two or more */; - routing::Restriction restriction(type, {} /* links */); + routing::Restriction restriction(type, {} /* links */); restriction.m_featureIds.resize(numLinks); auto const biasedFirstFeatureId = coding::DeltaCoder::Decode(bits); if (biasedFirstFeatureId == 0) @@ -180,7 +183,8 @@ private: LOG(LERROR, ("Decoded first link restriction feature id delta is zero.")); return false; } - restriction.m_featureIds[0] = prevFirstLinkFeatureId + base::checked_cast(biasedFirstFeatureId) - 1; + restriction.m_featureIds[0] = + prevFirstLinkFeatureId + base::checked_cast(biasedFirstFeatureId) - 1; for (size_t i = 1; i < numLinks; ++i) { auto const biasedDelta = coding::DeltaCoder::Decode(bits); @@ -189,9 +193,10 @@ private: LOG(LERROR, ("Decoded link restriction feature id delta is zero.")); return false; } - auto const delta = biasedDelta - 1; - restriction.m_featureIds[i] = static_cast( - bits::ZigZagDecode(delta) + restriction.m_featureIds[i - 1]); + + uint32_t const delta = biasedDelta - 1; + restriction.m_featureIds[i] = + static_cast(bits::ZigZagDecode(delta) + restriction.m_featureIds[i - 1]); } prevFirstLinkFeatureId = restriction.m_featureIds[0]; diff --git a/routing/road_index.hpp b/routing/road_index.hpp index ada8b054ce..2f13c83330 100644 --- a/routing/road_index.hpp +++ b/routing/road_index.hpp @@ -29,6 +29,15 @@ public: return Joint::kInvalidId; } + Joint::Id GetEndingJointId() const + { + if (m_jointIds.empty()) + return Joint::kInvalidId; + + ASSERT_NOT_EQUAL(m_jointIds.back(), Joint::kInvalidId, ()); + return m_jointIds.back(); + } + void AddJoint(uint32_t pointId, Joint::Id jointId) { ASSERT_NOT_EQUAL(jointId, Joint::kInvalidId, ()); @@ -113,10 +122,12 @@ public: m_roads[rp.GetFeatureId()].AddJoint(rp.GetPointId(), jointId); } + bool IsRoad(uint32_t featureId) const { return m_roads.count(featureId) != 0; } + RoadJointIds const & GetRoad(uint32_t featureId) const { auto const & it = m_roads.find(featureId); - CHECK(it != m_roads.cend(), ()); + CHECK(it != m_roads.cend(), ("Feature id:", featureId)); return it->second; } diff --git a/routing/routing.pro b/routing/routing.pro index 1635233426..44426f226b 100644 --- a/routing/routing.pro +++ b/routing/routing.pro @@ -42,6 +42,7 @@ SOURCES += \ pedestrian_directions.cpp \ pedestrian_model.cpp \ restriction_loader.cpp \ + restrictions_serialization.cpp \ road_graph.cpp \ road_graph_router.cpp \ road_index.cpp \ @@ -51,7 +52,6 @@ SOURCES += \ routing_algorithm.cpp \ routing_helpers.cpp \ routing_mapping.cpp \ - routing_serialization.cpp \ routing_session.cpp \ single_mwm_router.cpp \ speed_camera.cpp \ @@ -96,6 +96,7 @@ HEADERS += \ pedestrian_directions.hpp \ pedestrian_model.hpp \ restriction_loader.hpp \ + restrictions_serialization.hpp \ road_graph.hpp \ road_graph_router.hpp \ road_index.hpp \ @@ -109,7 +110,6 @@ HEADERS += \ routing_helpers.hpp \ routing_mapping.hpp \ routing_result_graph.hpp \ - routing_serialization.hpp \ routing_session.hpp \ routing_settings.hpp \ segment.hpp \ diff --git a/routing/routing_tests/CMakeLists.txt b/routing/routing_tests/CMakeLists.txt index ba0ae9da55..cbe5f9106e 100644 --- a/routing/routing_tests/CMakeLists.txt +++ b/routing/routing_tests/CMakeLists.txt @@ -9,10 +9,14 @@ set( astar_router_test.cpp async_router_test.cpp cross_routing_tests.cpp + cumulative_restriction_test.cpp followed_polyline_test.cpp + index_graph_tools.cpp + index_graph_tools.hpp nearest_edge_finder_tests.cpp online_cross_fetcher_test.cpp osrm_router_test.cpp + restriction_test.cpp road_graph_builder.cpp road_graph_builder.hpp road_graph_nearest_edges_test.cpp diff --git a/routing/routing_tests/cumulative_restriction_test.cpp b/routing/routing_tests/cumulative_restriction_test.cpp new file mode 100644 index 0000000000..ad5d4fe06b --- /dev/null +++ b/routing/routing_tests/cumulative_restriction_test.cpp @@ -0,0 +1,283 @@ +#include "testing/testing.hpp" + +#include "routing/routing_tests/index_graph_tools.hpp" + +#include "routing/car_model.hpp" +#include "routing/geometry.hpp" + +#include "traffic/traffic_cache.hpp" + +#include "geometry/point2d.hpp" + +#include +#include + +namespace +{ +using namespace routing; +using namespace routing_test; + +// Finish +// 3 * +// ^ +// F5 +// | +// 2 * * +// ↖ ↗ ↖ +// F2 F3 F4 +// ↖ ↗ ↖ +// 1 * * +// ↗ ↖ +// F0 F1 +// ↗ ↖ +// 0 * * +// 0 1 2 3 +// Start +// Note. This graph contains of 6 one segment directed features. +unique_ptr BuildXYGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(1 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(2 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(3 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(4 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(5 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {2.0, 3.0}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (0, 0) */ + MakeJoint({{1, 0}}), /* joint at point (2, 0) */ + MakeJoint({{0, 1}, {1, 1}, {2, 0}, {3, 0}}), /* joint at point (1, 1) */ + MakeJoint({{2, 1}}), /* joint at point (0, 2) */ + MakeJoint({{3, 1}, {4, 1}, {5, 0}}), /* joint at point (2, 2) */ + MakeJoint({{4, 0}}), /* joint at point (3, 1) */ + MakeJoint({{5, 1}}), /* joint at point (2, 3) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// Route through XY graph without any restrictions. +UNIT_TEST(XYGraph) +{ + unique_ptr graph = BuildXYGraph(); + IndexGraphStarter starter(*graph, + IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */); + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {2, 3}}; + TestRouteGeometry(starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +// Route through XY graph with one restriciton (type only) from F1 to F3. +UNIT_CLASS_TEST(RestrictionTest, XYGraph_RestrictionF1F3Only) +{ + Init(BuildXYGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {1 /* feature from */, 3 /* feature to */}}}; + + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {2, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */, + move(restrictions), *this); +} + +// Route through XY graph with one restriciton (type only) from F3 to F5. +UNIT_CLASS_TEST(RestrictionTest, XYGraph_RestrictionF3F5Only) +{ + Init(BuildXYGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {3 /* feature from */, 5 /* feature to */}}}; + + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {2, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */, + move(restrictions), *this); +} + +// Cumulative case. Route through XY graph with two restricitons (type only) applying +// in all possible orders. +UNIT_CLASS_TEST(RestrictionTest, XYGraph_PermutationsF3F5OnlyF1F3Only) +{ + Init(BuildXYGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {1 /* feature from */, 3 /* feature to */}}, + {Restriction::Type::Only, {3 /* feature from */, 5 /* feature to */}}}; + + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {2, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */, + move(restrictions), *this); +} + +// Cumulative case. Route through XY graph with two restricitons (type only and type no) applying +// in all possible orders. +UNIT_CLASS_TEST(RestrictionTest, XYGraph_PermutationsF3F5OnlyAndF0F2No) +{ + Init(BuildXYGraph()); + + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 2 /* feature to */}}, + {Restriction::Type::Only, {3 /* feature from */, 5 /* feature to */}}}; + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 1}, {2, 2}, {2, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */, + move(restrictions), *this); +} + +// Cumulative case. Trying to build route through XY graph with two restricitons applying +// according to the order. First from F3 to F5 (type only) +// and then and from F1 to F3 (type no). +UNIT_CLASS_TEST(RestrictionTest, XYGraph_RestrictionF3F5OnlyAndF1F3No) +{ + Init(BuildXYGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 3 /* feature to */}}, + {Restriction::Type::Only, {3 /* feature from */, 5 /* feature to */}}}; + + TestRestrictions({} /* expectedGeom */, AStarAlgorithm::Result::NoPath, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(2, 3)) /* finish */, + move(restrictions), *this); +} + +// Finish +// 3 * * +// ↖ ↗ +// F5 F6 +// ↖ ↗ +// 2 * * +// ↖ ↗ ↖ +// F2 F3 F4 +// ↖ ↗ ↖ +// 1 * * +// ↗ ↖ ^ +// F0 F1 F8 +// ↗ ↖ | +// 0 * *--F7--->* +// ^ +// F9 +// | +//-1 * +// 0 1 2 3 +// Start +// Note. This graph contains of 9 one segment directed features. +unique_ptr BuildXXGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(1 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(2 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(3 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(4 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(5 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {1.0, 3.0}})); + loader->AddRoad(6 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {3.0, 3.0}})); + loader->AddRoad(7 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {3.0, 0.0}})); + loader->AddRoad(8 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {3.0, 1.0}})); + loader->AddRoad(9 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, -1.0}, {2.0, 0.0}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (0, 0) */ + MakeJoint({{1, 0}, {7, 0}, {9, 1}}), /* joint at point (2, 0) */ + MakeJoint({{0, 1}, {1, 1}, {2, 0}, {3, 0}}), /* joint at point (1, 1) */ + MakeJoint({{2, 1}}), /* joint at point (0, 2) */ + MakeJoint({{3, 1}, {4, 1}, {5, 0}, {6, 0}}), /* joint at point (2, 2) */ + MakeJoint({{4, 0}, {8, 1}}), /* joint at point (3, 1) */ + MakeJoint({{5, 1}}), /* joint at point (1, 3) */ + MakeJoint({{6, 1}}), /* joint at point (3, 3) */ + MakeJoint({{7, 1}, {8, 0}}), /* joint at point (3, 0) */ + MakeJoint({{9, 0}}), /* joint at point (2, -1) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// Route through XY graph without any restrictions. +UNIT_CLASS_TEST(RestrictionTest, XXGraph) +{ + Init(BuildXXGraph()); + RestrictionVec restrictions = {}; + vector const expectedGeom = {{2 /* x */, -1 /* y */}, {2, 0}, {1, 1}, {2, 2}, {3, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(9, 0, m2::PointD(2, -1)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(3, 3)), /* finish */ + move(restrictions), *this); +} + +// Cumulative case. Route through XX graph with two restricitons (type only) applying +// in all possible orders. +UNIT_CLASS_TEST(RestrictionTest, XXGraph_PermutationsF1F3OnlyAndF3F6Only) +{ + Init(BuildXXGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {1 /* feature from */, 3 /* feature to */}}, + {Restriction::Type::Only, {3 /* feature from */, 6 /* feature to */}}}; + + vector const expectedGeom = {{2 /* x */, -1 /* y */}, {2, 0}, {1, 1}, {2, 2}, {3, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(9, 0, m2::PointD(2, -1)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(3, 3)), /* finish */ + move(restrictions), *this); +} + +// Route through XX graph with one restriciton (type no) from F1 to F3. +UNIT_CLASS_TEST(RestrictionTest, XXGraph_RestrictionF1F3No) +{ + Init(BuildXXGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 3 /* feature to */}}}; + vector const expectedGeom = { + {2 /* x */, -1 /* y */}, {2, 0}, {3, 0}, {3, 1}, {2, 2}, {3, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(9, 0, m2::PointD(2, -1)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(3, 3)), /* finish */ + move(restrictions), *this); +} + +// Cumulative case. Route through XX graph with four restricitons of different types applying +// in all possible orders. +UNIT_CLASS_TEST(RestrictionTest, XXGraph_PermutationsF1F3NoF7F8OnlyF8F4OnlyF4F6Only) +{ + Init(BuildXXGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 3 /* feature to */}}, + {Restriction::Type::Only, {4 /* feature from */, 6 /* feature to */}}, + {Restriction::Type::Only, {7 /* feature from */, 8 /* feature to */}}, + {Restriction::Type::Only, {8 /* feature from */, 4 /* feature to */}}}; + + vector const expectedGeom = { + {2 /* x */, -1 /* y */}, {2, 0}, {3, 0}, {3, 1}, {2, 2}, {3, 3}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(9, 0, m2::PointD(2, -1)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(3, 3)), /* finish */ + move(restrictions), *this); +} +} // namespace diff --git a/routing/routing_tests/index_graph_test.cpp b/routing/routing_tests/index_graph_test.cpp index 2e690f5219..d76872de60 100644 --- a/routing/routing_tests/index_graph_test.cpp +++ b/routing/routing_tests/index_graph_test.cpp @@ -31,12 +31,10 @@ void TestRoute(IndexGraph & graph, IndexGraphStarter::FakeVertex const & start, IndexGraphStarter::FakeVertex const & finish, size_t expectedLength, vector const * expectedRoute = nullptr) { - LOG(LINFO, ("Test route", start.GetFeatureId(), ",", start.GetSegmentIdx(), "=>", - finish.GetFeatureId(), ",", finish.GetSegmentIdx())); - IndexGraphStarter starter(graph, start, finish); vector route; - auto const resultCode = CalculateRoute(starter, route); + double timeSec; + auto const resultCode = CalculateRoute(starter, route, timeSec); TEST_EQUAL(resultCode, AStarAlgorithm::Result::OK, ()); TEST_GREATER(route.size(), 2, ()); @@ -386,4 +384,79 @@ UNIT_TEST(SerializeSimpleGraph) TEST_EQUAL(graph.GetJointId({2, 1}), Joint::kInvalidId, ()); } } + +// Finish +// 0.0004 * +// ^ +// | +// F2 +// | +// | +// 0.0003 *---------* +// | | +// | | +// | | +// | | +// | | +// 0.0002 * * +// | | +// | | +// 0.00015 * F0 * +// \ / +// \ / +// 0.0001 *---F0-*----* +// ^ +// | +// F1 +// | +// | +// 0 * Start +// 0 0.0001 0.0002 +// F0 is a two-way feature with a loop and F1 and F2 are an one-way one-segment features. +unique_ptr BuildLoopGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, false /* one way */, 100.0 /* speed */, + RoadGeometry::Points({{0.0002, 0.0001}, + {0.00015, 0.0001}, + {0.0001, 0.0001}, + {0.00015, 0.00015}, + {0.00015, 0.0002}, + {0.00015, 0.0003}, + {0.00005, 0.0003}, + {0.00005, 0.0002}, + {0.00005, 0.00015}, + {0.0001, 0.0001}})); + loader->AddRoad(1 /* feature id */, true /* one way */, 100.0 /* speed */, + RoadGeometry::Points({{0.0002, 0.0}, {0.0002, 0.0001}})); + loader->AddRoad(2 /* feature id */, true /* one way */, 100.0 /* speed */, + RoadGeometry::Points({{0.00005, 0.0003}, {0.00005, 0.0004}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 2 /* point id */}, {0, 9}}), /* joint at point (0.0002, 0) */ + MakeJoint({{1, 1}, {0, 0}}), /* joint at point (0.0002, 0.0001) */ + MakeJoint({{0, 6}, {2, 0}}), /* joint at point (0.00005, 0.0003) */ + MakeJoint({{2, 1}}), /* joint at point (0.00005, 0.0004) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// This test checks that the route from Start to Finish doesn't make an extra loop in F0. +// If it was so the route time had been much more. +UNIT_CLASS_TEST(RestrictionTest, LoopGraph) +{ + Init(BuildLoopGraph()); + SetStarter( + routing::IndexGraphStarter::FakeVertex(1, 0 /* seg id */, m2::PointD(0.0002, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(2, 0 /* seg id */, + m2::PointD(0.00005, 0.0004)) /* finish */); + + double constexpr kExpectedRouteTimeSec = 3.48; + TestRouteTime(*m_starter, AStarAlgorithm::Result::OK, kExpectedRouteTimeSec); +} } // namespace routing_test diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 3859f2c417..f8fad8ef2c 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -41,13 +41,16 @@ shared_ptr CreateEstimator(traffic::TrafficCache const & trafficC } AStarAlgorithm::Result CalculateRoute(IndexGraphStarter & starter, - vector & roadPoints) + vector & roadPoints, + double & timeSec) { AStarAlgorithm algorithm; RoutingResult routingResult; auto const resultCode = algorithm.FindPathBidirectional( starter, starter.GetStart(), starter.GetFinish(), routingResult, {}, {}); + timeSec = routingResult.distance; + roadPoints = routingResult.path; return resultCode; } @@ -56,12 +59,63 @@ void TestRouteGeometry(IndexGraphStarter & starter, AStarAlgorithm::Result expectedRouteResult, vector const & expectedRouteGeom) { - vector route; - auto const resultCode = CalculateRoute(starter, route); - TEST_EQUAL(resultCode, expectedRouteResult, ()); - TEST_EQUAL(IndexGraphStarter::GetRouteNumPoints(route), expectedRouteGeom.size(), ()); + vector routeSegs; + double timeSec = 0.0; + auto const resultCode = CalculateRoute(starter, routeSegs, timeSec); - for (size_t i = 0; i < IndexGraphStarter::GetRouteNumPoints(route); ++i) - TEST_EQUAL(expectedRouteGeom[i], starter.GetRoutePoint(route, i), ()); + if (AStarAlgorithm::Result::NoPath == expectedRouteResult && + expectedRouteGeom == vector()) + { + // The route goes through a restriction. So there's no choice for building route + // except for going through restriction. So no path. + return; + } + + TEST_EQUAL(resultCode, expectedRouteResult, ()); + if (resultCode != AStarAlgorithm::Result::OK) + return; + + CHECK(!routeSegs.empty(), ()); + vector geom; + + auto const pushPoint = [&geom](m2::PointD const & point) { + if (geom.empty() || geom.back() != point) + geom.push_back(point); + }; + + for (size_t i = 0; i < routeSegs.size(); ++i) + { + m2::PointD const & pnt = starter.GetPoint(routeSegs[i], false /* front */); + // Note. In case of A* router all internal points of route are duplicated. + // So it's necessary to exclude the duplicates. + pushPoint(pnt); + } + + pushPoint(starter.GetPoint(routeSegs.back(), false /* front */)); + TEST_EQUAL(geom, expectedRouteGeom, ()); +} + +void TestRouteTime(IndexGraphStarter & starter, + AStarAlgorithm::Result expectedRouteResult, + double expectedTime) +{ + vector routeSegs; + double timeSec = 0.0; + auto const resultCode = CalculateRoute(starter, routeSegs, timeSec); + + TEST_EQUAL(resultCode, expectedRouteResult, ()); + double const kEpsilon = 1e-5; + TEST(my::AlmostEqualAbs(timeSec, expectedTime, kEpsilon), ()); +} + +void TestRestrictions(vector const & expectedRouteGeom, + AStarAlgorithm::Result expectedRouteResult, + routing::IndexGraphStarter::FakeVertex const & start, + routing::IndexGraphStarter::FakeVertex const & finish, + RestrictionVec && restrictions, RestrictionTest & restrictionTest) +{ + restrictionTest.SetRestrictions(move(restrictions)); + restrictionTest.SetStarter(start, finish); + TestRouteGeometry(*restrictionTest.m_starter, expectedRouteResult, expectedRouteGeom); } } // namespace routing_test diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 4c5494b1b3..e03cdc8aa6 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -3,6 +3,7 @@ #include "routing/edge_estimator.hpp" #include "routing/index_graph.hpp" #include "routing/index_graph_starter.hpp" +#include "routing/restrictions_serialization.hpp" #include "routing/road_point.hpp" #include "routing/segment.hpp" @@ -10,6 +11,8 @@ #include "traffic/traffic_info.hpp" +#include "indexer/classificator_loader.hpp" + #include "geometry/point2d.hpp" #include "std/algorithm.hpp" @@ -20,6 +23,27 @@ namespace routing_test { +using namespace routing; + +struct RestrictionTest +{ + RestrictionTest() { classificator::Load(); } + void Init(unique_ptr graph) { m_graph = move(graph); } + void SetStarter(IndexGraphStarter::FakeVertex const & start, + IndexGraphStarter::FakeVertex const & finish) + { + m_starter = make_unique(*m_graph, start, finish); + } + + void SetRestrictions(RestrictionVec && restrictions) + { + m_graph->SetRestrictions(move(restrictions)); + } + + unique_ptr m_graph; + unique_ptr m_starter; +}; + class TestGeometryLoader final : public routing::GeometryLoader { public: @@ -38,15 +62,23 @@ routing::Joint MakeJoint(vector const & points); shared_ptr CreateEstimator(traffic::TrafficCache const & trafficCache); routing::AStarAlgorithm::Result CalculateRoute( - routing::IndexGraphStarter & starter, vector & roadPoints); - -void TestRouteSegments( - routing::IndexGraphStarter & starter, - routing::AStarAlgorithm::Result expectedRouteResult, - vector const & expectedRoute); + routing::IndexGraphStarter & starter, vector & roadPoints, double & timeSec); void TestRouteGeometry( routing::IndexGraphStarter & starter, routing::AStarAlgorithm::Result expectedRouteResult, vector const & expectedRouteGeom); + +void TestRouteTime(IndexGraphStarter & starter, + AStarAlgorithm::Result expectedRouteResult, + double expectedTime); + +/// \brief Applies |restrictions| to graph in |restrictionTest| and +/// tests the resulting route. +/// \note restrictionTest should have a valid |restrictionTest.m_graph|. +void TestRestrictions(vector const & expectedRouteGeom, + AStarAlgorithm::Result expectedRouteResult, + routing::IndexGraphStarter::FakeVertex const & start, + routing::IndexGraphStarter::FakeVertex const & finish, + RestrictionVec && restrictions, RestrictionTest & restrictionTest); } // namespace routing_test diff --git a/routing/routing_tests/restriction_test.cpp b/routing/routing_tests/restriction_test.cpp new file mode 100644 index 0000000000..04e50bf184 --- /dev/null +++ b/routing/routing_tests/restriction_test.cpp @@ -0,0 +1,838 @@ +#include "testing/testing.hpp" + +#include "routing/routing_tests/index_graph_tools.hpp" + +#include "routing/car_model.hpp" +#include "routing/geometry.hpp" +#include "routing/restriction_loader.hpp" + +#include "indexer/classificator_loader.hpp" + +#include "geometry/point2d.hpp" + +#include +#include + +namespace routing_test +{ +using namespace routing; + +// Finish +// * +// ^ +// | +// F7 +// | +// * +// ^ +// | +// F6 +// | +// Start * -- F0 --> * -- F1 --> * <-- F2 --> * -- F3 --> * +// | ^ +// | | +// F4 F5 +// | | +// ⌄ | +// * +unique_ptr BuildCrossGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{-1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(1 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 0.0}})); + loader->AddRoad(2 /* featureId */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.9999, 0.0}})); + loader->AddRoad(3 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.9999, 0.0}, {3.0, 0.0}})); + loader->AddRoad(4 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, -1.0}})); + loader->AddRoad(5 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, -1.0}, {1.0, 0.0}})); + loader->AddRoad(6 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(7 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {1.0, 2.0}})); + + vector const joints = { + MakeJoint({{0, 1}, {1, 0}}), MakeJoint({{1, 1}, {2, 0}, {4, 0}, {5, 1}, {6, 0}}), + MakeJoint({{2, 1}, {3, 0}}), MakeJoint({{4, 1}, {5, 0}}), MakeJoint({{6, 1}, {7, 0}})}; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +UNIT_CLASS_TEST(RestrictionTest, CrossGraph_NoUTurn) +{ + Init(BuildCrossGraph()); + SetStarter(routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(-1, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(7, 0, m2::PointD(1, 2)) /* finish */); + + vector const expectedGeom = { + {-1.0 /* x */, 0.0 /* y */}, {0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {1.0, 2.0}}; + TestRouteGeometry(*m_starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +UNIT_CLASS_TEST(RestrictionTest, CrossGraph_UTurn) +{ + Init(BuildCrossGraph()); + SetStarter(routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(-1, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(7, 0, m2::PointD(1, 2)) /* finish */); + + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 6 /* feature to */}}}; + vector const expectedGeom = {{-1.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {1.0, -1.0}, + {1.0, 0.0}, {1.0, 1.0}, {1.0, 2.0}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(-1, 0)), /* start */ + routing::IndexGraphStarter::FakeVertex(7, 0, m2::PointD(1, 2)), /* finish */ + move(restrictions), *this); +} + +// Finish +// 3 * +// ^ +// | +// F4 +// | +// 2 * +// ^ ↖ +// | F1 +// | ↖ +// 1 | * +// F0 ↖ +// | F2 +// | ↖ +// 0 *<--F3---<--F3---*<--F5--* Start +// 0 1 2 3 +// Note. F0, F1 and F2 are one segment features. F3 is a two segments feature. +unique_ptr BuildTriangularGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 2.0}})); + loader->AddRoad(1 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(2 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(3 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(4 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 2.0}, {0.0, 3.0}})); + loader->AddRoad(5 /* featureId */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {2.0, 0.0}})); + + vector const joints = { + MakeJoint({{2, 0}, {3, 0}, {5, 1}}), /* joint at point (2, 0) */ + MakeJoint({{3, 2}, {0, 0}}), /* joint at point (0, 0) */ + MakeJoint({{2, 1}, {1, 0}}), /* joint at point (1, 1) */ + MakeJoint({{0, 1}, {1, 1}, {4, 0}}), /* joint at point (0, 2) */ + MakeJoint({{5, 0}}), /* joint at point (3, 0) */ + MakeJoint({{4, 1}}) /* joint at point (0, 3) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// Route through triangular graph without any restrictions. +UNIT_CLASS_TEST(RestrictionTest, TriangularGraph) +{ + Init(BuildTriangularGraph()); + + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, {}, + *this); +} + +// Route through triangular graph with restriction type no from feature 2 to feature 1. +UNIT_CLASS_TEST(RestrictionTest, TriangularGraph_RestrictionNoF2F1) +{ + Init(BuildTriangularGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {2 /* feature from */, 1 /* feature to */}}}; + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictions), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TriangularGraph_RestrictionNoF5F2) +{ + Init(BuildTriangularGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {5 /* feature from */, 2 /* feature to */}}}; + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictions), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TriangularGraph_RestrictionOnlyF5F3) +{ + Init(BuildTriangularGraph()); + RestrictionVec restrictionsOnly = { + {Restriction::Type::Only, {5 /* feature from */, 3 /* feature to */}}}; + RestrictionVec restrictionsNo; + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictionsNo), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TriangularGraph_RestrictionNoF5F2RestrictionOnlyF5F3) +{ + Init(BuildTriangularGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {5 /* feature from */, 2 /* feature to */}}, + {Restriction::Type::Only, {5 /* feature from */, 3 /* feature to */}}}; + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictions), *this); +} + +// Finish +// 3 * +// | +// F4 +// | +// 2 * +// | \ +// F0 F2 +// | \ +// 1 * * +// | \ +// F0 F2 +// | \ +// 0 *---F1--*--F1--*--F3---* Start +// 0 1 2 3 +// Note. All features are two setments and two-way. +unique_ptr BuildTwowayCornerGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(1 /* feature id */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(2 /* feature id */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(3 /* feature id */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {2.0, 0.0}})); + loader->AddRoad(4 /* feature id */, false /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 2.0}, {0.0, 3.0}})); + + vector const joints = { + MakeJoint({{1 /* feature id */, 2 /* point id */}, {0, 0}}) + /* joint at point (0, 0) */, + MakeJoint({{1, 0}, {2, 0}, {3, 1}}), /* joint at point (2, 0) */ + MakeJoint({{2, 2}, {0, 2}, {4, 0}}), /* joint at point (0, 2) */ + MakeJoint({{4, 1}}), /* joint at point (0, 3) */ + MakeJoint({{3, 0}}), /* joint at point (3, 0) */ + }; + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +UNIT_CLASS_TEST(RestrictionTest, TwowayCornerGraph) +{ + Init(BuildTwowayCornerGraph()); + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(3, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, {}, + *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TwowayCornerGraph_RestrictionF3F2No) +{ + Init(BuildTwowayCornerGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {3 /* feature from */, 2 /* feature to */}}}; + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(3, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictions), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TwowayCornerGraph_RestrictionF3F1Only) +{ + Init(BuildTwowayCornerGraph()); + RestrictionVec restrictionsOnly = { + {Restriction::Type::Only, {3 /* feature from */, 1 /* feature to */}}}; + RestrictionVec restrictionsNo; + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(3, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(4, 0, m2::PointD(0, 3)) /* finish */, + move(restrictionsNo), *this); +} + +// Finish +// 3 * +// ^ +// | +// F11 +// | +// 2 *<---F5----*<---F6---* +// ^ ↖ ^ ↖ ^ +// | F7 | F8 | +// | ↖ F1 ↖ F2 +// | ↖ | ↖ | +// 1 F0 * * +// | ^ ↖ ^ +// | F1 F9 F2 +// | | ↖ | +// 0 *<----F4---*<---F3----*<--F10---* Start +// 0 1 2 3 +// Note. F1 and F2 are two segments features. The others are one segment ones. +unique_ptr BuildTwoSquaresGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 2.0}})); + loader->AddRoad(1 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, 1.0}, {1.0, 2.0}})); + loader->AddRoad(2 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {2.0, 1.0}, {2.0, 2.0}})); + loader->AddRoad(3 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}})); + loader->AddRoad(4 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(5 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 2.0}, {0.0, 2.0}})); + loader->AddRoad(6 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 2.0}, {1.0, 2.0}})); + loader->AddRoad(7 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(8 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 1.0}, {1.0, 2.0}})); + loader->AddRoad(9 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(10 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {2.0, 0.0}})); + loader->AddRoad(11 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 2.0}, {0.0, 3.0}})); + + vector const joints = { + MakeJoint({{4 /* featureId */, 1 /* pointId */}, {0, 0}}), /* joint at point (0, 0) */ + MakeJoint({{0, 1}, {5, 1}, {7, 1}, {11, 0}}), /* joint at point (0, 2) */ + MakeJoint({{4, 0}, {1, 0}, {3, 1}}), /* joint at point (1, 0) */ + MakeJoint({{5, 0}, {1, 2}, {6, 1}, {8, 1}}), /* joint at point (1, 2) */ + MakeJoint({{3, 0}, {2, 0}, {9, 0}, {10, 1}}), /* joint at point (2, 0) */ + MakeJoint({{2, 2}, {6, 0}}), /* joint at point (2, 2) */ + MakeJoint({{1, 1}, {9, 1}, {7, 0}}), /* joint at point (1, 1) */ + MakeJoint({{2, 1}, {8, 0}}), /* joint at point (2, 1) */ + MakeJoint({{10, 0}}), /* joint at point (3, 0) */ + MakeJoint({{11, 1}}), /* joint at point (0, 3) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +UNIT_CLASS_TEST(RestrictionTest, TwoSquaresGraph) +{ + Init(BuildTwoSquaresGraph()); + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(10, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(11, 0, m2::PointD(0, 3)) /* finish */, {}, + *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TwoSquaresGraph_RestrictionF10F3Only) +{ + Init(BuildTwoSquaresGraph()); + RestrictionVec restrictionsOnly = { + {Restriction::Type::Only, {10 /* feature from */, 3 /* feature to */}}}; + RestrictionVec restrictionsNo; + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {1, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(10, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(11, 0, m2::PointD(0, 3)) /* finish */, + move(restrictionsNo), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TwoSquaresGraph_RestrictionF10F3OnlyF3F4Only) +{ + Init(BuildTwoSquaresGraph()); + RestrictionVec restrictionsOnly = { + {Restriction::Type::Only, {3 /* feature from */, 4 /* feature to */}}, + {Restriction::Type::Only, {10 /* feature from */, 3 /* feature to */}}}; + RestrictionVec restrictionsNo; + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + + vector const expectedGeom = { + {3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(10, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(11, 0, m2::PointD(0, 3)) /* finish */, + move(restrictionsNo), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, TwoSquaresGraph_RestrictionF2F8NoRestrictionF9F1Only) +{ + Init(BuildTwoSquaresGraph()); + RestrictionVec restrictionsNo = { + {Restriction::Type::No, {2 /* feature from */, 8 /* feature to */}}}; // Invalid restriction. + RestrictionVec const restrictionsOnly = { + {Restriction::Type::Only, + {9 /* feature from */, 1 /* feature to */}}}; // Invalid restriction. + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 1}, {0, 2}, {0, 3}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(10, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(11, 0, m2::PointD(0, 3)) /* finish */, + move(restrictionsNo), *this); +} + +// 2 * +// | +// F6 +// |Finish +// 1 *-F4-*-F5-* +// | | +// F2 F3 +// | | +// 0 *---F1----*---F0---* Start +// 0 1 2 +// Note 1. All features are two-way. (It's possible to move along any direction of the features.) +// Note 2. Any feature contains of one segment. +unique_ptr BuildFlagGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}})); + loader->AddRoad(1 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(2 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}})); + loader->AddRoad(3 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(4 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 1.0}, {0.5, 1.0}})); + loader->AddRoad(5 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.5, 1.0}, {1.0, 1.0}})); + loader->AddRoad(6 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.5, 1.0}, {0.5, 2.0}})); + + vector const joints = { + MakeJoint({{1 /* feature id */, 1 /* point id */}, {2, 0}}), /* joint at point (0, 0) */ + MakeJoint({{2, 1}, {4, 0}}), /* joint at point (0, 1) */ + MakeJoint({{4, 1}, {5, 0}, {6, 0}}), /* joint at point (0.5, 1) */ + MakeJoint({{1, 0}, {3, 0}, {0, 1}}), /* joint at point (1, 0) */ + MakeJoint({{3, 1}, {5, 1}}), /* joint at point (1, 1) */ + MakeJoint({{6, 1}}), /* joint at point (0.5, 2) */ + MakeJoint({{0, 0}}), /* joint at point (2, 0) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// Route through flag graph without any restrictions. +UNIT_TEST(FlagGraph) +{ + unique_ptr graph = BuildFlagGraph(); + IndexGraphStarter starter( + *graph, routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(0.5, 1)) /* finish */); + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 0}, {1, 1}, {0.5, 1}}; + TestRouteGeometry(starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +// Route through flag graph with one restriciton (type no) from F0 to F3. +UNIT_CLASS_TEST(RestrictionTest, FlagGraph_RestrictionF0F3No) +{ + Init(BuildFlagGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {0 /* feature from */, 3 /* feature to */}}}; + vector const expectedGeom = { + {2 /* x */, 0 /* y */}, {1, 0}, {0, 0}, {0, 1}, {0.5, 1}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(0.5, 1)) /* finish */, + move(restrictions), *this); +} + +// Route through flag graph with one restriciton (type only) from F0 to F1. +UNIT_CLASS_TEST(RestrictionTest, FlagGraph_RestrictionF0F1Only) +{ + Init(BuildFlagGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {0 /* feature from */, 1 /* feature to */}}}; + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 0}, {1, 1}, {0.5, 1}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(0.5, 1)) /* finish */, + move(restrictions), *this); +} + +UNIT_CLASS_TEST(RestrictionTest, FlagGraph_PermutationsF1F3NoF7F8OnlyF8F4OnlyF4F6Only) +{ + Init(BuildFlagGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {0 /* feature from */, 3 /* feature to */}}, + {Restriction::Type::Only, {0 /* feature from */, 1 /* feature to */}}, + {Restriction::Type::Only, {1 /* feature from */, 2 /* feature to */}}}; + + vector const expectedGeom = { + {2 /* x */, 0 /* y */}, {1, 0}, {0, 0}, {0, 1}, {0.5, 1}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(0.5, 1)) /* finish */, + move(restrictions), *this); +} + +// 1 *-F4-*-F5-*---F6---* Finish +// | | +// F2 F3 +// | | +// 0 *---F1----*---F0---* Start +// 0 1 2 +// Note 1. All features except for F7 are two-way. +// Note 2. Any feature contains of one segment. +unique_ptr BuildPosterGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}})); + loader->AddRoad(1 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {0.0, 0.0}})); + loader->AddRoad(2 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}})); + loader->AddRoad(3 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(4 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 1.0}, {0.5, 1.0}})); + loader->AddRoad(5 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.5, 1.0}, {1.0, 1.0}})); + loader->AddRoad(6 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 1.0}, {2.0, 1.0}})); + + vector const joints = { + MakeJoint({{1 /* feature id */, 1 /* point id */}, {2, 0}}), /* joint at point (0, 0) */ + MakeJoint({{2, 1}, {4, 0}}), /* joint at point (0, 1) */ + MakeJoint({{4, 1}, {5, 0}}), /* joint at point (0.5, 1) */ + MakeJoint({{1, 0}, {3, 0}, {0, 1}}), /* joint at point (1, 0) */ + MakeJoint({{3, 1}, {5, 1}, {6, 0}}), /* joint at point (1, 1) */ + MakeJoint({{0, 0}}), /* joint at point (2, 0) */ + MakeJoint({{6, 1}}), /* joint at point (2, 1) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + + return graph; +} + +// Route through poster graph without any restrictions. +UNIT_TEST(PosterGraph) +{ + unique_ptr graph = BuildPosterGraph(); + IndexGraphStarter starter( + *graph, routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(2, 1)) /* finish */); + vector const expectedGeom = {{2 /* x */, 0 /* y */}, {1, 0}, {1, 1}, {2, 1}}; + + TestRouteGeometry(starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +// Route through poster graph with restrictions F0-F3 (type no). +UNIT_CLASS_TEST(RestrictionTest, PosterGraph_RestrictionF0F3No) +{ + Init(BuildPosterGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {0 /* feature from */, 3 /* feature to */}}}; + vector const expectedGeom = { + {2 /* x */, 0 /* y */}, {1, 0}, {0, 0}, {0, 1}, {0.5, 1}, {1, 1}, {2, 1}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(2, 1)), /* finish */ + move(restrictions), *this); +} + +// Route through poster graph with restrictions F0-F1 (type only). +UNIT_CLASS_TEST(RestrictionTest, PosterGraph_RestrictionF0F1Only) +{ + Init(BuildPosterGraph()); + + RestrictionVec restrictionsOnly = { + {Restriction::Type::Only, {0 /* feature from */, 1 /* feature to */}}}; + RestrictionVec restrictionsNo; + ConvertRestrictionsOnlyToNoAndSort(*m_graph, restrictionsOnly, restrictionsNo); + + vector const expectedGeom = { + {2 /* x */, 0 /* y */}, {1, 0}, {0, 0}, {0, 1}, {0.5, 1}, {1, 1}, {2, 1}}; + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(2, 0)), /* start */ + routing::IndexGraphStarter::FakeVertex(6, 0, m2::PointD(2, 1)), /* finish */ + move(restrictionsNo), *this); +} + +// 1 *--F1-->* +// ↗ ↘ +// F1 F1 +// ↗ ↘ +// 0 Start *---F3--->*---F0--->-------F0----->*---F2-->* Finish +// -1 0 1 2 3 4 +// Note. F0 is a two segments feature. F1 is a three segment one. F2 and F3 are one segment +// features. +unique_ptr BuildTwoWayGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 0.0}, {3.0, 0}})); + loader->AddRoad(1 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {1.0, 1.0}, {2.0, 1.0}, {3.0, 0.0}})); + loader->AddRoad(2 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {4.0, 0.0}})); + loader->AddRoad(3 /* feature id */, true /* oneWay */, 1.0 /* speed */, + RoadGeometry::Points({{-1.0, 0.0}, {0.0, 0.0}})); + + vector const joints = { + MakeJoint( + {{0 /* feature id */, 0 /* point id */}, {1, 0}, {3, 1}}), /* joint at point (0, 0) */ + MakeJoint( + {{0 /* feature id */, 2 /* point id */}, {1, 3}, {2, 0}}), /* joint at point (3, 0) */ + MakeJoint({{3 /* feature id */, 0 /* point id */}}), /* joint at point (-1, 0) */ + MakeJoint({{2 /* feature id */, 1 /* point id */}}), /* joint at point (4, 0) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +UNIT_TEST(TwoWayGraph) +{ + unique_ptr graph = BuildTwoWayGraph(); + IndexGraphStarter starter( + *graph, routing::IndexGraphStarter::FakeVertex(3, 0, m2::PointD(-1, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(2, 0, m2::PointD(4, 0)) /* finish */); + vector const expectedGeom = {{-1 /* x */, 0 /* y */}, {0, 0}, {1, 0}, {3, 0}, {4, 0}}; + + TestRouteGeometry(starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +// 1 *---F4----* +// | | +// F2 F3 +// | | +// 0 *<--F5---*<--F1----*<--F0---* Start +// Finish +// 0 1 2 3 +// Note 1. F0, F1 and F5 are one-way features. F3, F2 and F4 are two-way features. +// Note 2. Any feature contains of one segment. +unique_ptr BuildSquaresGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{3.0, 0.0}, {2.0, 0.0}})); + loader->AddRoad(1 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {1.0, 0.0}})); + loader->AddRoad(2 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {1.0, 1.0}})); + loader->AddRoad(3 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 0.0}, {2.0, 1.0}})); + loader->AddRoad(4 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{2.0, 1.0}, {1.0, 1.0}})); + loader->AddRoad(5 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {0.0, 0.0}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (3, 0) */ + MakeJoint({{0, 1}, {3, 0}, {1, 0}}), /* joint at point (2, 0) */ + MakeJoint({{3, 1}, {4, 0}}), /* joint at point (2, 1) */ + MakeJoint({{2, 1}, {4, 1}}), /* joint at point (1, 1) */ + MakeJoint({{1, 1}, {2, 0}, {5, 0}}), /* joint at point (1, 0) */ + MakeJoint({{5, 1}}) /* joint at point (0, 0) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +UNIT_TEST(SquaresGraph) +{ + unique_ptr graph = BuildSquaresGraph(); + IndexGraphStarter starter( + *graph, routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(0, 0)) /* finish */); + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}}; + TestRouteGeometry(starter, AStarAlgorithm::Result::OK, expectedGeom); +} + +// It's a test on correct working in case when because of adding restrictions +// start and finish could be match on blocked, moved or copied edges. +// See IndexGraphStarter constructor for a detailed description. +UNIT_CLASS_TEST(RestrictionTest, SquaresGraph_RestrictionF0F1OnlyF1F5Only) +{ + Init(BuildSquaresGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {0 /* feature from */, 1 /* feature to */}}, + {Restriction::Type::Only, {1 /* feature from */, 5 /* feature to */}}}; + vector const expectedGeom = {{3 /* x */, 0 /* y */}, {2, 0}, {1, 0}, {0, 0}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0, 0, m2::PointD(3, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(5, 0, m2::PointD(0, 0)) /* finish */, + move(restrictions), *this); +} + +// 0 Start *--F0--->*---F1---*---F1---*---F1---*---F2-->* Finish +// 0 1 2 3 4 5 +// Note. F0 and F2 are one segment one-way features. F1 is a 3 segment two-way feature. +unique_ptr BuildLineGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}})); + loader->AddRoad(1 /* feature id */, false /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0}, {3.0, 0.0}})); + loader->AddRoad(2 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{4.0, 0.0}, {5.0, 0.0}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (0, 0) */ + MakeJoint({{0, 1}, {1, 0}}), /* joint at point (1, 0) */ + MakeJoint({{1, 3}, {2, 0}}), /* joint at point (4, 0) */ + MakeJoint({{2, 1}}), /* joint at point (5, 0) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// This test checks that despite the fact uturn on F1 is prohibited (moving from F1 to F1 is +// prohibited) +// it's still possible to move from F1 to F1 in straight direction. +UNIT_CLASS_TEST(RestrictionTest, LineGraph_RestrictionF1F1No) +{ + Init(BuildLineGraph()); + RestrictionVec restrictions = { + {Restriction::Type::No, {1 /* feature from */, 1 /* feature to */}}}; + vector const expectedGeom = { + {0 /* x */, 0 /* y */}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0 /* feature id */, 0 /* seg id */, + m2::PointD(0, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(2, 0, m2::PointD(5, 0)) /* finish */, + move(restrictions), *this); +} + +// 2 *---F2-->* +// ^ +// F0 +// | +// 1 *---F1-->* Finish +// ^ +// F0 +// | +// 0 * +// 0 1 +// Start +unique_ptr BuildFGraph() +{ + unique_ptr loader = make_unique(); + loader->AddRoad(0 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}, {0.0, 2.0}})); + loader->AddRoad(1 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 1.0}, {1.0, 1.0}})); + loader->AddRoad(2 /* feature id */, true /* one way */, 1.0 /* speed */, + RoadGeometry::Points({{0.0, 2.0}, {1.0, 2.0}})); + + vector const joints = { + MakeJoint({{0 /* feature id */, 0 /* point id */}}), /* joint at point (0, 0) */ + MakeJoint({{0, 1}, {1, 0}}), /* joint at point (0, 1) */ + MakeJoint({{0, 2}, {2, 0}}), /* joint at point (0, 2) */ + MakeJoint({{1, 1}}), /* joint at point (1, 1) */ + MakeJoint({{2, 1}}), /* joint at point (1, 2) */ + }; + + traffic::TrafficCache const trafficCache; + unique_ptr graph = + make_unique(move(loader), CreateEstimator(trafficCache)); + graph->Import(joints); + return graph; +} + +// This test checks that having a Only restriction from F0 to F2 it's still possible move +// from F0 to F1. +UNIT_CLASS_TEST(RestrictionTest, FGraph_RestrictionF0F2Only) +{ + Init(BuildFGraph()); + RestrictionVec restrictions = { + {Restriction::Type::Only, {0 /* feature from */, 2 /* feature to */}}}; + vector const expectedGeom = {{0 /* x */, 0 /* y */}, {0, 1}, {1, 1}}; + + TestRestrictions(expectedGeom, AStarAlgorithm::Result::OK, + routing::IndexGraphStarter::FakeVertex(0 /* feature id */, 0 /* seg id */, + m2::PointD(0, 0)) /* start */, + routing::IndexGraphStarter::FakeVertex(1, 0, m2::PointD(1, 1)) /* finish */, + move(restrictions), *this); +} +} // namespace routing_test diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro index 49ee873ed0..f4c315d499 100644 --- a/routing/routing_tests/routing_tests.pro +++ b/routing/routing_tests/routing_tests.pro @@ -27,12 +27,14 @@ SOURCES += \ astar_router_test.cpp \ async_router_test.cpp \ cross_routing_tests.cpp \ + cumulative_restriction_test.cpp \ followed_polyline_test.cpp \ index_graph_test.cpp \ index_graph_tools.cpp \ nearest_edge_finder_tests.cpp \ online_cross_fetcher_test.cpp \ osrm_router_test.cpp \ + restriction_test.cpp \ road_graph_builder.cpp \ road_graph_nearest_edges_test.cpp \ route_tests.cpp \ diff --git a/routing/single_mwm_router.cpp b/routing/single_mwm_router.cpp index 288dcea1d9..345d327113 100644 --- a/routing/single_mwm_router.cpp +++ b/routing/single_mwm_router.cpp @@ -10,6 +10,7 @@ #include "routing/index_graph_starter.hpp" #include "routing/index_road_graph.hpp" #include "routing/pedestrian_model.hpp" +#include "routing/restriction_loader.hpp" #include "routing/route.hpp" #include "routing/routing_helpers.hpp" #include "routing/turns_generator.hpp" @@ -190,6 +191,10 @@ bool SingleMwmRouter::LoadIndex(MwmSet::MwmId const & mwmId, string const & coun FilesContainerR::TReader reader(mwmValue->m_cont.GetReader(ROUTING_FILE_TAG)); ReaderSource src(reader); IndexGraphSerializer::Deserialize(graph, src, kCarMask); + RestrictionLoader restrictionLoader(*mwmValue, graph); + if (restrictionLoader.HasRestrictions()) + graph.SetRestrictions(restrictionLoader.StealRestrictions()); + LOG(LINFO, (ROUTING_FILE_TAG, "section for", country, "loaded in", timer.ElapsedSeconds(), "seconds")); return true; @@ -203,8 +208,9 @@ bool SingleMwmRouter::LoadIndex(MwmSet::MwmId const & mwmId, string const & coun } bool SingleMwmRouter::RedressRoute(MwmSet::MwmId const & mwmId, IVehicleModel const & vehicleModel, - vector const & segments, RouterDelegate const & delegate, - IndexGraphStarter & starter, Route & route) const + vector const & segments, + RouterDelegate const & delegate, IndexGraphStarter & starter, + Route & route) const { vector junctions; size_t const numPoints = IndexGraphStarter::GetRouteNumPoints(segments); diff --git a/xcode/routing/routing.xcodeproj/project.pbxproj b/xcode/routing/routing.xcodeproj/project.pbxproj index d36a41eda4..5d7140792a 100644 --- a/xcode/routing/routing.xcodeproj/project.pbxproj +++ b/xcode/routing/routing.xcodeproj/project.pbxproj @@ -50,6 +50,12 @@ 56555E591D897D28009D786D /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6742ACDE1C68A13F009CB89E /* testingmain.cpp */; }; 56826BD01DB51C4E00807C62 /* car_router.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56826BCE1DB51C4E00807C62 /* car_router.cpp */; }; 56826BD11DB51C4E00807C62 /* car_router.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56826BCF1DB51C4E00807C62 /* car_router.hpp */; }; + 56CA09E31E30E73B00D05C9A /* applying_traffic_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56CA09DE1E30E73B00D05C9A /* applying_traffic_test.cpp */; }; + 56CA09E41E30E73B00D05C9A /* cumulative_restriction_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56CA09DF1E30E73B00D05C9A /* cumulative_restriction_test.cpp */; }; + 56CA09E51E30E73B00D05C9A /* index_graph_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56CA09E01E30E73B00D05C9A /* index_graph_tools.cpp */; }; + 56CA09E61E30E73B00D05C9A /* index_graph_tools.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56CA09E11E30E73B00D05C9A /* index_graph_tools.hpp */; }; + 56CA09E71E30E73B00D05C9A /* restriction_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56CA09E21E30E73B00D05C9A /* restriction_test.cpp */; }; + 56CA09E91E30F19800D05C9A /* libtraffic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56CA09E81E30F19800D05C9A /* libtraffic.a */; }; 56EA2FD51D8FD8590083F01A /* routing_helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56EA2FD41D8FD8590083F01A /* routing_helpers.hpp */; }; 56F0D7341D896A5300045886 /* libmap.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67BD35DE1C69F198003AA26F /* libmap.a */; }; 56F0D7391D896A5300045886 /* libstorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67BD35D41C69F155003AA26F /* libstorage.a */; }; @@ -141,7 +147,6 @@ 6742AD561C68C7D7009CB89E /* types.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD551C68C7C1009CB89E /* types.txt */; }; 6742AD5C1C68C7FA009CB89E /* drules_proto_clear.bin in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD571C68C7F6009CB89E /* drules_proto_clear.bin */; }; 6742AD5D1C68C7FA009CB89E /* drules_proto_dark.bin in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD581C68C7F6009CB89E /* drules_proto_dark.bin */; }; - 6742AD5E1C68C7FA009CB89E /* drules_proto_legacy.bin in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD591C68C7F6009CB89E /* drules_proto_legacy.bin */; }; 6742AD5F1C68C7FA009CB89E /* drules_proto-bw.bin in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD5A1C68C7F6009CB89E /* drules_proto-bw.bin */; }; 6742AD601C68C7FA009CB89E /* drules_proto.bin in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD5B1C68C7F6009CB89E /* drules_proto.bin */; }; 6742AD621C68F747009CB89E /* sound-strings in Resources */ = {isa = PBXBuildFile; fileRef = 6742AD611C68F747009CB89E /* sound-strings */; }; @@ -223,8 +228,6 @@ 67BD36051C69F51C003AA26F /* WorldCoasts.mwm in Resources */ = {isa = PBXBuildFile; fileRef = 67BD36021C69F513003AA26F /* WorldCoasts.mwm */; }; 67C79BA11E2CEE1400C40034 /* restriction_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67C79B9F1E2CEE1400C40034 /* restriction_loader.cpp */; }; 67C79BA21E2CEE1400C40034 /* restriction_loader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67C79BA01E2CEE1400C40034 /* restriction_loader.hpp */; }; - 67C79BA51E2CEE3100C40034 /* routing_serialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67C79BA31E2CEE3100C40034 /* routing_serialization.cpp */; }; - 67C79BA61E2CEE3100C40034 /* routing_serialization.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67C79BA41E2CEE3100C40034 /* routing_serialization.hpp */; }; 67C7D4291B4EB48F00FE41AA /* car_model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67C7D4211B4EB48F00FE41AA /* car_model.cpp */; }; 67C7D42A1B4EB48F00FE41AA /* car_model.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67C7D4221B4EB48F00FE41AA /* car_model.hpp */; }; 67C7D42B1B4EB48F00FE41AA /* pedestrian_model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67C7D4231B4EB48F00FE41AA /* pedestrian_model.cpp */; }; @@ -292,6 +295,12 @@ 563B91C41CC4F1DC00222BC1 /* bicycle_model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bicycle_model.hpp; sourceTree = ""; }; 56826BCE1DB51C4E00807C62 /* car_router.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = car_router.cpp; sourceTree = ""; }; 56826BCF1DB51C4E00807C62 /* car_router.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = car_router.hpp; sourceTree = ""; }; + 56CA09DE1E30E73B00D05C9A /* applying_traffic_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = applying_traffic_test.cpp; sourceTree = ""; }; + 56CA09DF1E30E73B00D05C9A /* cumulative_restriction_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cumulative_restriction_test.cpp; sourceTree = ""; }; + 56CA09E01E30E73B00D05C9A /* index_graph_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = index_graph_tools.cpp; sourceTree = ""; }; + 56CA09E11E30E73B00D05C9A /* index_graph_tools.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = index_graph_tools.hpp; sourceTree = ""; }; + 56CA09E21E30E73B00D05C9A /* restriction_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = restriction_test.cpp; sourceTree = ""; }; + 56CA09E81E30F19800D05C9A /* libtraffic.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtraffic.a; path = "/Users/vladimirbykoyanko/src_github_master/omim/xcode/traffic/../../../omim-build/xcode/Debug/libtraffic.a"; sourceTree = ""; }; 56EA2FD41D8FD8590083F01A /* routing_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = routing_helpers.hpp; sourceTree = ""; }; 56F0D75F1D896A5300045886 /* routing_benchmarks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = routing_benchmarks.app; sourceTree = BUILT_PRODUCTS_DIR; }; 670B84BE1A9381D900CE4492 /* cross_routing_context.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cross_routing_context.cpp; sourceTree = ""; }; @@ -415,8 +424,6 @@ 67BD36021C69F513003AA26F /* WorldCoasts.mwm */ = {isa = PBXFileReference; lastKnownFileType = file; path = WorldCoasts.mwm; sourceTree = ""; }; 67C79B9F1E2CEE1400C40034 /* restriction_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = restriction_loader.cpp; sourceTree = ""; }; 67C79BA01E2CEE1400C40034 /* restriction_loader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = restriction_loader.hpp; sourceTree = ""; }; - 67C79BA31E2CEE3100C40034 /* routing_serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = routing_serialization.cpp; sourceTree = ""; }; - 67C79BA41E2CEE3100C40034 /* routing_serialization.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = routing_serialization.hpp; sourceTree = ""; }; 67C7D4211B4EB48F00FE41AA /* car_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = car_model.cpp; sourceTree = ""; }; 67C7D4221B4EB48F00FE41AA /* car_model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = car_model.hpp; sourceTree = ""; }; 67C7D4231B4EB48F00FE41AA /* pedestrian_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pedestrian_model.cpp; sourceTree = ""; }; @@ -471,6 +478,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 56CA09E91E30F19800D05C9A /* libtraffic.a in Frameworks */, 3462FDAD1DC1E5BF00906FD7 /* libopening_hours.a in Frameworks */, 6742AD4D1C68AA59009CB89E /* librouting.a in Frameworks */, 6742AD401C68AA04009CB89E /* libosrm.a in Frameworks */, @@ -542,6 +550,7 @@ 56F0D7611D896DAF00045886 /* Frameworks */ = { isa = PBXGroup; children = ( + 56CA09E81E30F19800D05C9A /* libtraffic.a */, 3462FDAC1DC1E5BF00906FD7 /* libopening_hours.a */, ); name = Frameworks; @@ -561,6 +570,11 @@ 6742ACA01C68A07C009CB89E /* routing_tests */ = { isa = PBXGroup; children = ( + 56CA09DE1E30E73B00D05C9A /* applying_traffic_test.cpp */, + 56CA09DF1E30E73B00D05C9A /* cumulative_restriction_test.cpp */, + 56CA09E01E30E73B00D05C9A /* index_graph_tools.cpp */, + 56CA09E11E30E73B00D05C9A /* index_graph_tools.hpp */, + 56CA09E21E30E73B00D05C9A /* restriction_test.cpp */, 6742ACDE1C68A13F009CB89E /* testingmain.cpp */, 6742ACA61C68A0B1009CB89E /* astar_algorithm_test.cpp */, 6742ACA71C68A0B1009CB89E /* astar_progress_test.cpp */, @@ -685,8 +699,6 @@ 675343FA1A3F640D00A0A8C3 /* routing */ = { isa = PBXGroup; children = ( - 67C79BA31E2CEE3100C40034 /* routing_serialization.cpp */, - 67C79BA41E2CEE3100C40034 /* routing_serialization.hpp */, 67C79B9F1E2CEE1400C40034 /* restriction_loader.cpp */, 67C79BA01E2CEE1400C40034 /* restriction_loader.hpp */, 0C470E6F1E0D4EB1005B824D /* segment.hpp */, @@ -844,11 +856,11 @@ A1616E2C1B6B60AB003F078E /* router_delegate.hpp in Headers */, A17B42991BCFBD0E00A1EAE4 /* osrm_helpers.hpp in Headers */, 67C7D42E1B4EB48F00FE41AA /* turns_sound_settings.hpp in Headers */, - 67C79BA61E2CEE3100C40034 /* routing_serialization.hpp in Headers */, 56099E341CC9247E00A7772A /* bicycle_directions.hpp in Headers */, 670EE55E1B6001E7001E8064 /* routing_session.hpp in Headers */, 56099E291CC7C97D00A7772A /* loaded_path_segment.hpp in Headers */, 670EE55F1B6001E7001E8064 /* routing_settings.hpp in Headers */, + 56CA09E61E30E73B00D05C9A /* index_graph_tools.hpp in Headers */, 671F58BE1B874EC80032311E /* followed_polyline.hpp in Headers */, 67C7D42C1B4EB48F00FE41AA /* pedestrian_model.hpp in Headers */, 0C5FEC6A1DDE193F0017688C /* road_index.hpp in Headers */, @@ -1026,7 +1038,6 @@ 6742AD601C68C7FA009CB89E /* drules_proto.bin in Resources */, 6742AD621C68F747009CB89E /* sound-strings in Resources */, 6742AD5D1C68C7FA009CB89E /* drules_proto_dark.bin in Resources */, - 6742AD5E1C68C7FA009CB89E /* drules_proto_legacy.bin in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1094,6 +1105,7 @@ buildActionMask = 2147483647; files = ( 0C5FEC641DDE192A0017688C /* joint.cpp in Sources */, + 56CA09E71E30E73B00D05C9A /* restriction_test.cpp in Sources */, 0C5BC9D11E28FD4E0071BFDD /* index_road_graph.cpp in Sources */, 56826BD01DB51C4E00807C62 /* car_router.cpp in Sources */, 56099E2E1CC8FBDA00A7772A /* osrm_path_segment_factory.cpp in Sources */, @@ -1124,11 +1136,12 @@ 674A28B11B1605D2001A525C /* osrm_engine.cpp in Sources */, 0C08AA341DF83223004195DD /* index_graph_serialization.cpp in Sources */, 674F9BD41B0A580E00704FFA /* road_graph.cpp in Sources */, + 56CA09E51E30E73B00D05C9A /* index_graph_tools.cpp in Sources */, 0C0DF92A1DE898FF0055A22F /* routing_helpers.cpp in Sources */, - 67C79BA51E2CEE3100C40034 /* routing_serialization.cpp in Sources */, 67AB92E61B7B3E6E00AB5194 /* turns_tts_text.cpp in Sources */, 0C5FEC601DDE192A0017688C /* index_graph.cpp in Sources */, 0C5FEC6D1DDE19A40017688C /* index_graph_test.cpp in Sources */, + 56CA09E41E30E73B00D05C9A /* cumulative_restriction_test.cpp in Sources */, 6753441E1A3F644F00A0A8C3 /* turns.cpp in Sources */, 670B84C01A9381D900CE4492 /* cross_routing_context.cpp in Sources */, A120B3501B4A7C0A002F3808 /* routing_algorithm.cpp in Sources */, @@ -1136,6 +1149,7 @@ 0C5FEC5E1DDE192A0017688C /* geometry.cpp in Sources */, 674F9BD01B0A580E00704FFA /* online_cross_fetcher.cpp in Sources */, 670EE5751B664796001E8064 /* router.cpp in Sources */, + 56CA09E31E30E73B00D05C9A /* applying_traffic_test.cpp in Sources */, 0C5FEC621DDE192A0017688C /* joint_index.cpp in Sources */, 670C62131AC5A15700C38A8C /* routing_mapping.cpp in Sources */, 67C79BA11E2CEE1400C40034 /* restriction_loader.cpp in Sources */,