Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Byko-Ianko <v.bykoianko@corp.mail.ru>2016-11-16 08:26:16 +0300
committerVladimir Byko-Ianko <v.bykoianko@corp.mail.ru>2016-11-25 10:25:11 +0300
commit91ead6af9046745416b136c39561d1ba598f1b07 (patch)
tree6b8eff31b91ba709b6f943ea998256eea2181a2c
parenteb1c85bce50a6d98fa92b1cec278643b26b59946 (diff)
Generating restriction, implementaion implementation using them at runtime§:wq, applying them on index graph and tests on it.
-rw-r--r--routing/fseg_index.cpp104
-rw-r--r--routing/geometry.cpp13
-rw-r--r--routing/geometry.hpp18
-rw-r--r--routing/index_graph.cpp429
-rw-r--r--routing/index_graph.hpp124
-rw-r--r--routing/index_graph_starter.cpp8
-rw-r--r--routing/joint_index.cpp17
-rw-r--r--routing/joint_index.hpp6
-rw-r--r--routing/road_index.cpp53
-rw-r--r--routing/road_index.hpp27
-rw-r--r--routing/road_point.hpp23
-rw-r--r--routing/routing_serialization.cpp4
-rw-r--r--routing/routing_tests/index_graph_test.cpp71
-rw-r--r--routing/routing_tests/index_graph_tools.cpp84
-rw-r--r--routing/routing_tests/index_graph_tools.hpp46
-rw-r--r--routing/routing_tests/restriction_test.cpp622
-rw-r--r--routing/routing_tests/routing_tests.pro3
-rw-r--r--routing/single_mwm_router.cpp10
18 files changed, 1559 insertions, 103 deletions
diff --git a/routing/fseg_index.cpp b/routing/fseg_index.cpp
new file mode 100644
index 0000000000..a2ab7037eb
--- /dev/null
+++ b/routing/fseg_index.cpp
@@ -0,0 +1,104 @@
+#include "routing/fseg_index.hpp"
+
+#include "base/logging.hpp"
+
+#include "std/utility.hpp"
+
+namespace routing
+{
+void FSegIndex::Import(vector<Joint> const & joints)
+{
+ for (JointId jointId = 0; jointId < joints.size(); ++jointId)
+ {
+ Joint const & joint = joints[jointId];
+ for (uint32_t i = 0; i < joint.GetSize(); ++i)
+ {
+ FSegId const & entry = joint.GetEntry(i);
+ RoadJointIds & roadJoints = m_roads[entry.GetFeatureId()];
+ roadJoints.AddJoint(entry.GetSegId(), jointId);
+ if (entry.GetFeatureId() > m_maxFeatureId)
+ m_maxFeatureId = entry.GetFeatureId();
+ }
+ }
+}
+
+bool FSegIndex::GetAdjacentSegments(uint32_t featureIdFrom, uint32_t featureIdTo,
+ routing::FSegId & from, routing::FSegId & to, JointId & jointId) const
+{
+ auto const fromIt = m_roads.find(featureIdFrom);
+ if (fromIt == m_roads.cend())
+ return false;
+
+ auto const toIt = m_roads.find(featureIdTo);
+ if (toIt == m_roads.cend())
+ return false;
+
+ routing::RoadJointIds const & roadJointIdsFrom = fromIt->second;
+ routing::RoadJointIds const & roadJointIdsTo = toIt->second;
+ if (roadJointIdsFrom.GetSize() == 0 || roadJointIdsTo.GetSize() == 0)
+ return false; // No sence in restrictions on features without joints.
+
+ // Note. It's important to check other variant besides a restriction from last segment
+ // of featureIdFrom to first segment of featureIdTo since two way features can have
+ // reverse point order.
+ if (roadJointIdsFrom.Back() == roadJointIdsTo.Front())
+ {
+ from = routing::FSegId(featureIdFrom, roadJointIdsFrom.GetSize() - 1);
+ to = routing::FSegId(featureIdTo, 0);
+ jointId = roadJointIdsFrom.Back();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Front() == roadJointIdsTo.Back())
+ {
+ from = routing::FSegId(featureIdFrom, 0);
+ to = routing::FSegId(featureIdTo, roadJointIdsTo.GetSize() - 1);
+ jointId = roadJointIdsFrom.Front();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Back() == roadJointIdsTo.Back())
+ {
+ from = routing::FSegId(featureIdFrom, roadJointIdsFrom.GetSize() - 1);
+ to = routing::FSegId(featureIdTo, roadJointIdsTo.GetSize() - 1);
+ jointId = roadJointIdsFrom.Back();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Front() == roadJointIdsTo.Front())
+ {
+ from = routing::FSegId(featureIdFrom, 0);
+ to = routing::FSegId(featureIdTo, 0);
+ jointId = roadJointIdsFrom.Front();
+ return true;
+ }
+ return false; // |featureIdFrom| and |featureIdTo| are not adjacent.
+}
+
+void FSegIndex::ApplyRestrictionNo(routing::FSegId const & from, routing::FSegId const & to, JointId jointId)
+{
+}
+
+void FSegIndex::ApplyRestrictionOnly(routing::FSegId const & from, routing::FSegId const & to, JointId jointId)
+{
+}
+
+pair<JointId, uint32_t> FSegIndex::FindNeigbor(FSegId fseg, bool forward) const
+{
+ auto const it = m_roads.find(fseg.GetFeatureId());
+ if (it == m_roads.cend())
+ return make_pair(kInvalidJointId, 0);
+
+ RoadJointIds const & joints = it->second;
+ int32_t const step = forward ? 1 : -1;
+
+ for (uint32_t segId = fseg.GetSegId() + step; segId < joints.GetSize(); segId += step)
+ {
+ JointId const jointId = joints.GetJointId(segId);
+ if (jointId != kInvalidJointId)
+ return make_pair(jointId, segId);
+ }
+
+ return make_pair(kInvalidJointId, 0);
+}
+} // namespace routing
diff --git a/routing/geometry.cpp b/routing/geometry.cpp
index 218aa0eb03..556911a4c6 100644
--- a/routing/geometry.cpp
+++ b/routing/geometry.cpp
@@ -6,6 +6,8 @@
#include "base/assert.hpp"
+#include "std/iostream.hpp"
+
using namespace routing;
namespace
@@ -83,6 +85,17 @@ RoadGeometry const & Geometry::GetRoad(uint32_t featureId) const
return road;
}
+string DebugPrint(RoadGeometry const & roadGeometry)
+{
+ stringstream str;
+ str << "RoadGeometry [ m_isRoad: " << roadGeometry.m_isRoad
+ << ", m_isOneWay: " << roadGeometry.m_isOneWay
+ << ", m_speed: " << roadGeometry.m_speed
+ << ", m_points: " << DebugPrint(roadGeometry.m_points)
+ << " ]";
+ return str.str();
+}
+
unique_ptr<GeometryLoader> CreateGeometryLoader(Index const & index, MwmSet::MwmId const & mwmId,
shared_ptr<IVehicleModel> vehicleModel)
{
diff --git a/routing/geometry.hpp b/routing/geometry.hpp
index f10cb6dd78..7324f352fc 100644
--- a/routing/geometry.hpp
+++ b/routing/geometry.hpp
@@ -11,12 +11,15 @@
#include "std/cstdint.hpp"
#include "std/shared_ptr.hpp"
+#include "std/string.hpp"
#include "std/unique_ptr.hpp"
namespace routing
{
class RoadGeometry final
{
+ friend string DebugPrint(RoadGeometry const & roadGeometry);
+
public:
using Points = buffer_vector<m2::PointD, 32>;
@@ -40,6 +43,14 @@ public:
uint32_t GetPointsCount() const { return m_points.size(); }
+ bool operator==(RoadGeometry const & roadGeometry)
+ {
+ return m_isRoad == roadGeometry.m_isRoad
+ && m_isOneWay == roadGeometry.m_isOneWay
+ && m_speed == roadGeometry.m_speed
+ && m_points == roadGeometry.m_points;
+ }
+
private:
bool m_isRoad = false;
bool m_isOneWay = false;
@@ -47,6 +58,8 @@ private:
Points m_points;
};
+string DebugPrint(RoadGeometry const & roadGeometry);
+
class GeometryLoader
{
public:
@@ -63,11 +76,6 @@ public:
RoadGeometry const & GetRoad(uint32_t featureId) const;
- m2::PointD const & GetPoint(RoadPoint const & rp) const
- {
- return GetRoad(rp.GetFeatureId()).GetPoint(rp.GetPointId());
- }
-
private:
// Feature id to RoadGeometry map.
mutable unordered_map<uint32_t, RoadGeometry> m_roads;
diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp
index f2c8131298..d0e45261cc 100644
--- a/routing/index_graph.cpp
+++ b/routing/index_graph.cpp
@@ -2,6 +2,7 @@
#include "base/assert.hpp"
#include "base/exception.hpp"
+#include "base/stl_helpers.hpp"
namespace routing
{
@@ -18,15 +19,404 @@ void IndexGraph::GetEdgesList(Joint::Id jointId, bool isOutgoing, vector<JointEd
});
}
+m2::PointD const & IndexGraph::GetPoint(RoadPoint const & ftp) const
+{
+ RoadGeometry const & road = GetRoad(ftp.GetFeatureId());
+ CHECK_LESS(ftp.GetPointId(), road.GetPointsCount(), ());
+ return road.GetPoint(ftp.GetPointId());
+}
+
m2::PointD const & IndexGraph::GetPoint(Joint::Id jointId) const
{
- return m_geometry.GetPoint(m_jointIndex.GetPoint(jointId));
+ return GetPoint(m_jointIndex.GetPoint(jointId));
+}
+
+double IndexGraph::GetSpeed(RoadPoint ftp) const
+{
+ return GetRoad(ftp.GetFeatureId()).GetSpeed();
+}
+
+void IndexGraph::Build(uint32_t jointNumber)
+{
+ m_jointIndex.Build(m_roadIndex, jointNumber);
}
void IndexGraph::Import(vector<Joint> const & joints)
{
m_roadIndex.Import(joints);
- m_jointIndex.Build(m_roadIndex, joints.size());
+ Build(joints.size());
+}
+
+void IndexGraph::GetSingleFeaturePaths(RoadPoint from, RoadPoint to,
+ vector<RoadPoint> & singleFeaturePath)
+{
+ CHECK_EQUAL(from.GetFeatureId(), to.GetFeatureId(), ());
+ singleFeaturePath.clear();
+ if (from == to)
+ {
+ singleFeaturePath.push_back(from);
+ return;
+ }
+
+ int shift = to.GetPointId() > from.GetPointId() ? 1 : -1;
+ for (int i = from.GetPointId(); i != to.GetPointId(); i += shift)
+ singleFeaturePath.emplace_back(from.GetFeatureId(), i);
+ singleFeaturePath.emplace_back(from.GetFeatureId(), to.GetPointId());
+}
+
+void IndexGraph::GetConnectionPaths(Joint::Id from, Joint::Id to,
+ vector<vector<RoadPoint>> & connectionPaths)
+{
+ CHECK_NOT_EQUAL(from, Joint::kInvalidId, ());
+ CHECK_NOT_EQUAL(to, Joint::kInvalidId, ());
+
+ vector<pair<RoadPoint, RoadPoint>> connections;
+ m_jointIndex.FindPointsWithCommonFeature(from, to, connections);
+ CHECK_LESS(0, connections.size(), ());
+
+ connectionPaths.clear();
+ for (pair<RoadPoint, RoadPoint> const & c : connections)
+ {
+ vector<RoadPoint> roadPoints;
+ GetSingleFeaturePaths(c.first, c.second, roadPoints);
+ connectionPaths.push_back(move(roadPoints));
+ }
+}
+
+void IndexGraph::GetShortestConnectionPath(Joint::Id from, Joint::Id to,
+ vector<RoadPoint> & shortestConnectionPath)
+{
+ vector<pair<RoadPoint, RoadPoint>> connections;
+ m_jointIndex.FindPointsWithCommonFeature(from, to, connections);
+ CHECK_LESS(0, connections.size(), ());
+
+ // Note. The case when size of connections is zero is the most common case. If not,
+ // it's necessary to perform a costy calls below.
+ if (connections.size() == 1)
+ {
+ GetSingleFeaturePaths(connections[0].first /* from */, connections[0].second /* to */,
+ shortestConnectionPath);
+ return;
+ }
+
+ double minWeight = numeric_limits<double>::max();
+ pair<RoadPoint, RoadPoint> minWeightConnection;
+ for (pair<RoadPoint, RoadPoint> const & c : connections)
+ {
+ CHECK_EQUAL(c.first.GetFeatureId(), c.second.GetFeatureId(), ());
+ RoadGeometry const & geom = GetRoad(c.first.GetFeatureId());
+ if (!geom.IsRoad())
+ continue;
+
+ double weight = m_estimator->CalcEdgesWeight(geom, c.first.GetPointId() /* from */,
+ c.second.GetPointId() /* to */);
+ if (weight < minWeight)
+ {
+ minWeight = weight;
+ minWeightConnection = c;
+ }
+ }
+
+ if (minWeight == numeric_limits<double>::max())
+ {
+ MYTHROW(RootException,
+ ("Joint from:", from, "and joint to:", to, "are not connected a feature necessary type."));
+ }
+
+ GetSingleFeaturePaths(minWeightConnection.first /* from */, minWeightConnection.second /* to */,
+ shortestConnectionPath);
+}
+
+void IndexGraph::GetOutgoingGeomEdges(vector<JointEdge> const & outgoingEdges, Joint::Id center,
+ vector<JointEdgeGeom> & outgoingGeomEdges)
+{
+ for (JointEdge const & e : outgoingEdges)
+ {
+ vector<RoadPoint> shortestConnectionPath;
+ GetShortestConnectionPath(center, e.GetTarget(), shortestConnectionPath);
+ outgoingGeomEdges.emplace_back(e.GetTarget(), shortestConnectionPath);
+ }
+}
+
+void IndexGraph::CreateFakeFeatureGeometry(vector<RoadPoint> const & geometrySource,
+ RoadGeometry & geometry) const
+{
+ double averageSpeed = 0.0;
+ buffer_vector<m2::PointD, 32> points(geometrySource.size());
+ for (size_t i = 0; i < geometrySource.size(); ++i)
+ {
+ RoadPoint const ftp = geometrySource[i];
+ averageSpeed += GetSpeed(ftp) / geometrySource.size();
+ points[i] = GetPoint(ftp);
+ }
+
+ geometry = RoadGeometry(true /* oneWay */, averageSpeed, points);
+}
+
+uint32_t IndexGraph::AddFakeFeature(Joint::Id from, Joint::Id to, vector<RoadPoint> const & geometrySource)
+{
+ CHECK_LESS(from, m_jointIndex.GetNumJoints(), ());
+ CHECK_LESS(to, m_jointIndex.GetNumJoints(), ());
+ CHECK_LESS(1, geometrySource.size(), ());
+
+ // Getting fake feature geometry.
+ RoadGeometry geom;
+ CreateFakeFeatureGeometry(geometrySource, geom);
+ m_fakeFeatureGeometry.insert(make_pair(m_nextFakeFeatureId, geom));
+
+ // Adding to indexes for fake feature: |from|->{via point}->|to|.
+ RoadPoint const fromFakeFtPoint(m_nextFakeFeatureId, 0 /* point id */);
+ RoadPoint const toFakeFtPoint(m_nextFakeFeatureId, geometrySource.size() - 1 /* point id */);
+
+ m_roadIndex.AddJoint(fromFakeFtPoint, from);
+ m_roadIndex.AddJoint(toFakeFtPoint, to);
+
+ m_jointIndex.AppendToJoint(from, fromFakeFtPoint);
+ m_jointIndex.AppendToJoint(to, toFakeFtPoint);
+
+ return m_nextFakeFeatureId++;
+}
+
+void IndexGraph::FindOneStepAsideRoadPoint(RoadPoint const & center, Joint::Id centerId,
+ vector<JointEdge> const & edges,
+ vector<Joint::Id> & oneStepAside) const
+{
+ oneStepAside.clear();
+ m_roadIndex.ForEachJoint(center.GetFeatureId(),
+ [&](uint32_t /*pointId*/, Joint::Id jointId){
+ for (JointEdge const & e : edges)
+ {
+ if (e.GetTarget() == jointId)
+ oneStepAside.push_back(jointId);
+ }
+ });
+}
+
+bool IndexGraph::ApplyRestrictionPrepareData(RoadPoint const & from, RoadPoint const & to, Joint::Id centerId,
+ vector<JointEdge> & ingoingEdges, vector<JointEdge> & outgoingEdges,
+ Joint::Id & fromFirstOneStepAside, Joint::Id & toFirstOneStepAside)
+{
+ GetEdgesList(centerId, false /* isOutgoing */, ingoingEdges);
+ vector<Joint::Id> fromOneStepAside;
+ FindOneStepAsideRoadPoint(from, centerId, ingoingEdges, fromOneStepAside);
+ if (fromOneStepAside.empty())
+ return false;
+
+ GetEdgesList(centerId, true /* isOutgoing */, outgoingEdges);
+ vector<Joint::Id> toOneStepAside;
+ FindOneStepAsideRoadPoint(to, centerId, outgoingEdges, toOneStepAside);
+ if (toOneStepAside.empty())
+ return false;
+
+ fromFirstOneStepAside = fromOneStepAside.back();
+ toFirstOneStepAside = toOneStepAside.back();
+ return true;
+}
+
+void IndexGraph::ApplyRestrictionNo(RoadPoint const & from, RoadPoint const & to,
+ Joint::Id centerId)
+{
+ vector<JointEdge> ingoingEdges;
+ vector<JointEdge> outgoingEdges;
+ Joint::Id fromFirstOneStepAside = Joint::kInvalidId;
+ Joint::Id toFirstOneStepAside = Joint::kInvalidId;
+ if (!ApplyRestrictionPrepareData(from, to, centerId, ingoingEdges, outgoingEdges,
+ fromFirstOneStepAside, toFirstOneStepAside))
+ {
+ return;
+ }
+
+ // One ingoing edge case.
+ if (ingoingEdges.size() == 1)
+ {
+ DisableEdge(centerId, toFirstOneStepAside);
+ return;
+ }
+
+ // One outgoing edge case.
+ if (outgoingEdges.size() == 1)
+ {
+ DisableEdge(fromFirstOneStepAside, centerId);
+ return;
+ }
+
+ // Prohibition of moving from one segment to another in case of any number of ingoing and outgoing edges.
+ // The idea is to tranform the navigation graph for every non-degenerate case as it's shown below.
+ // At the picture below a restriction for prohibition moving from 4 to O to 3 is shown.
+ // So to implement it it's necessary to remove (disable) an edge 4-O and add features (edges) 4-N-1 and N-2.
+ //
+ // 1 2 3 1 2 3
+ // * * * * * *
+ // ↖ ^ ↗ ^↖ ↗^ ↗
+ // ↖ | ↗ | ↖ | ↗
+ // ↖ | ↗ |↗ ↖| ↗
+ // * O ==> N * * O
+ // ↗ ^ ↖ ^ ^ ↖
+ // ↗ | ↖ | | ↖
+ // ↗ | ↖ | | ↖
+ // * * * * * *
+ // 4 5 7 4 5 7
+
+ outgoingEdges.erase(remove_if(outgoingEdges.begin(), outgoingEdges.end(),
+ [&](JointEdge const & e)
+ {
+ // Removing edge N->3 in example above.
+ return e.GetTarget() == toFirstOneStepAside
+ // Preveting form adding in loop below
+ // cycles |fromFirstOneStepAside|->|centerId|->|fromFirstOneStepAside|.
+ || e.GetTarget() == fromFirstOneStepAside
+ // Removing edges |centerId|->|centerId|.
+ || e.GetTarget() == centerId;
+ }), outgoingEdges.end());
+
+ // Getting outgoing shortest edges geometry.
+ // Node. |centerId| could be connected with any outgoing joint with
+ // mote than one edge but only the shortest one should be takenn into
+ // account.
+ my::SortUnique(outgoingEdges, my::LessBy(&JointEdge::GetTarget),
+ my::EqualsBy(&JointEdge::GetTarget));
+ vector<JointEdgeGeom> outgoingShortestEdges;
+ GetOutgoingGeomEdges(outgoingEdges, centerId, outgoingShortestEdges);
+
+ vector<RoadPoint> shortestIngoingPath;
+ GetShortestConnectionPath(fromFirstOneStepAside, centerId, shortestIngoingPath);
+ JointEdgeGeom ingoingShortestEdge(fromFirstOneStepAside, shortestIngoingPath);
+
+ Joint::Id newJoint = Joint::kInvalidId;
+ for (auto it = outgoingShortestEdges.cbegin();
+ it != outgoingShortestEdges.cend(); ++it)
+ {
+ if (it == outgoingShortestEdges.cbegin())
+ {
+ // Adding the edge 4->N->1 in example above.
+ if (fromFirstOneStepAside == centerId || centerId == it->GetTarget())
+ {
+ // @TODO(bykoianko) In rare cases it's posible that outgoing edges staring from |centerId|
+ // contain as targets |centerId|. The same thing with ingoing edges.
+ // The most likely it's a consequence of adding
+ // restrictions with type no for some bidirectional roads. It's necessary to
+ // investigate this case, to undestand the reasons of appearing such edges clearly,
+ // prevent appearing of such edges and write unit tests on it.
+ return;
+ }
+ CHECK(!ingoingShortestEdge.GetShortestPath().empty(), ());
+ vector<RoadPoint> geometrySource(ingoingShortestEdge.GetShortestPath().cbegin(),
+ ingoingShortestEdge.GetShortestPath().cend() - 1);
+ geometrySource.insert(geometrySource.end(), it->GetShortestPath().cbegin(),
+ it->GetShortestPath().cend());
+
+ uint32_t const featureId = AddFakeFeature(fromFirstOneStepAside, it->GetTarget(), geometrySource);
+ newJoint = InsertJoint({featureId,
+ static_cast<uint32_t>(ingoingShortestEdge.GetShortestPath().size() - 1)
+ /* Intermediate joint of added feature */});
+ }
+ else
+ {
+ // Adding the edge N->2 in example above.
+ AddFakeFeature(newJoint, it->GetTarget(), it->GetShortestPath());
+ }
+ }
+
+ DisableEdge(fromFirstOneStepAside, centerId);
+}
+
+void IndexGraph::ApplyRestrictionOnly(RoadPoint const & from, RoadPoint const & to,
+ Joint::Id centerId)
+{
+ vector<JointEdge> ingoingEdges;
+ vector<JointEdge> outgoingEdges;
+ Joint::Id fromFirstOneStepAside = Joint::kInvalidId;
+ Joint::Id toFirstOneStepAside = Joint::kInvalidId;
+ if (!ApplyRestrictionPrepareData(from, to, centerId, ingoingEdges, outgoingEdges,
+ fromFirstOneStepAside, toFirstOneStepAside))
+ {
+ return;
+ }
+
+ // One outgoing edge case.
+ if (outgoingEdges.size() == 1)
+ {
+ for (auto const & e : ingoingEdges)
+ {
+ if (e.GetTarget() != fromFirstOneStepAside)
+ DisableEdge(e.GetTarget(), centerId);
+ }
+ return;
+ }
+
+ // One ingoing edge case.
+ if (ingoingEdges.size() == 1)
+ {
+ for (auto const & e : outgoingEdges)
+ {
+ if (e.GetTarget() != toFirstOneStepAside)
+ DisableEdge(centerId, e.GetTarget());
+ }
+ return;
+ }
+
+ // It's possible to move only from one segment to another in case of any number of ingoing and outgoing edges.
+ // The idea is to tranform the navigation graph for every non-degenerate case as it's shown below.
+ // At the picture below a restriction for permission moving only from 7 to O to 3 is shown.
+ // So to implement it it's necessary to remove (disable) an edge 7-O and add feature (edge) 4-N-3.
+ // Adding N is important for a route recovery stage. (The geometry of O will be copied to N.)
+ //
+ // 1 2 3 1 2 3
+ // * * * * * *
+ // ↖ ^ ↗ ↖ ^ ↗^
+ // ↖ | ↗ ↖ | ↗ |
+ // ↖ | ↗ ↖| ↗ |
+ // * O ==> * O * N
+ // ↗ ^ ↖ ↗^ ^
+ // ↗ | ↖ ↗ | |
+ // ↗ | ↖ ↗ | |
+ // * * * * * *
+ // 4 5 7 4 5 7
+
+ vector<RoadPoint> ingoingShortestPath;
+ GetShortestConnectionPath(fromFirstOneStepAside, centerId, ingoingShortestPath);
+ vector<RoadPoint> outgoingShortestPath;
+ GetShortestConnectionPath(centerId, toFirstOneStepAside, outgoingShortestPath);
+ CHECK_LESS(0, ingoingShortestPath.size(), ());
+ vector<RoadPoint> geometrySource(ingoingShortestPath.cbegin(), ingoingShortestPath.cend() - 1);
+ geometrySource.insert(geometrySource.end(), outgoingShortestPath.cbegin(), outgoingShortestPath.cend());
+
+ AddFakeFeature(fromFirstOneStepAside, toFirstOneStepAside, geometrySource);
+ DisableEdge(fromFirstOneStepAside, centerId);
+}
+
+void IndexGraph::ApplyRestrictions(RestrictionVec const & restrictions)
+{
+ for (Restriction const & restriction : restrictions)
+ {
+ if (restriction.m_featureIds.size() != 2)
+ {
+ LOG(LERROR, ("Only to link restriction are supported."));
+ continue;
+ }
+
+ RoadPoint from;
+ RoadPoint to;
+ Joint::Id jointId;
+ if (!m_roadIndex.GetAdjacentFtPoints(restriction.m_featureIds[0], restriction.m_featureIds[1],
+ from, to, jointId))
+ {
+ continue; // Restriction is not contain adjacent features.
+ }
+
+ try
+ {
+ switch (restriction.m_type)
+ {
+ case Restriction::Type::No: ApplyRestrictionNo(from, to, jointId); continue;
+ case Restriction::Type::Only: ApplyRestrictionOnly(from, to, jointId); continue;
+ }
+ }
+ catch(RootException const & e)
+ {
+ LOG(LERROR, ("Exception while applying restrictions. Message:", e.Msg()));
+ }
+ }
}
Joint::Id IndexGraph::InsertJoint(RoadPoint const & rp)
@@ -54,33 +444,48 @@ bool IndexGraph::JointLiesOnRoad(Joint::Id jointId, uint32_t featureId) const
inline void IndexGraph::GetNeighboringEdges(RoadPoint rp, bool isOutgoing,
vector<JointEdge> & edges) const
{
- RoadGeometry const & road = m_geometry.GetRoad(rp.GetFeatureId());
+ RoadGeometry const & road = GetRoad(rp.GetFeatureId());
if (!road.IsRoad())
return;
bool const bidirectional = !road.IsOneWay();
if (!isOutgoing || bidirectional)
- GetNeighboringEdge(road, rp, false /* forward */, edges);
+ GetNeighboringEdge(road, rp, false /* forward */, isOutgoing, edges);
if (isOutgoing || bidirectional)
- GetNeighboringEdge(road, rp, true /* forward */, edges);
+ GetNeighboringEdge(road, rp, true /* forward */, isOutgoing, edges);
}
inline void IndexGraph::GetNeighboringEdge(RoadGeometry const & road, RoadPoint rp, bool forward,
- vector<JointEdge> & edges) const
+ bool outgoing, vector<JointEdge> & edges) const
{
pair<Joint::Id, uint32_t> const & neighbor = m_roadIndex.FindNeighbor(rp, forward);
- if (neighbor.first != Joint::kInvalidId)
- {
- double const distance = m_estimator->CalcEdgesWeight(road, rp.GetPointId(), neighbor.second);
- edges.push_back({neighbor.first, distance});
- }
+ if (neighbor.first == Joint::kInvalidId)
+ return;
+
+ Joint::Id const rbJointId = m_roadIndex.GetJointId(rp);
+ auto const edge = outgoing ? make_pair(rbJointId, neighbor.first)
+ : make_pair(neighbor.first, rbJointId);
+ if (m_blockedEdges.find(edge) != m_blockedEdges.end())
+ return;
+
+ double const distance = m_estimator->CalcEdgesWeight(road, rp.GetPointId(), neighbor.second);
+ edges.push_back({neighbor.first, distance});
+}
+
+RoadGeometry const & IndexGraph::GetRoad(uint32_t featureId) const
+{
+ auto const it = m_fakeFeatureGeometry.find(featureId);
+ if (it != m_fakeFeatureGeometry.cend())
+ return it->second;
+
+ return m_geometry.GetRoad(featureId);
}
void IndexGraph::GetDirectedEdge(uint32_t featureId, uint32_t pointFrom, uint32_t pointTo,
Joint::Id target, bool forward, vector<JointEdge> & edges) const
{
- RoadGeometry const & road = m_geometry.GetRoad(featureId);
+ RoadGeometry const & road = GetRoad(featureId);
if (!road.IsRoad())
return;
diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp
index 0d7f576b58..413c8fbc54 100644
--- a/routing/index_graph.hpp
+++ b/routing/index_graph.hpp
@@ -6,6 +6,7 @@
#include "routing/joint_index.hpp"
#include "routing/road_index.hpp"
#include "routing/road_point.hpp"
+#include "routing/routing_serialization.hpp"
#include "coding/reader.hpp"
#include "coding/write_to_sink.hpp"
@@ -13,6 +14,7 @@
#include "geometry/point2d.hpp"
#include "std/cstdint.hpp"
+#include "std/set.hpp"
#include "std/shared_ptr.hpp"
#include "std/unique_ptr.hpp"
#include "std/utility.hpp"
@@ -29,8 +31,27 @@ public:
private:
// Target is vertex going to for outgoing edges, vertex going from for ingoing edges.
- Joint::Id const m_target;
- double const m_weight;
+ Joint::Id m_target;
+ double m_weight;
+};
+
+class JointEdgeGeom final
+{
+public:
+ JointEdgeGeom() = default;
+ JointEdgeGeom(Joint::Id target, vector<RoadPoint> & shortestPath)
+ : m_target(target), m_shortestPath(shortestPath)
+ {
+ }
+ Joint::Id GetTarget() const { return m_target; }
+ vector<RoadPoint> const & GetShortestPath() const { return m_shortestPath; }
+
+private:
+ // Target is vertex going to for outgoing edges, vertex going from for ingoing edges.
+ Joint::Id m_target = Joint::kInvalidId;
+ // Two joints can be connected with several path. But in case of find shortest routes
+ // only the shortest one should be considered.
+ vector<RoadPoint> m_shortestPath;
};
class IndexGraph final
@@ -48,11 +69,12 @@ public:
m2::PointD const & GetPoint(Joint::Id jointId) const;
Geometry const & GetGeometry() const { return m_geometry; }
+
EdgeEstimator const & GetEstimator() const { return *m_estimator; }
uint32_t GetNumRoads() const { return m_roadIndex.GetSize(); }
uint32_t GetNumJoints() const { return m_jointIndex.GetNumJoints(); }
uint32_t GetNumPoints() const { return m_jointIndex.GetNumPoints(); }
-
+ m2::PointD const & GetPoint(RoadPoint const & ftp) const;
void Import(vector<Joint> const & joints);
Joint::Id InsertJoint(RoadPoint const & rp);
bool JointLiesOnRoad(Joint::Id jointId, uint32_t featureId) const;
@@ -75,16 +97,108 @@ public:
{
uint32_t const jointsSize = ReadPrimitiveFromSource<uint32_t>(src);
m_roadIndex.Deserialize(src);
- m_jointIndex.Build(m_roadIndex, jointsSize);
+ Build(jointsSize);
}
+ Joint::Id GetJointIdForTesting(RoadPoint const & ftp) const {return m_roadIndex.GetJointId(ftp); }
+
+ /// \brief Disable all edges between |from| and |to| if they are different and adjacent.
+ /// \note Despit the fact that |from| and |to| could be connected with several edges
+ /// it's a rare case. In most cases |from| and |to| are connected with only one edge
+ /// if they are adjacent.
+ /// \note The method doesn't affect routing if |from| and |to| are not adjacent or
+ /// if one of them is equal to Joint::kInvalidId.
+ void DisableEdge(Joint::Id from, Joint::Id to) { m_blockedEdges.insert(make_pair(from, to)); }
+
+ /// \brief Connects joint |from| and |to| with a fake oneway feature. Geometry for the feature
+ /// points is taken from |geometrySource|. If |geometrySource| contains more than
+ /// two points the feature is created with intermediate (not joint) point(s).
+ /// \returns feature id which was added.
+ uint32_t AddFakeFeature(Joint::Id from, Joint::Id to, vector<RoadPoint> const & geometrySource);
+
+ /// \brief Adds restriction to navigation graph which says that it's prohibited to go from
+ /// |from| to |to|.
+ /// \note |from| and |to| could be only begining or ending feature points and they has to belong to
+ /// the same junction with |jointId|. That means features |from| and |to| has to be adjacent.
+ /// \note This method could be called only after |m_roads| have been loaded with the help of Deserialize()
+ /// or Import().
+ /// \note This method adds fake features with ids which follows immediately after real feature ids.
+ void ApplyRestrictionNo(RoadPoint const & from, RoadPoint const & to, Joint::Id jointId);
+
+ /// \brief Adds restriction to navigation graph which says that from feature |from| it's permitted only
+ /// to go to feature |to| all other ways starting form |form| is prohibited.
+ /// \note All notes which are valid for ApplyRestrictionNo() is valid for ApplyRestrictionOnly().
+ void ApplyRestrictionOnly(RoadPoint const & from, RoadPoint const & to, Joint::Id jointId);
+
+ /// \brief Add restrictions in |restrictions| to |m_ftPointIndex|.
+ void ApplyRestrictions(RestrictionVec const & restrictions);
+
+ /// \brief Fills |singleFeaturePath| with points from point |from| to point |to|
+ /// \note |from| and |to| has to belong the same feature.
+ /// \note The order on points in items of |connectionPaths| is from |from| to |to|.
+ void GetSingleFeaturePaths(RoadPoint from, RoadPoint to, vector<RoadPoint> & singleFeaturePath);
+
+ /// \brief Fills |connectionPaths| with all path from joint |from| to joint |to|.
+ /// If |from| and |to| don't belong to the same feature |connectionPaths| an exception
+ /// |RootException| with be raised.
+ /// If |from| and |to| belong to only one feature |connectionPaths| will have one item.
+ /// It's most common case.
+ /// If |from| and |to| could be connected by several feature |connectionPaths|
+ /// will have several items.
+ /// \note The order on points in items of |connectionPaths| is from |from| to |to|.
+ void GetConnectionPaths(Joint::Id from, Joint::Id to, vector<vector<RoadPoint>> & connectionPaths);
+
+ /// \brief Fills |shortestConnectionPath| with shortest path from joint |from| to joint |to|.
+ /// \note The order on points in |shortestConnectionPath| is from |from| to |to|.
+ /// \note In most cases the method doesn't load geometry to find the shortest path
+ /// because there's only one path between |from| and |to|.
+ /// \note if |shortestConnectionPath| could be filled after call of the method
+ /// an exception of type |RootException| is raised. It could happend,
+ /// for example, if |from| and |to| are connected with a pedestrian road
+ /// but a car route is looked for.
+ void GetShortestConnectionPath(Joint::Id from, Joint::Id to,
+ vector<RoadPoint> & shortestConnectionPath);
+
+ void GetOutgoingGeomEdges(vector<JointEdge> const & outgoingEdges, Joint::Id center,
+ vector<JointEdgeGeom> & outgoingGeomEdges);
+
+ void CreateFakeFeatureGeometry(vector<RoadPoint> const & geometrySource, RoadGeometry & geometry) const;
+
+ /// \returns RoadGeometry by a real or fake featureId.
+ RoadGeometry const & GetRoad(uint32_t featureId) const;
+
+ bool IsFakeFeature(uint32_t featureId) const { return featureId >= kStartFakeFeatureIds; }
+
+ static uint32_t const kStartFakeFeatureIds = 1024 * 1024 * 1024;
+
private:
void GetNeighboringEdge(RoadGeometry const & road, RoadPoint rp, bool forward,
- vector<JointEdge> & edges) const;
+ bool outgoing, vector<JointEdge> & edges) const;
+ void Build(uint32_t jointNumber);
+
+ double GetSpeed(RoadPoint ftp) const;
+
+ /// \brief Finds neghboring of |centerId| joint on feature id of |center|
+ /// which is contained in |edges| and fills |oneStepAside| with it.
+ /// \note If oneStepAside is empty no neghboring nodes were found.
+ /// \note Taking into account the way of setting restrictions almost always |oneStepAside|
+ /// will contain one or zero items. Besides the it it's posible to draw map
+ /// the (wrong) way |oneStepAside| will contain any number of items.
+ void FindOneStepAsideRoadPoint(RoadPoint const & center, Joint::Id centerId,
+ vector<JointEdge> const & edges, vector<Joint::Id> & oneStepAside) const;
+
+ bool ApplyRestrictionPrepareData(RoadPoint const & from, RoadPoint const & to, Joint::Id centerId,
+ vector<JointEdge> & ingoingEdges, vector<JointEdge> & outgoingEdges,
+ Joint::Id & fromFirstOneStepAside, Joint::Id & toFirstOneStepAside);
Geometry m_geometry;
shared_ptr<EdgeEstimator> m_estimator;
RoadIndex m_roadIndex;
JointIndex m_jointIndex;
+
+ set<pair<Joint::Id, Joint::Id>> m_blockedEdges;
+ uint32_t m_nextFakeFeatureId = kStartFakeFeatureIds;
+ // Mapping from fake feature id to fake feature geometry.
+ map<uint32_t, RoadGeometry> m_fakeFeatureGeometry;
};
} // namespace routing
diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp
index e58e63eec8..cb14ad8954 100644
--- a/routing/index_graph_starter.cpp
+++ b/routing/index_graph_starter.cpp
@@ -16,10 +16,10 @@ IndexGraphStarter::IndexGraphStarter(IndexGraph const & graph, RoadPoint startPo
m2::PointD const & IndexGraphStarter::GetPoint(Joint::Id jointId) const
{
if (jointId == m_start.m_fakeId)
- return m_graph.GetGeometry().GetPoint(m_start.m_point);
+ return m_graph.GetPoint(m_start.m_point);
if (jointId == m_finish.m_fakeId)
- return m_graph.GetGeometry().GetPoint(m_finish.m_point);
+ return m_graph.GetPoint(m_finish.m_point);
return m_graph.GetPoint(jointId);
}
@@ -136,7 +136,7 @@ void IndexGraphStarter::FindPointsWithCommonFeature(Joint::Id jointId0, Joint::I
if (rp0.GetFeatureId() != rp1.GetFeatureId())
return;
- RoadGeometry const & road = m_graph.GetGeometry().GetRoad(rp0.GetFeatureId());
+ RoadGeometry const & road = m_graph.GetRoad(rp0.GetFeatureId());
if (!road.IsRoad())
return;
@@ -149,7 +149,7 @@ void IndexGraphStarter::FindPointsWithCommonFeature(Joint::Id jointId0, Joint::I
{
// CalcEdgesWeight is very expensive.
// So calculate it only if second common feature found.
- RoadGeometry const & prevRoad = m_graph.GetGeometry().GetRoad(result0.GetFeatureId());
+ RoadGeometry const & prevRoad = m_graph.GetRoad(result0.GetFeatureId());
minWeight = m_graph.GetEstimator().CalcEdgesWeight(prevRoad, result0.GetPointId(),
result1.GetPointId());
}
diff --git a/routing/joint_index.cpp b/routing/joint_index.cpp
index b073848d97..8a0c64acd2 100644
--- a/routing/joint_index.cpp
+++ b/routing/joint_index.cpp
@@ -17,6 +17,23 @@ void JointIndex::AppendToJoint(Joint::Id jointId, RoadPoint rp)
m_dynamicJoints[jointId].AddPoint(rp);
}
+void JointIndex::FindPointsWithCommonFeature(Joint::Id jointId0, Joint::Id jointId1,
+ vector<pair<RoadPoint, RoadPoint>> & result) const
+{
+ result.clear();
+ ForEachPoint(jointId0, [&](RoadPoint const & rp0)
+ {
+ ForEachPoint(jointId1, [&](RoadPoint const & rp1)
+ {
+ if (rp0.GetFeatureId() == rp1.GetFeatureId())
+ result.emplace_back(rp0, rp1);
+ });
+ });
+
+ if (result.empty())
+ MYTHROW(RootException, ("Can't find common feature for joints", jointId0, jointId1));
+}
+
void JointIndex::Build(RoadIndex const & roadIndex, uint32_t numJoints)
{
// + 1 is protection for 'End' method from out of bounds.
diff --git a/routing/joint_index.hpp b/routing/joint_index.hpp
index 0e3c8c0441..cef629ef37 100644
--- a/routing/joint_index.hpp
+++ b/routing/joint_index.hpp
@@ -5,7 +5,7 @@
#include "routing/road_point.hpp"
#include "base/assert.hpp"
-
+#include "std/utility.hpp"
#include "std/vector.hpp"
namespace routing
@@ -43,6 +43,10 @@ public:
}
void Build(RoadIndex const & roadIndex, uint32_t numJoints);
+
+ /// \brief fills result with paths from |jointId0| to |jointId1|.
+ void FindPointsWithCommonFeature(Joint::Id jointId0, Joint::Id jointId1,
+ vector<pair<RoadPoint, RoadPoint>> & result) const;
Joint::Id InsertJoint(RoadPoint const & rp);
void AppendToJoint(Joint::Id jointId, RoadPoint rp);
diff --git a/routing/road_index.cpp b/routing/road_index.cpp
index e6668e8a0d..fbea8cba5f 100644
--- a/routing/road_index.cpp
+++ b/routing/road_index.cpp
@@ -18,6 +18,59 @@ void RoadIndex::Import(vector<Joint> const & joints)
}
}
+bool RoadIndex::GetAdjacentFtPoints(uint32_t featureIdFrom, uint32_t featureIdTo, RoadPoint & from,
+ RoadPoint & to, Joint::Id & jointId) const
+{
+ auto const fromIt = m_roads.find(featureIdFrom);
+ if (fromIt == m_roads.cend())
+ return false;
+
+ auto const toIt = m_roads.find(featureIdTo);
+ if (toIt == m_roads.cend())
+ return false;
+
+ RoadJointIds const & roadJointIdsFrom = fromIt->second;
+ RoadJointIds const & roadJointIdsTo = toIt->second;
+ if (roadJointIdsFrom.GetSize() == 0 || roadJointIdsTo.GetSize() == 0)
+ return false; // No sence in restrictions on features without joints.
+
+ // Note. It's important to check other variant besides a restriction from last segment
+ // of featureIdFrom to first segment of featureIdTo since two way features can have
+ // reverse point order.
+ if (roadJointIdsFrom.Back() == roadJointIdsTo.Front())
+ {
+ from = RoadPoint(featureIdFrom, roadJointIdsFrom.GetSize() - 1);
+ to = RoadPoint(featureIdTo, 0);
+ jointId = roadJointIdsFrom.Back();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Front() == roadJointIdsTo.Back())
+ {
+ from = RoadPoint(featureIdFrom, 0);
+ to = RoadPoint(featureIdTo, roadJointIdsTo.GetSize() - 1);
+ jointId = roadJointIdsFrom.Front();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Back() == roadJointIdsTo.Back())
+ {
+ from = RoadPoint(featureIdFrom, roadJointIdsFrom.GetSize() - 1);
+ to = RoadPoint(featureIdTo, roadJointIdsTo.GetSize() - 1);
+ jointId = roadJointIdsFrom.Back();
+ return true;
+ }
+
+ if (roadJointIdsFrom.Front() == roadJointIdsTo.Front())
+ {
+ from = RoadPoint(featureIdFrom, 0);
+ to = RoadPoint(featureIdTo, 0);
+ jointId = roadJointIdsFrom.Front();
+ return true;
+ }
+ return false; // |featureIdFrom| and |featureIdTo| are not adjacent.
+}
+
pair<Joint::Id, uint32_t> RoadIndex::FindNeighbor(RoadPoint rp, bool forward) const
{
auto const it = m_roads.find(rp.GetFeatureId());
diff --git a/routing/road_index.hpp b/routing/road_index.hpp
index 90bd61e70a..fd077538c3 100644
--- a/routing/road_index.hpp
+++ b/routing/road_index.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "routing/joint.hpp"
+#include "routing/routing_serialization.hpp"
#include "coding/reader.hpp"
#include "coding/write_to_sink.hpp"
@@ -74,8 +75,14 @@ public:
return make_pair(Joint::kInvalidId, 0);
}
- template <class Sink>
- void Serialize(Sink & sink) const
+ Joint::Id Front() const { return m_jointIds.front(); }
+
+ Joint::Id Back() const { return m_jointIds.back(); }
+
+ size_t GetSize() const { return m_jointIds.size(); }
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
{
WriteToSink(sink, static_cast<Joint::Id>(m_jointIds.size()));
for (Joint::Id jointId : m_jointIds)
@@ -106,6 +113,12 @@ class RoadIndex final
public:
void Import(vector<Joint> const & joints);
+ /// \brief if |featureIdFrom| and |featureIdTo| are adjacent and if they are connected by
+ /// its ends fills |from| and |to| with corresponding |from| and |to| and return true.
+ /// If not returns false.
+ bool GetAdjacentFtPoints(uint32_t featureIdFrom, uint32_t featureIdTo,
+ RoadPoint & from, RoadPoint & to, Joint::Id & jointId) const;
+
void AddJoint(RoadPoint rp, Joint::Id jointId)
{
m_roads[rp.GetFeatureId()].AddJoint(rp.GetPointId(), jointId);
@@ -158,6 +171,16 @@ public:
f(it.first, it.second);
}
+ template <typename F>
+ void ForEachJoint(uint32_t featureId, F && f) const
+ {
+ auto const it = m_roads.find(featureId);
+ if (it == m_roads.cend())
+ return;
+
+ it->second.ForEachJoint(f);
+ }
+
private:
// Map from feature id to RoadJointIds.
unordered_map<uint32_t, RoadJointIds> m_roads;
diff --git a/routing/road_point.hpp b/routing/road_point.hpp
index 654f89c675..da4303a80c 100644
--- a/routing/road_point.hpp
+++ b/routing/road_point.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "std/cstdint.hpp"
+#include "std/limits.hpp"
#include "std/sstream.hpp"
#include "std/string.hpp"
@@ -17,25 +18,31 @@ public:
RoadPoint(uint32_t featureId, uint32_t pointId) : m_featureId(featureId), m_pointId(pointId) {}
- uint32_t GetFeatureId() const { return m_featureId; }
-
- uint32_t GetPointId() const { return m_pointId; }
+ bool operator==(RoadPoint const & roadPoint) const
+ {
+ return m_featureId == roadPoint.m_featureId && m_pointId == roadPoint.m_pointId;
+ }
- bool operator==(RoadPoint const & rp) const
+ bool operator<(RoadPoint const & roadPoint) const
{
- return m_featureId == rp.m_featureId && m_pointId == rp.m_pointId;
+ if (m_featureId == roadPoint.m_featureId)
+ return m_pointId < roadPoint.m_pointId;
+ return m_featureId < roadPoint.m_featureId;
}
+ uint32_t GetFeatureId() const { return m_featureId; }
+
+ uint32_t GetPointId() const { return m_pointId; }
+
private:
uint32_t m_featureId;
uint32_t m_pointId;
};
-inline string DebugPrint(RoadPoint const & rp)
+inline string DebugPrint(RoadPoint const & roadPoint)
{
ostringstream out;
- out << "rp("
- << "(" << rp.GetFeatureId() << ", " << rp.GetPointId() << ")";
+ out << "RoadPoint[" << roadPoint.GetFeatureId() << ", " << roadPoint.GetPointId() << "]";
return out.str();
}
} // namespace routing
diff --git a/routing/routing_serialization.cpp b/routing/routing_serialization.cpp
index d1e5a3a366..107ae8cd96 100644
--- a/routing/routing_serialization.cpp
+++ b/routing/routing_serialization.cpp
@@ -44,8 +44,8 @@ string DebugPrint(Restriction::Type const & type) { return ToString(type); }
string DebugPrint(Restriction const & restriction)
{
ostringstream out;
- out << "m_featureIds:[" << ::DebugPrint(restriction.m_featureIds)
- << "] m_type:" << DebugPrint(restriction.m_type) << " ";
+ out << "Restriction [ m_featureIds: " << ::DebugPrint(restriction.m_featureIds)
+ << " m_type: " << DebugPrint(restriction.m_type) << " ]" << endl;
return out.str();
}
} // namespace routing
diff --git a/routing/routing_tests/index_graph_test.cpp b/routing/routing_tests/index_graph_test.cpp
index 3458b7cd8e..da2ff816cf 100644
--- a/routing/routing_tests/index_graph_test.cpp
+++ b/routing/routing_tests/index_graph_test.cpp
@@ -1,5 +1,7 @@
#include "testing/testing.hpp"
+#include "routing/routing_tests/index_graph_tools.hpp"
+
#include "routing/base/astar_algorithm.hpp"
#include "routing/car_model.hpp"
#include "routing/edge_estimator.hpp"
@@ -18,75 +20,20 @@
namespace
{
using namespace routing;
-
-class TestGeometryLoader final : public GeometryLoader
-{
-public:
- // GeometryLoader overrides:
- void Load(uint32_t featureId, RoadGeometry & road) const override;
-
- void AddRoad(uint32_t featureId, bool oneWay, RoadGeometry::Points const & points);
-
-private:
- unordered_map<uint32_t, RoadGeometry> m_roads;
-};
-
-void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) const
-{
- auto it = m_roads.find(featureId);
- if (it == m_roads.cend())
- return;
-
- road = it->second;
-}
-
-void TestGeometryLoader::AddRoad(uint32_t featureId, bool oneWay,
- RoadGeometry::Points const & points)
-{
- auto it = m_roads.find(featureId);
- if (it != m_roads.end())
- {
- ASSERT(false, ("Already contains feature", featureId));
- return;
- }
-
- m_roads[featureId] = RoadGeometry(oneWay, 1.0 /* speed */, points);
-}
-
-Joint MakeJoint(vector<RoadPoint> const & points)
-{
- Joint joint;
- for (auto const & point : points)
- joint.AddPoint(point);
-
- return joint;
-}
-
-shared_ptr<EdgeEstimator> CreateEstimator()
-{
- return EdgeEstimator::CreateForCar(*make_shared<CarModelFactory>()->GetVehicleModel());
-}
+using namespace routing_test;
void TestRoute(IndexGraph const & graph, RoadPoint const & start, RoadPoint const & finish,
size_t expectedLength, vector<RoadPoint> const * expectedRoute = nullptr)
{
- LOG(LINFO, ("Test route", start.GetFeatureId(), ",", start.GetPointId(), "=>",
- finish.GetFeatureId(), ",", finish.GetPointId()));
-
- AStarAlgorithm<IndexGraphStarter> algorithm;
- RoutingResult<Joint::Id> routingResult;
-
+ vector<RoadPoint> route;
IndexGraphStarter starter(graph, start, finish);
- auto const resultCode = algorithm.FindPath(starter, starter.GetStartJoint(),
- starter.GetFinishJoint(), routingResult, {}, {});
- TEST_EQUAL(resultCode, AStarAlgorithm<IndexGraphStarter>::Result::OK, ());
+ AStarAlgorithm<IndexGraphStarter>::Result const resultCode = CalculateRoute(starter, route);
- vector<RoadPoint> roadPoints;
- starter.RedressRoute(routingResult.path, roadPoints);
- TEST_EQUAL(roadPoints.size(), expectedLength, ());
+ TEST_EQUAL(resultCode, AStarAlgorithm<IndexGraphStarter>::Result::OK, ());
+ TEST_EQUAL(route.size(), expectedLength, ());
if (expectedRoute != nullptr)
- TEST_EQUAL(roadPoints, *expectedRoute, ());
+ TEST_EQUAL(route, *expectedRoute, ());
}
void TestEdges(IndexGraph const & graph, Joint::Id jointId,
@@ -244,6 +191,7 @@ UNIT_TEST(FindPathManhattan)
street.emplace_back(static_cast<double>(i), static_cast<double>(j));
avenue.emplace_back(static_cast<double>(j), static_cast<double>(i));
}
+
loader->AddRoad(i, false, street);
loader->AddRoad(i + kCitySize, false, avenue);
}
@@ -256,6 +204,7 @@ UNIT_TEST(FindPathManhattan)
for (uint32_t j = 0; j < kCitySize; ++j)
joints.emplace_back(MakeJoint({{i, j}, {j + kCitySize, i}}));
}
+
graph.Import(joints);
for (uint32_t startY = 0; startY < kCitySize; ++startY)
diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp
new file mode 100644
index 0000000000..eba158a256
--- /dev/null
+++ b/routing/routing_tests/index_graph_tools.cpp
@@ -0,0 +1,84 @@
+#include "routing/routing_tests/index_graph_tools.hpp"
+
+#include "testing/testing.hpp"
+
+#include "routing/car_model.hpp"
+
+namespace routing_test
+{
+using namespace routing;
+
+void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) const
+{
+ auto it = m_roads.find(featureId);
+ if (it == m_roads.cend())
+ return;
+
+ road = it->second;
+}
+
+void TestGeometryLoader::AddRoad(uint32_t featureId, bool oneWay,
+ RoadGeometry::Points const & points)
+{
+ auto it = m_roads.find(featureId);
+ if (it != m_roads.end())
+ {
+ ASSERT(false, ("Already contains feature", featureId));
+ return;
+ }
+
+ m_roads[featureId] = RoadGeometry(oneWay, 1.0 /* speed */, points);
+}
+
+Joint MakeJoint(vector<RoadPoint> const & points)
+{
+ Joint joint;
+ for (auto const & point : points)
+ joint.AddPoint(point);
+
+ return joint;
+}
+
+shared_ptr<EdgeEstimator> CreateEstimator()
+{
+ return EdgeEstimator::CreateForCar(*make_shared<CarModelFactory>()->GetVehicleModel());
+}
+
+AStarAlgorithm<IndexGraphStarter>::Result CalculateRoute(IndexGraphStarter const & starter,
+ vector<RoadPoint> & roadPoints)
+{
+ AStarAlgorithm<IndexGraphStarter> algorithm;
+ RoutingResult<Joint::Id> routingResult;
+ AStarAlgorithm<IndexGraphStarter>::Result const resultCode = algorithm.FindPath(
+ starter, starter.GetStartJoint(), starter.GetFinishJoint(), routingResult, {}, {});
+
+ starter.RedressRoute(routingResult.path, roadPoints);
+ return resultCode;
+}
+
+void TestRouteSegments(IndexGraphStarter const & starter,
+ AStarAlgorithm<IndexGraphStarter>::Result expectedRouteResult,
+ vector<RoadPoint> const & expectedRoute)
+{
+ vector<RoadPoint> route;
+ AStarAlgorithm<IndexGraphStarter>::Result const resultCode = CalculateRoute(starter, route);
+ TEST_EQUAL(resultCode, expectedRouteResult, ());
+ TEST_EQUAL(route, expectedRoute, ());
+}
+
+void TestRouteGeometry(IndexGraphStarter const & starter,
+ AStarAlgorithm<IndexGraphStarter>::Result expectedRouteResult,
+ vector<m2::PointD> const & expectedRouteGeom)
+{
+ vector<RoadPoint> route;
+ AStarAlgorithm<IndexGraphStarter>::Result const resultCode = CalculateRoute(starter, route);
+ TEST_EQUAL(resultCode, expectedRouteResult, ());
+ TEST_EQUAL(route.size(), expectedRouteGeom.size(), ());
+ for (size_t i = 0; i < route.size(); ++i)
+ {
+ RoadGeometry roadGeom = starter.GetGraph().GetRoad(route[i].GetFeatureId());
+ CHECK_LESS(route[i].GetPointId(), roadGeom.GetPointsCount(), ());
+ TEST_EQUAL(expectedRouteGeom[i], roadGeom.GetPoint(route[i].GetPointId()), ());
+ }
+}
+} // namespace routing_test
diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp
new file mode 100644
index 0000000000..7a5482430f
--- /dev/null
+++ b/routing/routing_tests/index_graph_tools.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "routing/edge_estimator.hpp"
+#include "routing/index_graph.hpp"
+#include "routing/index_graph_starter.hpp"
+#include "routing/road_point.hpp"
+
+#include "routing/base/astar_algorithm.hpp"
+
+#include "geometry/point2d.hpp"
+
+#include "std/algorithm.hpp"
+#include "std/shared_ptr.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/unordered_map.hpp"
+#include "std/vector.hpp"
+
+namespace routing_test
+{
+class TestGeometryLoader final : public routing::GeometryLoader
+{
+public:
+ // GeometryLoader overrides:
+ void Load(uint32_t featureId, routing::RoadGeometry & road) const override;
+
+ void AddRoad(uint32_t featureId, bool oneWay, routing::RoadGeometry::Points const & points);
+
+private:
+ unordered_map<uint32_t, routing::RoadGeometry> m_roads;
+};
+
+routing::Joint MakeJoint(vector<routing::RoadPoint> const & points);
+
+shared_ptr<routing::EdgeEstimator> CreateEstimator();
+
+routing::AStarAlgorithm<routing::IndexGraphStarter>::Result CalculateRoute(
+ routing::IndexGraphStarter const & graph, vector<routing::RoadPoint> & roadPoints);
+
+void TestRouteSegments(routing::IndexGraphStarter const & starter,
+ routing::AStarAlgorithm<routing::IndexGraphStarter>::Result expectedRouteResult,
+ vector<routing::RoadPoint> const & expectedRoute);
+
+void TestRouteGeometry(routing::IndexGraphStarter const & starter,
+ routing::AStarAlgorithm<routing::IndexGraphStarter>::Result expectedRouteResult,
+ vector<m2::PointD> const & expectedRouteGeom);
+} // 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..895481763e
--- /dev/null
+++ b/routing/routing_tests/restriction_test.cpp
@@ -0,0 +1,622 @@
+#include "testing/testing.hpp"
+
+#include "routing/routing_tests/index_graph_tools.hpp"
+
+#include "routing/car_model.hpp"
+
+#include "routing/geometry.hpp"
+#include "geometry/point2d.hpp"
+
+#include "std/unique_ptr.hpp"
+#include "std/vector.hpp"
+
+namespace
+{
+using namespace routing;
+using namespace routing_test;
+
+void TestRoutes(vector<RoadPoint> const & starts,
+ vector<RoadPoint> const & finishes,
+ vector<vector<RoadPoint>> const & expectedRoutes,
+ IndexGraph & graph)
+{
+ CHECK_EQUAL(starts.size(), expectedRoutes.size(), ());
+ CHECK_EQUAL(finishes.size(), expectedRoutes.size(), ());
+
+ for (size_t i = 0; i < expectedRoutes.size(); ++i)
+ {
+ IndexGraphStarter starter(graph, starts[i], finishes[i]);
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedRoutes[i]);
+ }
+}
+
+void EdgeTest(Joint::Id vertex, size_t expectedIntgoingNum, size_t expectedOutgoingNum,
+ IndexGraph & graph)
+{
+ vector<IndexGraphStarter::TEdgeType> ingoing;
+ graph.GetEdgesList(vertex, false /* is outgoing */, ingoing);
+ TEST_EQUAL(ingoing.size(), expectedIntgoingNum, ());
+
+ vector<IndexGraphStarter::TEdgeType> outgoing;
+ graph.GetEdgesList(vertex, true /* is outgoing */, outgoing);
+ TEST_EQUAL(outgoing.size(), expectedOutgoingNum, ());
+}
+
+// Finish
+// 2 *
+// ^ ↖
+// | F1
+// | ↖
+// 1 | *
+// F0 ↖
+// | F2
+// | ↖
+// 0 *<--F3---<--F3---* Start
+// 0 1 2
+// Note. F0, F1 and F2 are one segment features. F3 is a two segments feature.
+unique_ptr<IndexGraph> BuildTriangularGraph()
+{
+ auto const loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* featureId */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {0.0, 2.0}}));
+ loader->AddRoad(1 /* featureId */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 1.0}, {0.0, 2.0}}));
+ loader->AddRoad(2 /* featureId */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 2.0}, {1.0, 1.0}}));
+ loader->AddRoad(3 /* featureId */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}));
+ return loader;
+ };
+
+ vector<Joint> const joints = {MakeJoint({{2, 0}, {3, 0}}), /* 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}}) /* joint at point (0, 2) */
+ };
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ return graph;
+}
+
+// Route through triangular graph without any restrictions.
+UNIT_TEST(TriangularGraph)
+{
+ unique_ptr<IndexGraph> graph = BuildTriangularGraph();
+ IndexGraphStarter starter(*graph, RoadPoint(2, 0) /* start */, RoadPoint(1, 1) /* finish */);
+ vector<RoadPoint> const expectedRoute = {{2 /* feature id */, 0 /* seg id */},
+ {2, 1}, {1, 1}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedRoute);
+}
+
+// Route through triangular graph with feature 2 disabled.
+UNIT_TEST(TriangularGraph_DisableF2)
+{
+ unique_ptr<IndexGraph> graph = BuildTriangularGraph();
+ graph->DisableEdge(graph->GetJointIdForTesting({2 /* feature id */, 0 /* point id */}),
+ graph->GetJointIdForTesting({2, 1}));
+
+ IndexGraphStarter starter(*graph, RoadPoint(2, 0) /* start */, RoadPoint(1, 1) /* finish */);
+ vector<RoadPoint> const expectedRouteOneEdgeRemoved = {{3 /* feature id */, 0 /* seg id */},
+ {3, 1}, {3, 2}, {0, 1}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK,
+ expectedRouteOneEdgeRemoved);
+}
+
+// Route through triangular graph with restriction type no from feature 2 to feature 1.
+UNIT_TEST(TriangularGraph_RestrictionNoF2F1)
+{
+ unique_ptr<IndexGraph> graph = BuildTriangularGraph();
+ graph->ApplyRestrictionNo({2 /* feature id */, 1 /* seg id */}, {1, 0},
+ graph->GetJointIdForTesting({1, 0}));
+
+ IndexGraphStarter starter(*graph, RoadPoint(2, 0) /* start */, RoadPoint(1, 1) /* finish */);
+ vector<RoadPoint> const expectedRouteRestrictionF2F1No = {{3 /* feature id */, 0 /* seg id */},
+ {3, 1}, {3, 2}, {0, 1}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK,
+ expectedRouteRestrictionF2F1No);
+}
+
+// Finish
+// 2 *
+// ^ ↖
+// | ↖
+// | ↖
+// 1 | Fake adding one link feature
+// F0 ↖
+// | ↖
+// | ↖
+// 0 *<--F1---<--F1--* Start
+// 0 1 2
+// Note. F1 is a two setments feature. The others are one setment ones.
+unique_ptr<IndexGraph> BuildCornerGraph()
+{
+ auto const loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {0.0, 2.0}}));
+ loader->AddRoad(1 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}));
+ return loader;
+ };
+
+ vector<Joint> const joints =
+ {MakeJoint({{1 /* feature id */, 2 /* point id */}, {0, 0}}), /* joint at point (0, 0) */};
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ graph->InsertJoint({1 /* feature id */, 0 /* point id */}); // Start joint.
+ graph->InsertJoint({0 /* feature id */, 1 /* point id */}); // Finish joint.
+ return graph;
+}
+
+// Route through corner graph without any restrictions.
+UNIT_TEST(CornerGraph)
+{
+ unique_ptr<IndexGraph> graph = BuildCornerGraph();
+
+ // Route along F1 and F0.
+ IndexGraphStarter starter(*graph, RoadPoint(1, 0) /* start */, RoadPoint(0, 1) /* finish */);
+ vector<RoadPoint> const expectedRoute = {{1 /* feature id */, 0 /* point id */},
+ {1, 1}, {1, 2}, {0, 1}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedRoute);
+}
+
+// Generate geometry based on feature 1 of corner graph.
+UNIT_TEST(CornerGraph_CreateFakeFeature1Geometry)
+{
+ unique_ptr<IndexGraph> graph = BuildCornerGraph();
+
+ vector<vector<RoadPoint>> oneEdgeConnectionPaths;
+ graph->GetConnectionPaths(graph->GetJointIdForTesting({1 /* feature id */, 0 /* point id */}),
+ graph->GetJointIdForTesting({1, 2}), oneEdgeConnectionPaths);
+ TEST_EQUAL(oneEdgeConnectionPaths.size(), 1, ());
+
+ vector<RoadPoint> const expectedDirectOrder = {{1, 0}, {1, 1}, {1, 2}};
+ TEST_EQUAL(oneEdgeConnectionPaths[0], expectedDirectOrder, ());
+ RoadGeometry geometryDirect;
+ graph->CreateFakeFeatureGeometry(oneEdgeConnectionPaths[0], geometryDirect);
+ RoadGeometry expectedGeomentryDirect(true /* one way */, 1.0 /* speed */,
+ buffer_vector<m2::PointD, 32>({{2.0, 0.0}, {1.0, 0.0},
+ {0.0, 0.0}}));
+ TEST_EQUAL(geometryDirect, expectedGeomentryDirect, ());
+}
+
+// Generate geometry based on reversed feature 1 of corner graph.
+UNIT_TEST(CornerGraph_CreateFakeReversedFeature1Geometry)
+{
+ unique_ptr<IndexGraph> graph = BuildCornerGraph();
+
+ vector<vector<RoadPoint>> oneEdgeConnectionPaths;
+ graph->GetConnectionPaths(graph->GetJointIdForTesting({1 /* feature id */, 2 /* point id */}),
+ graph->GetJointIdForTesting({1, 0}), oneEdgeConnectionPaths);
+ TEST_EQUAL(oneEdgeConnectionPaths.size(), 1, ());
+
+ vector<RoadPoint> const expectedBackOrder = {{1, 2}, {1, 1}, {1, 0}};
+ TEST_EQUAL(oneEdgeConnectionPaths[0], expectedBackOrder, ());
+ RoadGeometry geometryBack;
+ graph->CreateFakeFeatureGeometry(oneEdgeConnectionPaths[0], geometryBack);
+ RoadGeometry expectedGeomentryBack(true /* one way */, 1.0 /* speed */,
+ buffer_vector<m2::PointD, 32>({{0.0, 0.0}, {1.0, 0.0},
+ {2.0, 0.0}}));
+ TEST_EQUAL(geometryBack, expectedGeomentryBack, ());
+}
+
+// Route through corner graph with adding a fake edge.
+UNIT_TEST(CornerGraph_AddFakeFeature)
+{
+ RoadPoint const kStart(1, 0);
+ RoadPoint const kFinish(0, 1);
+ unique_ptr<IndexGraph> graph = BuildCornerGraph();
+
+ graph->AddFakeFeature(graph->GetJointIdForTesting(kStart), graph->GetJointIdForTesting(kFinish),
+ {kStart, kFinish} /* geometrySource */);
+
+ IndexGraphStarter starter(*graph, kStart, kFinish);
+ vector<RoadPoint> const expectedRouteByFakeFeature = {{IndexGraph::kStartFakeFeatureIds, 0 /* seg id */},
+ {IndexGraph::kStartFakeFeatureIds, 1 /* seg id */}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedRouteByFakeFeature);
+}
+
+// Finish 2 Finish 1 Finish 0
+// 2 *<---F5----*<---F6---*
+// ^ ↖ ^ ↖ ^
+// | Fake-1 | Fake-0 |
+// | ↖ F1 ↖ F2
+// | ↖ | ↖ |
+// 1 F0 * *
+// | ^ ↖ ^
+// | F1 Fake-2 F2
+// | | ↖ |
+// 0 *<----F4---*<---F3----* Start
+// 0 1 2
+// Note. F1 and F2 are two segments features. The others are one segment ones.
+unique_ptr<IndexGraph> BuildTwoSquaresGraph()
+{
+ auto const loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {0.0, 2.0}}));
+ loader->AddRoad(1 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {1.0, 1.0}, {1.0, 2.0}}));
+ loader->AddRoad(2 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {2.0, 1.0}, {2.0, 2.0}}));
+ loader->AddRoad(3 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {1.0, 0.0}}));
+ loader->AddRoad(4 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {0.0, 0.0}}));
+ loader->AddRoad(5 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 2.0}, {0.0, 2.0}}));
+ loader->AddRoad(6 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 2.0}, {1.0, 2.0}}));
+ return loader;
+ };
+
+ vector<Joint> const joints = {
+ MakeJoint({{4 /* featureId */, 1 /* pointId */}, {0, 0}}), /* joint at point (0, 0) */
+ MakeJoint({{0, 1}, {5, 1}}), /* joint at point (0, 2) */
+ MakeJoint({{4, 0}, {1, 0}, {3, 1}}), /* joint at point (1, 0) */
+ MakeJoint({{5, 0}, {1, 2}, {6, 1}}), /* joint at point (1, 2) */
+ MakeJoint({{3, 0}, {2, 0}}), /* joint at point (2, 0) */
+ MakeJoint({{2, 2}, {6, 0}}), /* joint at point (2, 2) */
+ };
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ return graph;
+}
+
+// Route through two squares graph without any restrictions.
+UNIT_TEST(TwoSquaresGraph)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoSquaresGraph();
+
+ vector<RoadPoint> const starts = {{2 /* feature id */, 0 /* point id */}, {2, 0}, {2, 0}};
+ vector<RoadPoint> const finishes = {{6 /* feature id */, 0 /* point id */}, {6, 1}, {5, 1}};
+ vector<RoadPoint> const expectedRoute0 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {2, 2}};
+ vector<RoadPoint> const expectedRoute1 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {2, 2}, {6, 1}};
+ vector<RoadPoint> const expectedRoute2 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {2, 2}, {6, 1}, {5, 1}};
+
+ TestRoutes(starts, finishes, {expectedRoute0, expectedRoute1, expectedRoute2}, *graph);
+}
+
+// Route through two squares graph with adding a Fake-0 edge.
+UNIT_TEST(TwoSquaresGraph_AddFakeFeatureZero)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoSquaresGraph();
+ graph->AddFakeFeature(graph->InsertJoint({2 /* feature id */, 1 /* point id */}),
+ graph->GetJointIdForTesting({6 /* feature id */, 1 /* point id */}),
+ {{2, 1}, {6, 1}} /* geometrySource */);
+
+ vector<RoadPoint> const starts = {{2 /* feature id */, 0 /* point id */}, {2, 0}, {2, 0}};
+ vector<RoadPoint> const finishes = {{6 /* feature id */, 0 /* point id */}, {6, 1}, {5, 1}};
+ vector<RoadPoint> const expectedRoute0 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {2, 2}};
+ vector<RoadPoint> const expectedRoute1 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {IndexGraph::kStartFakeFeatureIds, 1}};
+ vector<RoadPoint> const expectedRoute2 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {IndexGraph::kStartFakeFeatureIds, 1}, {5, 1}};
+
+ TestRoutes(starts, finishes, {expectedRoute0, expectedRoute1, expectedRoute2}, *graph);
+}
+
+// Route through two squares graph with adding a Fake-0, Fake-1 and Fake-2 edge.
+UNIT_TEST(TwoSquaresGraph_AddFakeFeatureZeroOneTwo)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoSquaresGraph();
+ // Adding features: Fake 0, Fake 1 and Fake 2.
+ graph->AddFakeFeature(graph->InsertJoint({2 /* feature id */, 1 /* point id */}),
+ graph->GetJointIdForTesting({6 /* feature id */, 1 /* point id */}),
+ {{2, 1}, {6, 1}} /* geometrySource */);
+ graph->AddFakeFeature(graph->InsertJoint({1 /* feature id */, 1 /* point id */}),
+ graph->GetJointIdForTesting({5 /* feature id */, 1 /* point id */}),
+ {{1, 1}, {5, 1}} /* geometrySource */);
+ graph->AddFakeFeature(graph->GetJointIdForTesting({2 /* feature id */, 0 /* point id */}),
+ graph->GetJointIdForTesting({1 /* feature id */, 1 /* point id */}),
+ {{2, 0}, {1, 1}} /* geometrySource */);
+
+ vector<RoadPoint> const starts = {{2 /* feature id */, 0 /* point id */}, {2, 0}, {2, 0}};
+ vector<RoadPoint> const finishes = {{6 /* feature id */, 0 /* point id */}, {6, 1}, {5, 1}};
+ vector<RoadPoint> const expectedRoute0 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {2, 2}};
+ vector<RoadPoint> const expectedRoute1 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {IndexGraph::kStartFakeFeatureIds, 1}};
+ vector<RoadPoint> const expectedRoute2 = {{IndexGraph::kStartFakeFeatureIds + 2 /* Fake 2 */, 0},
+ {IndexGraph::kStartFakeFeatureIds + 2 /* Fake 2 */, 1},
+ {IndexGraph::kStartFakeFeatureIds + 1 /* Fake 1 */, 1}};
+ TestRoutes(starts, finishes, {expectedRoute0, expectedRoute1, expectedRoute2}, *graph);
+
+ // Disabling Fake-2 feature.
+ graph->DisableEdge(graph->GetJointIdForTesting({IndexGraph::kStartFakeFeatureIds + 2 /* Fake 2 */, 0}),
+ graph->GetJointIdForTesting({IndexGraph::kStartFakeFeatureIds + 2 /* Fake 2 */, 1}));
+ vector<RoadPoint> const expectedRoute2Disable2 = {{2 /* feature id */, 0 /* point id */},
+ {2, 1}, {IndexGraph::kStartFakeFeatureIds, 1}, {5, 1}};
+ TestRoutes(starts, finishes, {expectedRoute0, expectedRoute1, expectedRoute2Disable2}, *graph);
+}
+
+// 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<IndexGraph> BuildFlagGraph()
+{
+ auto const loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {1.0, 0.0}}));
+ loader->AddRoad(1 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {0.0, 0.0}}));
+ loader->AddRoad(2 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {0.0, 1.0}}));
+ loader->AddRoad(3 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {1.0, 1.0}}));
+ loader->AddRoad(4 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 1.0}, {0.5, 1.0}}));
+ loader->AddRoad(5 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.5, 1.0}, {1.0, 1.0}}));
+ return loader;
+ };
+
+ vector<Joint> 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}}), /* joint at point (1, 1) */
+ };
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ // Note. It's necessary to insert start joint because the edge F0 is used
+ // for creating restrictions.
+ graph->InsertJoint({0 /* feature id */, 0 /* point id */}); // start
+ return graph;
+}
+
+// @TODO(bykoianko) It's necessary to implement put several no restriction on the same junction
+// ingoing edges of the same jucntion. For example test with flag graph with two restriction
+// type no from F0 to F3 and form F0 to F1.
+
+// Route through flag graph without any restrictions.
+UNIT_TEST(FlagGraph)
+{
+ unique_ptr<IndexGraph> graph = BuildFlagGraph();
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(5, 0) /* finish */);
+ vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */},
+ {1, 0}, {1, 1}, {0.5, 1}};
+ TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom);
+}
+
+// Route through flag graph with one restriciton (type no) from F0 to F3.
+UNIT_TEST(FlagGraph_RestrictionF0F3No)
+{
+ unique_ptr<IndexGraph> graph = BuildFlagGraph();
+ Joint::Id const restictionCenterId = graph->GetJointIdForTesting({0, 1});
+
+ // Testing outgoing and ingoing edge number near restriction joint.
+ EdgeTest(restictionCenterId, 3 /* expectedIntgoingNum */, 3 /* expectedOutgoingNum */, *graph);
+ graph->ApplyRestrictionNo({0 /* feature id */, 1 /* point id */}, {3 /* feature id */, 0 /* point id */},
+ restictionCenterId);
+ EdgeTest(restictionCenterId, 2 /* expectedIntgoingNum */, 3 /* expectedOutgoingNum */, *graph);
+
+ // Testing route building after adding the restriction.
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(5, 0) /* finish */);
+ vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */},
+ {1, 0}, {0, 0}, {0, 1}, {0.5, 1}};
+ TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom);
+}
+
+// Route through flag graph with one restriciton (type only) from F0 to F1.
+UNIT_TEST(FlagGraph_RestrictionF0F1Only)
+{
+ unique_ptr<IndexGraph> graph = BuildFlagGraph();
+ Joint::Id const restictionCenterId = graph->GetJointIdForTesting({0, 1});
+ graph->ApplyRestrictionOnly({0 /* feature id */, 1 /* point id */}, {1 /* feature id */, 0 /* point id */},
+ restictionCenterId);
+
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(5, 0) /* finish */);
+ vector<RoadPoint> const expectedRoute = {{IndexGraph::kStartFakeFeatureIds, 0 /* point id */},
+ {IndexGraph::kStartFakeFeatureIds, 1},
+ {IndexGraph::kStartFakeFeatureIds, 2},
+ {2 /* feature id */, 1 /* point id */},
+ {4, 1}};
+ TestRouteSegments(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedRoute);
+}
+
+// 1 *-F4-*-F5-*---F6---* Finish
+// | |
+// 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<IndexGraph> BuildPosterGraph()
+{
+ auto loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{2.0, 0.0}, {1.0, 0.0}}));
+ loader->AddRoad(1 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {0.0, 0.0}}));
+ loader->AddRoad(2 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {0.0, 1.0}}));
+ loader->AddRoad(3 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 0.0}, {1.0, 1.0}}));
+ loader->AddRoad(4 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 1.0}, {0.5, 1.0}}));
+ loader->AddRoad(5 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{0.5, 1.0}, {1.0, 1.0}}));
+ loader->AddRoad(6 /* feature id */, false /* one way */, buffer_vector<m2::PointD, 32>(
+ {{1.0, 1.0}, {2.0, 1.0}}));
+ return loader;
+ };
+
+ vector<Joint> 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) */
+ };
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ // Note. It's necessary to insert start and finish joints because the edge F0 is used
+ // for creating restrictions.
+ graph->InsertJoint({0 /* feature id */, 0 /* point id */}); // start
+ graph->InsertJoint({6 /* feature id */, 1 /* point id */}); // finish
+
+ return graph;
+}
+
+// Route through poster graph without any restrictions.
+UNIT_TEST(PosterGraph)
+{
+ unique_ptr<IndexGraph> graph = BuildPosterGraph();
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(6, 1) /* finish */);
+ vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */},
+ {1, 0}, {1, 1}, {2, 1}};
+
+ TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom);
+}
+
+// Route through poster graph with restrictions F0-F3 (type no).
+UNIT_TEST(PosterGraph_RestrictionF0F3No)
+{
+ unique_ptr<IndexGraph> graph = BuildPosterGraph();
+ Joint::Id const restictionCenterId = graph->GetJointIdForTesting({0, 1});
+
+ graph->ApplyRestrictionNo({0 /* feature id */, 1 /* point id */}, {3 /* feature id */, 0 /* point id */},
+ restictionCenterId);
+
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(6, 1) /* finish */);
+ vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */},
+ {1, 0}, {0, 0}, {0, 1}, {0.5, 1}, {1, 1}, {2, 1}};
+ TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom);
+}
+
+// Route through poster graph with restrictions F0-F1 (type only).
+UNIT_TEST(PosterGraph_RestrictionF0F1Only)
+{
+ unique_ptr<IndexGraph> graph = BuildPosterGraph();
+ Joint::Id const restictionCenterId = graph->GetJointIdForTesting({0, 1});
+
+ graph->ApplyRestrictionOnly({0 /* feature id */, 1 /* point id */}, {1 /* feature id */, 0 /* point id */},
+ restictionCenterId);
+
+ IndexGraphStarter starter(*graph, RoadPoint(0, 0) /* start */, RoadPoint(6, 1) /* finish */);
+ vector<m2::PointD> const expectedGeom = {{2 /* x */, 0 /* y */},
+ {1, 0}, {0, 0}, {0, 1}, {0.5, 1}, {1, 1}, {2, 1}};
+ TestRouteGeometry(starter, AStarAlgorithm<IndexGraphStarter>::Result::OK, expectedGeom);
+}
+
+// 1 *--F1-->*
+// ↗ ↘
+// F1 F1
+// ↗ ↘
+// Start 0 *---F0--->-------F0----->* Finish
+// 0 1 2 3
+// Start
+// Note. F0 is a two setments feature. F1 is a three segment one.
+unique_ptr<IndexGraph> BuildTwoWayGraph()
+{
+ auto const loader = []()
+ {
+ unique_ptr<TestGeometryLoader> loader = make_unique<TestGeometryLoader>();
+ loader->AddRoad(0 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {1.0, 0.0}, {3.0, 0}}));
+ loader->AddRoad(1 /* feature id */, true /* oneWay */, buffer_vector<m2::PointD, 32>(
+ {{0.0, 0.0}, {1.0, 1.0}, {2.0, 1.0}, {3.0, 0.0}}));
+ return loader;
+ };
+
+ vector<Joint> const joints = {
+ MakeJoint({{0 /* feature id */, 0 /* point id */}, {1, 0}}), /* joint at point (0, 0) */
+ MakeJoint({{0 /* feature id */, 2 /* point id */}, {1, 3}})};
+
+ unique_ptr<IndexGraph> graph = make_unique<IndexGraph>(loader(), CreateEstimator());
+ graph->Import(joints);
+ return graph;
+}
+
+UNIT_TEST(TwoWay_GetSingleFeaturePaths)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoWayGraph();
+ vector<RoadPoint> singleFeaturePath;
+
+ // Full feature 0.
+ graph->GetSingleFeaturePaths({0 /* feature id */, 0 /* point id */}, {0, 2}, singleFeaturePath);
+ vector<RoadPoint> const expectedF0 = {{0 /* feature id */, 0 /* point id */}, {0, 1}, {0, 2}};
+ TEST_EQUAL(singleFeaturePath, expectedF0, ());
+
+ // Full feature 1.
+ graph->GetSingleFeaturePaths({1 /* feature id */, 0 /* point id */}, {1, 3}, singleFeaturePath);
+ vector<RoadPoint> const expectedF1 = {{1 /* feature id */, 0 /* point id */}, {1, 1},
+ {1, 2}, {1, 3}};
+ TEST_EQUAL(singleFeaturePath, expectedF1, ());
+
+ // Full feature 1 in reversed order.
+ graph->GetSingleFeaturePaths({1 /* feature id */, 3 /* point id */}, {1, 0}, singleFeaturePath);
+ vector<RoadPoint> const expectedReversedF1 = {{1 /* feature id */, 3 /* point id */}, {1, 2},
+ {1, 1}, {1, 0}};
+ TEST_EQUAL(singleFeaturePath, expectedReversedF1, ());
+
+ // Part of feature 0.
+ graph->GetSingleFeaturePaths({0 /* feature id */, 1 /* point id */}, {0, 2}, singleFeaturePath);
+ vector<RoadPoint> const expectedPartF0 = {{0 /* feature id */, 1 /* point id */}, {0, 2}};
+ TEST_EQUAL(singleFeaturePath, expectedPartF0, ());
+
+ // Part of feature 1 in reversed order.
+ graph->GetSingleFeaturePaths({1 /* feature id */, 2 /* point id */}, {1, 1}, singleFeaturePath);
+ vector<RoadPoint> const expectedPartF1 = {{1 /* feature id */, 2 /* point id */}, {1, 1}};
+ TEST_EQUAL(singleFeaturePath, expectedPartF1, ());
+
+ // Single point test.
+ graph->GetSingleFeaturePaths({0 /* feature id */, 0 /* point id */}, {0, 0}, singleFeaturePath);
+ vector<RoadPoint> const expectedSinglePoint = {{0 /* feature id */, 0 /* point id */}};
+ TEST_EQUAL(singleFeaturePath, expectedSinglePoint, ());
+}
+
+UNIT_TEST(TwoWay_GetConnectionPaths)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoWayGraph();
+ vector<vector<RoadPoint>> connectionPaths;
+
+ graph->GetConnectionPaths(graph->GetJointIdForTesting({1 /* feature id */, 0 /* point id */}),
+ graph->GetJointIdForTesting({0, 2}), connectionPaths);
+ vector<vector<RoadPoint>> const expectedConnectionPaths = {
+ {{0, 0}, {0, 1}, {0, 2}}, // feature 0
+ {{1, 0}, {1, 1}, {1, 2}, {1, 3}}, // feature 1
+ };
+ sort(connectionPaths.begin(), connectionPaths.end());
+ TEST_EQUAL(connectionPaths, expectedConnectionPaths, ());
+}
+
+UNIT_TEST(TwoWay_GetShortestConnectionPath)
+{
+ unique_ptr<IndexGraph> graph = BuildTwoWayGraph();
+ vector<RoadPoint> shortestPath;
+
+ graph->GetShortestConnectionPath(graph->GetJointIdForTesting({0 /* feature id */, 0 /* point id */}),
+ graph->GetJointIdForTesting({1, 3}), shortestPath);
+ vector<RoadPoint> const expectedShortestPath = {
+ {0, 0}, {0, 1}, {0, 2}, // feature 0
+ };
+ TEST_EQUAL(shortestPath, expectedShortestPath, ());
+}
+} // namespace
diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro
index 100399f067..b170fc5d4f 100644
--- a/routing/routing_tests/routing_tests.pro
+++ b/routing/routing_tests/routing_tests.pro
@@ -28,6 +28,7 @@ SOURCES += \
cross_routing_tests.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 \
@@ -40,6 +41,8 @@ SOURCES += \
turns_sound_test.cpp \
turns_tts_text_tests.cpp \
vehicle_model_test.cpp \
+ restriction_test.cpp \
HEADERS += \
+ index_graph_tools.hpp \
road_graph_builder.hpp \
diff --git a/routing/single_mwm_router.cpp b/routing/single_mwm_router.cpp
index f139a1fc5a..72dd05eddc 100644
--- a/routing/single_mwm_router.cpp
+++ b/routing/single_mwm_router.cpp
@@ -8,6 +8,7 @@
#include "routing/index_graph.hpp"
#include "routing/index_graph_starter.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"
@@ -38,10 +39,9 @@ vector<Junction> ConvertToJunctions(IndexGraphStarter const & starter,
vector<Junction> junctions;
junctions.reserve(roadPoints.size());
- Geometry const & geometry = starter.GetGraph().GetGeometry();
// TODO: Use real altitudes for pedestrian and bicycle routing.
for (RoadPoint const & point : roadPoints)
- junctions.emplace_back(geometry.GetPoint(point), feature::kDefaultAltitudeMeters);
+ junctions.emplace_back(starter.GetGraph().GetPoint(point), feature::kDefaultAltitudeMeters);
return junctions;
}
@@ -117,7 +117,7 @@ IRouter::ResultCode SingleMwmRouter::DoCalculateRoute(MwmSet::MwmId const & mwmI
IndexGraphStarter starter(graph, start, finish);
AStarProgress progress(0, 100);
- progress.Initialize(graph.GetGeometry().GetPoint(start), graph.GetGeometry().GetPoint(finish));
+ progress.Initialize(graph.GetPoint(start), graph.GetPoint(finish));
uint32_t drawPointsStep = 0;
auto onVisitJunction = [&](Joint::Id const & from, Joint::Id const & to) {
@@ -198,6 +198,10 @@ bool SingleMwmRouter::LoadIndex(MwmSet::MwmId const & mwmId, string const & coun
feature::RoutingSectionHeader header;
header.Deserialize(src);
graph.Deserialize(src);
+ RestrictionLoader restrictionLoader(*mwmValue);
+ if (restrictionLoader.HasRestrictions())
+ graph.ApplyRestrictions(restrictionLoader.GetRestrictions());
+
LOG(LINFO,
(ROUTING_FILE_TAG, "section for", country, "loaded in", timer.ElapsedSeconds(), "seconds"));
return true;