Merge pull request #3025 from bykoianko/master-bicycle-directions-first-appr2

Bicycle turn generation.
This commit is contained in:
Konstantin Shalnev 2016-05-20 18:11:17 +03:00
commit b803754b1b
27 changed files with 781 additions and 293 deletions

View file

@ -4,11 +4,105 @@
#include "indexer/feature_data.hpp"
#include "indexer/classificator.hpp"
#include "std/map.hpp"
#include "std/sstream.hpp"
#include "std/utility.hpp"
namespace
{
class HighwayClasses
{
map<uint32_t, ftypes::HighwayClass> m_map;
public:
HighwayClasses()
{
auto const & c = classif();
m_map[c.GetTypeByPath({"highway", "motorway"})] = ftypes::HighwayClass::Trunk;
m_map[c.GetTypeByPath({"highway", "motorway_link"})] = ftypes::HighwayClass::Trunk;
m_map[c.GetTypeByPath({"highway", "trunk"})] = ftypes::HighwayClass::Trunk;
m_map[c.GetTypeByPath({"highway", "trunk_link"})] = ftypes::HighwayClass::Trunk;
m_map[c.GetTypeByPath({"route", "ferry"})] = ftypes::HighwayClass::Trunk;
m_map[c.GetTypeByPath({"highway", "primary"})] = ftypes::HighwayClass::Primary;
m_map[c.GetTypeByPath({"highway", "primary_link"})] = ftypes::HighwayClass::Primary;
m_map[c.GetTypeByPath({"highway", "secondary"})] = ftypes::HighwayClass::Secondary;
m_map[c.GetTypeByPath({"highway", "secondary_link"})] = ftypes::HighwayClass::Secondary;
m_map[c.GetTypeByPath({"highway", "tertiary"})] = ftypes::HighwayClass::Tertiary;
m_map[c.GetTypeByPath({"highway", "tertiary_link"})] = ftypes::HighwayClass::Tertiary;
m_map[c.GetTypeByPath({"highway", "unclassified"})] = ftypes::HighwayClass::LivingStreet;
m_map[c.GetTypeByPath({"highway", "residential"})] = ftypes::HighwayClass::LivingStreet;
m_map[c.GetTypeByPath({"highway", "living_street"})] = ftypes::HighwayClass::LivingStreet;
m_map[c.GetTypeByPath({"highway", "road"})] = ftypes::HighwayClass::LivingStreet;
m_map[c.GetTypeByPath({"highway", "service"})] = ftypes::HighwayClass::Service;
m_map[c.GetTypeByPath({"highway", "track"})] = ftypes::HighwayClass::Service;
m_map[c.GetTypeByPath({"highway", "pedestrian"})] = ftypes::HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "footway"})] = ftypes::HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "bridleway"})] = ftypes::HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "steps"})] = ftypes::HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "cycleway"})] = ftypes::HighwayClass::Pedestrian;
m_map[c.GetTypeByPath({"highway", "path"})] = ftypes::HighwayClass::Pedestrian;
}
ftypes::HighwayClass Get(uint32_t t) const
{
auto const it = m_map.find(t);
if (it == m_map.cend())
return ftypes::HighwayClass::Error;
return it->second;
}
};
char const * HighwayClassToString(ftypes::HighwayClass const cls)
{
switch (cls)
{
case ftypes::HighwayClass::Undefined: return "Undefined";
case ftypes::HighwayClass::Error: return "Error";
case ftypes::HighwayClass::Trunk: return "Trunk";
case ftypes::HighwayClass::Primary: return "Primary";
case ftypes::HighwayClass::Secondary: return "Secondary";
case ftypes::HighwayClass::Tertiary: return "Tertiary";
case ftypes::HighwayClass::LivingStreet: return "LivingStreet";
case ftypes::HighwayClass::Service: return "Service";
case ftypes::HighwayClass::Pedestrian: return "Pedestrian";
case ftypes::HighwayClass::Count: return "Count";
}
ASSERT(false, ());
return "";
}
} // namespace
namespace ftypes
{
string DebugPrint(HighwayClass const cls)
{
stringstream out;
out << "[ " << HighwayClassToString(cls) << " ]";
return out.str();
}
HighwayClass GetHighwayClass(feature::TypesHolder const & types)
{
uint8_t constexpr kTruncLevel = 2;
static HighwayClasses highwayClasses;
for (auto t : types)
{
ftype::TruncValue(t, kTruncLevel);
HighwayClass const hc = highwayClasses.Get(t);
if (hc != HighwayClass::Error)
return hc;
}
return HighwayClass::Error;
}
uint32_t BaseChecker::PrepareToMatch(uint32_t type, uint8_t level)
{
ftype::TruncValue(type, level);
@ -386,75 +480,4 @@ bool IsTypeConformed(uint32_t type, StringIL const & path)
}
return true;
}
string DebugPrint(HighwayClass const cls)
{
stringstream out;
out << "[ ";
switch (cls)
{
case HighwayClass::Undefined:
out << "Undefined";
case HighwayClass::Error:
out << "Error";
case HighwayClass::Trunk:
out << "Trunk";
case HighwayClass::Primary:
out << "Primary";
case HighwayClass::Secondary:
out << "Secondary";
case HighwayClass::Tertiary:
out << "Tertiary";
case HighwayClass::LivingStreet:
out << "LivingStreet";
case HighwayClass::Service:
out << "Service";
case HighwayClass::Count:
out << "Count";
default:
out << "Unknown value of HighwayClass: " << static_cast<int>(cls);
}
out << " ]";
return out.str();
}
HighwayClass GetHighwayClass(feature::TypesHolder const & types)
{
Classificator const & c = classif();
static pair<HighwayClass, uint32_t> const kHighwayClasses[] = {
{HighwayClass::Trunk, c.GetTypeByPath({"highway", "motorway"})},
{HighwayClass::Trunk, c.GetTypeByPath({"highway", "motorway_link"})},
{HighwayClass::Trunk, c.GetTypeByPath({"highway", "trunk"})},
{HighwayClass::Trunk, c.GetTypeByPath({"highway", "trunk_link"})},
{HighwayClass::Trunk, c.GetTypeByPath({"route", "ferry"})},
{HighwayClass::Primary, c.GetTypeByPath({"highway", "primary"})},
{HighwayClass::Primary, c.GetTypeByPath({"highway", "primary_link"})},
{HighwayClass::Secondary, c.GetTypeByPath({"highway", "secondary"})},
{HighwayClass::Secondary, c.GetTypeByPath({"highway", "secondary_link"})},
{HighwayClass::Tertiary, c.GetTypeByPath({"highway", "tertiary"})},
{HighwayClass::Tertiary, c.GetTypeByPath({"highway", "tertiary_link"})},
{HighwayClass::LivingStreet, c.GetTypeByPath({"highway", "unclassified"})},
{HighwayClass::LivingStreet, c.GetTypeByPath({"highway", "residential"})},
{HighwayClass::LivingStreet, c.GetTypeByPath({"highway", "living_street"})},
{HighwayClass::Service, c.GetTypeByPath({"highway", "service"})},
{HighwayClass::Service, c.GetTypeByPath({"highway", "track"})}};
uint8_t const kTruncLevel = 2;
for (auto t : types)
{
ftype::TruncValue(t, kTruncLevel);
for (auto const & cls : kHighwayClasses)
{
if (cls.second == t)
return cls.first;
}
}
return HighwayClass::Error;
}
HighwayClass GetHighwayClass(FeatureType const & ft)
{
return GetHighwayClass(feature::TypesHolder(ft));
}
}
} // namespace ftypes

View file

@ -205,12 +205,11 @@ enum class HighwayClass
Tertiary,
LivingStreet,
Service,
Count // This value is used for internals only.
Pedestrian,
Count // This value is used for internals only.
};
string DebugPrint(HighwayClass const cls);
HighwayClass GetHighwayClass(feature::TypesHolder const & types);
HighwayClass GetHighwayClass(FeatureType const & ft);
} // namespace ftypes

View file

@ -0,0 +1,215 @@
#include "routing/bicycle_directions.hpp"
#include "routing/car_model.hpp"
#include "routing/router_delegate.hpp"
#include "routing/routing_result_graph.hpp"
#include "routing/turns_generator.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "indexer/index.hpp"
#include "indexer/scales.hpp"
#include "geometry/point2d.hpp"
namespace
{
using namespace routing;
using namespace routing::turns;
class RoutingResult : public IRoutingResult
{
public:
RoutingResult(IRoadGraph::TEdgeVector const & routeEdges,
BicycleDirectionsEngine::TAdjacentEdgesMap const & adjacentEdges,
TUnpackedPathSegments const & pathSegments)
: m_routeEdges(routeEdges)
, m_adjacentEdges(adjacentEdges)
, m_pathSegments(pathSegments)
, m_routeLength(0)
{
for (auto const & edge : routeEdges)
{
m_routeLength += MercatorBounds::DistanceOnEarth(edge.GetStartJunction().GetPoint(),
edge.GetEndJunction().GetPoint());
}
}
// turns::IRoutingResult overrides:
TUnpackedPathSegments const & GetSegments() const override { return m_pathSegments; }
void GetPossibleTurns(TNodeId node, m2::PointD const & ingoingPoint,
m2::PointD const & junctionPoint, size_t & ingoingCount,
TurnCandidates & outgoingTurns) const override
{
ingoingCount = 0;
outgoingTurns.candidates.clear();
auto const adjacentEdges = m_adjacentEdges.find(node);
if (adjacentEdges == m_adjacentEdges.cend())
{
ASSERT(false, ());
return;
}
ingoingCount = adjacentEdges->second.m_ingoingTurnsCount;
outgoingTurns.candidates = adjacentEdges->second.m_outgoingTurns.candidates;
}
double GetPathLength() const override { return m_routeLength; }
m2::PointD const & GetStartPoint() const override
{
CHECK(!m_routeEdges.empty(), ());
return m_routeEdges.front().GetStartJunction().GetPoint();
}
m2::PointD const & GetEndPoint() const override
{
CHECK(!m_routeEdges.empty(), ());
return m_routeEdges.back().GetEndJunction().GetPoint();
}
private:
IRoadGraph::TEdgeVector const & m_routeEdges;
BicycleDirectionsEngine::TAdjacentEdgesMap const & m_adjacentEdges;
TUnpackedPathSegments const & m_pathSegments;
double m_routeLength;
};
} // namespace
namespace routing
{
BicycleDirectionsEngine::BicycleDirectionsEngine(Index const & index) : m_index(index) {}
void BicycleDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times, Route::TTurns & turns,
vector<m2::PointD> & routeGeometry,
my::Cancellable const & cancellable)
{
times.clear();
turns.clear();
routeGeometry.clear();
m_adjacentEdges.clear();
m_pathSegments.clear();
size_t const pathSize = path.size();
if (pathSize == 0)
return;
auto emptyPathWorkaround = [&]()
{
turns.emplace_back(pathSize - 1, turns::TurnDirection::ReachedYourDestination);
this->m_adjacentEdges[0] = AdjacentEdges(1); // There's one ingoing edge to the finish.
};
if (pathSize == 1)
{
emptyPathWorkaround();
return;
}
CalculateTimes(graph, path, times);
IRoadGraph::TEdgeVector routeEdges;
if (!ReconstructPath(graph, path, routeEdges, cancellable))
{
LOG(LDEBUG, ("Couldn't reconstruct path."));
emptyPathWorkaround();
return;
}
if (routeEdges.empty())
{
emptyPathWorkaround();
return;
}
// Filling |m_adjacentEdges|.
m_adjacentEdges.insert(make_pair(0, AdjacentEdges(0)));
for (size_t i = 1; i < pathSize; ++i)
{
Junction const & prevJunction = path[i - 1];
Junction const & currJunction = path[i];
IRoadGraph::TEdgeVector outgoingEdges, ingoingEdges;
graph.GetOutgoingEdges(currJunction, outgoingEdges);
graph.GetIngoingEdges(currJunction, ingoingEdges);
AdjacentEdges adjacentEdges = AdjacentEdges(ingoingEdges.size());
// Outgoing edge angle is not used for bicyle routing.
adjacentEdges.m_outgoingTurns.isCandidatesAngleValid = false;
adjacentEdges.m_outgoingTurns.candidates.reserve(outgoingEdges.size());
ASSERT_EQUAL(routeEdges.size(), pathSize - 1, ());
FeatureID const inFeatureId = routeEdges[i - 1].GetFeatureId();
for (auto const & edge : outgoingEdges)
{
auto const & outFeatureId = edge.GetFeatureId();
// Checking for if |edge| is a fake edge.
if (!outFeatureId.IsValid())
continue;
adjacentEdges.m_outgoingTurns.candidates.emplace_back(0.0 /* angle */, outFeatureId.m_index,
GetHighwayClass(outFeatureId));
}
LoadedPathSegment pathSegment;
if (inFeatureId.IsValid())
{
LoadPathGeometry(inFeatureId, {prevJunction.GetPoint(), currJunction.GetPoint()},
pathSegment);
}
m_adjacentEdges.insert(make_pair(inFeatureId.m_index, move(adjacentEdges)));
m_pathSegments.push_back(move(pathSegment));
}
RoutingResult resultGraph(routeEdges, m_adjacentEdges, m_pathSegments);
RouterDelegate delegate;
Route::TTimes turnAnnotationTimes;
Route::TStreets streetNames;
MakeTurnAnnotation(resultGraph, delegate, routeGeometry, turns, turnAnnotationTimes, streetNames);
}
Index::FeaturesLoaderGuard & BicycleDirectionsEngine::GetLoader(MwmSet::MwmId const & id)
{
if (!m_loader || id != m_loader->GetId())
m_loader = make_unique<Index::FeaturesLoaderGuard>(m_index, id);
return *m_loader;
}
ftypes::HighwayClass BicycleDirectionsEngine::GetHighwayClass(FeatureID const & featureId)
{
FeatureType ft;
GetLoader(featureId.m_mwmId).GetFeatureByIndex(featureId.m_index, ft);
auto const highwayClass = ftypes::GetHighwayClass(ft);
ASSERT_NOT_EQUAL(highwayClass, ftypes::HighwayClass::Error, ());
ASSERT_NOT_EQUAL(highwayClass, ftypes::HighwayClass::Undefined, ());
return highwayClass;
}
void BicycleDirectionsEngine::LoadPathGeometry(FeatureID const & featureId,
vector<m2::PointD> const & path,
LoadedPathSegment & pathSegment)
{
pathSegment.Clear();
if (!featureId.IsValid())
{
ASSERT(false, ());
return;
}
FeatureType ft;
GetLoader(featureId.m_mwmId).GetFeatureByIndex(featureId.m_index, ft);
auto const highwayClass = ftypes::GetHighwayClass(ft);
ASSERT_NOT_EQUAL(highwayClass, ftypes::HighwayClass::Error, ());
ASSERT_NOT_EQUAL(highwayClass, ftypes::HighwayClass::Undefined, ());
pathSegment.m_highwayClass = highwayClass;
pathSegment.m_isLink = ftypes::IsLinkChecker::Instance()(ft);
ft.GetName(FeatureType::DEFAULT_LANG, pathSegment.m_name);
pathSegment.m_nodeId = featureId.m_index;
pathSegment.m_onRoundabout = ftypes::IsRoundAboutChecker::Instance()(ft);
pathSegment.m_path = path;
// @TODO(bykoianko) It's better to fill pathSegment.m_weight.
}
} // namespace routing

View file

@ -0,0 +1,45 @@
#pragma once
#include "routing/directions_engine.hpp"
#include "routing/loaded_path_segment.hpp"
#include "routing/turn_candidate.hpp"
#include "std/map.hpp"
#include "std/unique_ptr.hpp"
class Index;
namespace routing
{
class BicycleDirectionsEngine : public IDirectionsEngine
{
public:
struct AdjacentEdges
{
explicit AdjacentEdges(size_t ingoingTurnsCount = 0) : m_ingoingTurnsCount(ingoingTurnsCount) {}
turns::TurnCandidates m_outgoingTurns;
size_t m_ingoingTurnsCount;
};
using TAdjacentEdgesMap = map<TNodeId, AdjacentEdges>;
BicycleDirectionsEngine(Index const & index);
// IDirectionsEngine override:
void Generate(IRoadGraph const & graph, vector<Junction> const & path, Route::TTimes & times,
Route::TTurns & turns, vector<m2::PointD> & routeGeometry,
my::Cancellable const & cancellable) override;
private:
Index::FeaturesLoaderGuard & GetLoader(MwmSet::MwmId const & id);
ftypes::HighwayClass GetHighwayClass(FeatureID const & featureId);
void LoadPathGeometry(FeatureID const & featureId, vector<m2::PointD> const & path,
LoadedPathSegment & pathSegment);
TAdjacentEdgesMap m_adjacentEdges;
TUnpackedPathSegments m_pathSegments;
Index const & m_index;
unique_ptr<Index::FeaturesLoaderGuard> m_loader;
};
} // namespace routing

View file

@ -0,0 +1,90 @@
#include "routing/directions_engine.hpp"
#include "base/assert.hpp"
namespace
{
double constexpr KMPH2MPS = 1000.0 / (60 * 60);
} // namespace
namespace routing
{
void IDirectionsEngine::CalculateTimes(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times) const
{
times.clear();
if (path.size() <= 1)
return;
// graph.GetMaxSpeedKMPH() below is used on purpose.
// The idea is while pedestian (bicycle) routing ways for pedestrians (cyclists) are prefered.
// At the same time routing along big roads is still possible but if there's
// a pedestrian (bicycle) alternative it's prefered. To implement it a small speed
// is set in pedestrian_model (bicycle_model) for big roads. On the other hand
// the most likely a pedestrian (a cyclist) will go along big roads with average
// speed (graph.GetMaxSpeedKMPH()).
double const speedMPS = graph.GetMaxSpeedKMPH() * KMPH2MPS;
times.reserve(path.size());
double trackTimeSec = 0.0;
times.emplace_back(0, trackTimeSec);
m2::PointD prev = path[0].GetPoint();
for (size_t i = 1; i < path.size(); ++i)
{
m2::PointD const & curr = path[i].GetPoint();
double const lengthM = MercatorBounds::DistanceOnEarth(prev, curr);
trackTimeSec += lengthM / speedMPS;
times.emplace_back(i, trackTimeSec);
prev = curr;
}
}
bool IDirectionsEngine::ReconstructPath(IRoadGraph const & graph, vector<Junction> const & path,
vector<Edge> & routeEdges,
my::Cancellable const & cancellable) const
{
routeEdges.clear();
if (path.size() <= 1)
return false;
routeEdges.reserve(path.size() - 1);
Junction curr = path[0];
vector<Edge> currEdges;
for (size_t i = 1; i < path.size(); ++i)
{
if (cancellable.IsCancelled())
return false;
Junction const & next = path[i];
currEdges.clear();
graph.GetOutgoingEdges(curr, currEdges);
bool found = false;
for (auto const & e : currEdges)
{
if (e.GetEndJunction() == next)
{
routeEdges.emplace_back(e);
found = true;
break;
}
}
if (!found)
return false;
curr = next;
}
ASSERT_EQUAL(routeEdges.size() + 1, path.size(), ());
return true;
}
} // namespace routing

View file

@ -5,6 +5,8 @@
#include "base/cancellable.hpp"
#include "std/vector.hpp"
namespace routing
{
@ -14,9 +16,19 @@ public:
virtual ~IDirectionsEngine() = default;
virtual void Generate(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times,
Route::TTurns & turnsDir,
Route::TTimes & times, Route::TTurns & turns,
vector<m2::PointD> & routeGeometry,
my::Cancellable const & cancellable) = 0;
protected:
/// \brief constructs route based on |graph| and |path|. Fills |routeEdges| with the route.
/// \returns false in case of any errors while reconstruction, if reconstruction process
/// was cancelled and in case of extremely short paths of 0 or 1 point. Returns true otherwise.
bool ReconstructPath(IRoadGraph const & graph, vector<Junction> const & path,
vector<Edge> & routeEdges, my::Cancellable const & cancellable) const;
void CalculateTimes(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times) const;
};
} // namespace routing

View file

@ -28,8 +28,8 @@ struct LoadedPathSegment
vector<m2::PointD> m_path;
vector<turns::SingleLaneInfo> m_lanes;
string m_name;
TEdgeWeight m_weight;
TNodeId m_nodeId;
TEdgeWeight m_weight; /*!< Time in seconds to pass the segment. */
TNodeId m_nodeId; /*!< May be NodeId for OSRM router or FeatureId::index for graph router. */
ftypes::HighwayClass m_highwayClass;
bool m_onRoundabout;
bool m_isLink;

View file

@ -52,30 +52,21 @@ double constexpr kMwmLoadedProgress = 10.0f;
double constexpr kPointsFoundProgress = 15.0f;
double constexpr kCrossPathFoundProgress = 50.0f;
double constexpr kPathFoundProgress = 70.0f;
double PiMinusTwoVectorsAngle(m2::PointD const & p, m2::PointD const & p1, m2::PointD const & p2)
{
return math::pi - ang::TwoVectorsAngle(p, p1, p2);
}
} // namespace
using RawRouteData = InternalRouteResult;
class OSRMRoutingResultGraph : public turns::IRoutingResultGraph
class OSRMRoutingResult : public turns::IRoutingResult
{
public:
// turns::IRoutingResultGraph overrides:
virtual TUnpackedPathSegments const & GetSegments() const override
{
return m_loadedSegments;
}
virtual void GetPossibleTurns(TNodeId node, m2::PointD const & ingoingPoint,
m2::PointD const & junctionPoint,
size_t & ingoingCount,
turns::TTurnCandidates & outgoingTurns) const override
// turns::IRoutingResult overrides:
TUnpackedPathSegments const & GetSegments() const override { return m_loadedSegments; }
void GetPossibleTurns(TNodeId node, m2::PointD const & ingoingPoint,
m2::PointD const & junctionPoint, size_t & ingoingCount,
turns::TurnCandidates & outgoingTurns) const override
{
double const kReadCrossEpsilon = 1.0E-4;
double const kFeaturesNearTurnMeters = 3.0;
// Geting nodes by geometry.
vector<NodeID> geomNodes;
@ -143,31 +134,28 @@ public:
m2::PointD const outgoingPoint = ft.GetPoint(
seg.m_pointStart < seg.m_pointEnd ? seg.m_pointStart + 1 : seg.m_pointStart - 1);
ASSERT_LESS(MercatorBounds::DistanceOnEarth(junctionPoint, ft.GetPoint(seg.m_pointStart)),
kFeaturesNearTurnMeters, ());
turns::kFeaturesNearTurnMeters, ());
outgoingTurns.isCandidatesAngleValid = true;
double const a =
my::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint));
outgoingTurns.emplace_back(a, targetNode, ftypes::GetHighwayClass(ft));
my::RadToDeg(turns::PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint));
outgoingTurns.candidates.emplace_back(a, targetNode, ftypes::GetHighwayClass(ft));
}
sort(outgoingTurns.begin(), outgoingTurns.end(),
sort(outgoingTurns.candidates.begin(), outgoingTurns.candidates.end(),
[](turns::TurnCandidate const & t1, turns::TurnCandidate const & t2)
{
return t1.angle < t2.angle;
});
{
return t1.angle < t2.angle;
});
}
virtual double GetShortestPathLength() const override { return m_rawResult.shortestPathLength; }
virtual m2::PointD const & GetStartPoint() const override
{
return m_rawResult.sourceEdge.segmentPoint;
}
virtual m2::PointD const & GetEndPoint() const override
{
return m_rawResult.targetEdge.segmentPoint;
}
double GetPathLength() const override { return m_rawResult.shortestPathLength; }
OSRMRoutingResultGraph(Index const & index, RoutingMapping & mapping, RawRoutingResult & result)
m2::PointD const & GetStartPoint() const override { return m_rawResult.sourceEdge.segmentPoint; }
m2::PointD const & GetEndPoint() const override { return m_rawResult.targetEdge.segmentPoint; }
OSRMRoutingResult(Index const & index, RoutingMapping & mapping, RawRoutingResult & result)
: m_rawResult(result), m_index(index), m_routingMapping(mapping)
{
for (auto const & pathSegments : m_rawResult.unpackedPathSegments)
@ -193,7 +181,6 @@ public:
}
}
~OSRMRoutingResultGraph() {}
private:
TUnpackedPathSegments m_loadedSegments;
RawRoutingResult m_rawResult;
@ -336,7 +323,7 @@ OsrmRouter::ResultCode OsrmRouter::MakeRouteFromCrossesPath(TCheckedPath const &
Route::TTimes mwmTimes;
Route::TStreets mwmStreets;
vector<m2::PointD> mwmPoints;
OSRMRoutingResultGraph resultGraph(*m_pIndex, *mwmMapping, routingResult);
OSRMRoutingResult resultGraph(*m_pIndex, *mwmMapping, routingResult);
if (MakeTurnAnnotation(resultGraph, delegate, mwmPoints, mwmTurnsDir, mwmTimes, mwmStreets) != NoError)
{
LOG(LWARNING, ("Can't load road path data from disk for", mwmMapping->GetCountryName()));
@ -506,7 +493,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRoute(m2::PointD const & startPoint,
Route::TStreets streets;
vector<m2::PointD> points;
OSRMRoutingResultGraph resultGraph(*m_pIndex, *startMapping, routingResult);
OSRMRoutingResult resultGraph(*m_pIndex, *startMapping, routingResult);
if (MakeTurnAnnotation(resultGraph, delegate, points, turnsDir, times, streets) != NoError)
{
LOG(LWARNING, ("Can't load road path data from disk!"));

View file

@ -1,18 +1,15 @@
#include "routing/pedestrian_directions.hpp"
#include "routing/turns_generator.hpp"
#include "routing/road_graph.hpp"
#include "indexer/classificator.hpp"
#include "indexer/feature.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "indexer/index.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
namespace
{
double constexpr KMPH2MPS = 1000.0 / (60 * 60);
bool HasType(uint32_t type, feature::TypesHolder const & types)
{
for (uint32_t t : types)
@ -23,6 +20,14 @@ bool HasType(uint32_t type, feature::TypesHolder const & types)
}
return false;
}
void Convert(vector<routing::Junction> const & path, vector<m2::PointD> & geometry)
{
size_t const pathSize = path.size();
geometry.resize(pathSize);
for (size_t i = 0; i < pathSize; ++i)
geometry[i] = path[i].GetPoint();
}
} // namespace
namespace routing
@ -36,92 +41,35 @@ PedestrianDirectionsEngine::PedestrianDirectionsEngine()
}
void PedestrianDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times,
Route::TTurns & turnsDir,
Route::TTimes & times, Route::TTurns & turns,
vector<m2::PointD> & routeGeometry,
my::Cancellable const & cancellable)
{
CHECK_GREATER(path.size(), 1, ());
times.clear();
turns.clear();
routeGeometry.clear();
if (path.size() <= 1)
return;
CalculateTimes(graph, path, times);
vector<Edge> routeEdges;
if (!ReconstructPath(graph, path, routeEdges, cancellable))
{
LOG(LDEBUG, ("Couldn't reconstruct path"));
LOG(LDEBUG, ("Couldn't reconstruct path."));
// use only "arrival" direction
turnsDir.emplace_back(path.size() - 1, turns::PedestrianDirection::ReachedYourDestination);
turns.emplace_back(path.size() - 1, turns::PedestrianDirection::ReachedYourDestination);
return;
}
CalculateTurns(graph, routeEdges, turnsDir, cancellable);
CalculateTurns(graph, routeEdges, turns, cancellable);
Convert(path, routeGeometry);
}
bool PedestrianDirectionsEngine::ReconstructPath(IRoadGraph const & graph, vector<Junction> const & path,
vector<Edge> & routeEdges,
my::Cancellable const & cancellable) const
{
routeEdges.reserve(path.size() - 1);
Junction curr = path[0];
vector<Edge> currEdges;
for (size_t i = 1; i < path.size(); ++i)
{
if (cancellable.IsCancelled())
return false;
Junction const & next = path[i];
currEdges.clear();
graph.GetOutgoingEdges(curr, currEdges);
bool found = false;
for (Edge const & e : currEdges)
{
if (e.GetEndJunction() == next)
{
routeEdges.emplace_back(e);
found = true;
break;
}
}
if (!found)
return false;
curr = next;
}
ASSERT_EQUAL(routeEdges.size()+1, path.size(), ());
return true;
}
void PedestrianDirectionsEngine::CalculateTimes(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times) const
{
double const speedMPS = graph.GetMaxSpeedKMPH() * KMPH2MPS;
times.reserve(path.size());
double trackTimeSec = 0.0;
times.emplace_back(0, trackTimeSec);
m2::PointD prev = path[0].GetPoint();
for (size_t i = 1; i < path.size(); ++i)
{
m2::PointD const & curr = path[i].GetPoint();
double const lengthM = MercatorBounds::DistanceOnEarth(prev, curr);
trackTimeSec += lengthM / speedMPS;
times.emplace_back(i, trackTimeSec);
prev = curr;
}
}
void PedestrianDirectionsEngine::CalculateTurns(IRoadGraph const & graph, vector<Edge> const & routeEdges,
Route::TTurns & turnsDir,
void PedestrianDirectionsEngine::CalculateTurns(IRoadGraph const & graph,
vector<Edge> const & routeEdges,
Route::TTurns & turns,
my::Cancellable const & cancellable) const
{
for (size_t i = 0; i < routeEdges.size(); ++i)
@ -137,24 +85,23 @@ void PedestrianDirectionsEngine::CalculateTurns(IRoadGraph const & graph, vector
if (HasType(m_typeSteps, types))
{
if (edge.IsForward())
turnsDir.emplace_back(i, turns::PedestrianDirection::Upstairs);
turns.emplace_back(i, turns::PedestrianDirection::Upstairs);
else
turnsDir.emplace_back(i, turns::PedestrianDirection::Downstairs);
turns.emplace_back(i, turns::PedestrianDirection::Downstairs);
}
else
{
graph.GetJunctionTypes(edge.GetStartJunction(), types);
if (HasType(m_typeLiftGate, types))
turnsDir.emplace_back(i, turns::PedestrianDirection::LiftGate);
turns.emplace_back(i, turns::PedestrianDirection::LiftGate);
else if (HasType(m_typeGate, types))
turnsDir.emplace_back(i, turns::PedestrianDirection::Gate);
turns.emplace_back(i, turns::PedestrianDirection::Gate);
}
}
// direction "arrival"
// (index of last junction is the same as number of edges)
turnsDir.emplace_back(routeEdges.size(), turns::PedestrianDirection::ReachedYourDestination);
turns.emplace_back(routeEdges.size(), turns::PedestrianDirection::ReachedYourDestination);
}
} // namespace routing

View file

@ -11,19 +11,11 @@ public:
PedestrianDirectionsEngine();
// IDirectionsEngine override:
void Generate(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times,
Route::TTurns & turnsDir,
void Generate(IRoadGraph const & graph, vector<Junction> const & path, Route::TTimes & times,
Route::TTurns & turns, vector<m2::PointD> & routeGeometry,
my::Cancellable const & cancellable) override;
private:
bool ReconstructPath(IRoadGraph const & graph, vector<Junction> const & path,
vector<Edge> & routeEdges,
my::Cancellable const & cancellable) const;
void CalculateTimes(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times) const;
void CalculateTurns(IRoadGraph const & graph, vector<Edge> const & routeEdges,
Route::TTurns & turnsDir,
my::Cancellable const & cancellable) const;

View file

@ -1,3 +1,4 @@
#include "routing/bicycle_directions.hpp"
#include "routing/bicycle_model.hpp"
#include "routing/features_road_graph.hpp"
#include "routing/nearest_edge_finder.hpp"
@ -49,14 +50,6 @@ IRouter::ResultCode Convert(IRoutingAlgorithm::Result value)
return IRouter::ResultCode::RouteNotFound;
}
void Convert(vector<Junction> const & path, vector<m2::PointD> & geometry)
{
geometry.clear();
geometry.reserve(path.size());
for (auto const & pos : path)
geometry.emplace_back(pos.GetPoint());
}
// Check if the found edges lays on mwm with pedestrian routing support.
bool CheckMwmVersion(vector<pair<Edge, m2::PointD>> const & vicinities, vector<string> & mwmNames)
{
@ -172,7 +165,6 @@ IRouter::ResultCode RoadGraphRouter::CalculateRoute(m2::PointD const & startPoin
m2::PointD const & finalPoint,
RouterDelegate const & delegate, Route & route)
{
if (!CheckMapExistence(startPoint, route) || !CheckMapExistence(finalPoint, route))
return RouteFileNotExist;
@ -247,14 +239,13 @@ void RoadGraphRouter::ReconstructRoute(vector<Junction> && path, Route & route,
if (path.size() == 1)
path.emplace_back(path.back());
vector<m2::PointD> geometry;
Convert(path, geometry);
Route::TTimes times;
Route::TTurns turnsDir;
vector<m2::PointD> geometry;
// @TODO(bykoianko) streetNames is not filled in Generate(). It should be done.
Route::TStreets streetNames;
if (m_directionsEngine)
m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, cancellable);
m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, geometry, cancellable);
route.SetGeometry(geometry.begin(), geometry.end());
route.SetSectionTimes(times);
@ -267,7 +258,9 @@ unique_ptr<IRouter> CreatePedestrianAStarRouter(Index & index, TCountryFileFn co
unique_ptr<IVehicleModelFactory> vehicleModelFactory(new PedestrianModelFactory());
unique_ptr<IRoutingAlgorithm> algorithm(new AStarRoutingAlgorithm());
unique_ptr<IDirectionsEngine> directionsEngine(new PedestrianDirectionsEngine());
unique_ptr<IRouter> router(new RoadGraphRouter("astar-pedestrian", index, countryFileFn, move(vehicleModelFactory), move(algorithm), move(directionsEngine)));
unique_ptr<IRouter> router(new RoadGraphRouter("astar-pedestrian", index, countryFileFn,
move(vehicleModelFactory), move(algorithm),
move(directionsEngine)));
return router;
}
@ -276,7 +269,9 @@ unique_ptr<IRouter> CreatePedestrianAStarBidirectionalRouter(Index & index, TCou
unique_ptr<IVehicleModelFactory> vehicleModelFactory(new PedestrianModelFactory());
unique_ptr<IRoutingAlgorithm> algorithm(new AStarBidirectionalRoutingAlgorithm());
unique_ptr<IDirectionsEngine> directionsEngine(new PedestrianDirectionsEngine());
unique_ptr<IRouter> router(new RoadGraphRouter("astar-bidirectional-pedestrian", index, countryFileFn, move(vehicleModelFactory), move(algorithm), move(directionsEngine)));
unique_ptr<IRouter> router(new RoadGraphRouter("astar-bidirectional-pedestrian", index,
countryFileFn, move(vehicleModelFactory),
move(algorithm), move(directionsEngine)));
return router;
}
@ -284,10 +279,9 @@ unique_ptr<IRouter> CreateBicycleAStarBidirectionalRouter(Index & index, TCountr
{
unique_ptr<IVehicleModelFactory> vehicleModelFactory(new BicycleModelFactory());
unique_ptr<IRoutingAlgorithm> algorithm(new AStarBidirectionalRoutingAlgorithm());
unique_ptr<IDirectionsEngine> directionsEngine(new PedestrianDirectionsEngine());
unique_ptr<IDirectionsEngine> directionsEngine(new BicycleDirectionsEngine(index));
unique_ptr<IRouter> router(new RoadGraphRouter("astar-bidirectional-bicycle", index, countryFileFn, move(vehicleModelFactory),
move(algorithm), move(directionsEngine)));
return router;
}
} // namespace routing

View file

@ -50,18 +50,25 @@ void Route::AddAbsentCountry(string const & name)
double Route::GetTotalDistanceMeters() const
{
if (!m_poly.IsValid())
return 0.0;
return m_poly.GetTotalDistanceM();
}
double Route::GetCurrentDistanceFromBeginMeters() const
{
if (!m_poly.IsValid())
return 0.0;
return m_poly.GetDistanceFromBeginM();
}
void Route::GetTurnsDistances(vector<double> & distances) const
{
double mercatorDistance = 0;
distances.clear();
if (!m_poly.IsValid())
return;
double mercatorDistance = 0.0;
auto const & polyline = m_poly.GetPolyline();
for (auto currentTurn = m_turns.begin(); currentTurn != m_turns.end(); ++currentTurn)
{
@ -84,6 +91,8 @@ void Route::GetTurnsDistances(vector<double> & distances) const
double Route::GetCurrentDistanceToEndMeters() const
{
if (!m_poly.IsValid())
return 0.0;
return m_poly.GetDistanceToEndM();
}

View file

@ -45,7 +45,10 @@ public:
template <class TIter> void SetGeometry(TIter beg, TIter end)
{
FollowedPolyline(beg, end).Swap(m_poly);
if (beg == end)
FollowedPolyline().Swap(m_poly);
else
FollowedPolyline(beg, end).Swap(m_poly);
Update();
}

View file

@ -15,11 +15,13 @@ INCLUDEPATH += $$ROOT_DIR/3party/jansson/src \
SOURCES += \
async_router.cpp \
base/followed_polyline.cpp \
bicycle_directions.cpp \
bicycle_model.cpp \
car_model.cpp \
cross_mwm_road_graph.cpp \
cross_mwm_router.cpp \
cross_routing_context.cpp \
directions_engine.cpp \
features_road_graph.cpp \
nearest_edge_finder.cpp \
online_absent_fetcher.cpp \
@ -51,7 +53,8 @@ HEADERS += \
async_router.hpp \
base/astar_algorithm.hpp \
base/followed_polyline.hpp \
bicycle_model.cpp \
bicycle_directions.hpp \
bicycle_model.hpp \
car_model.hpp \
cross_mwm_road_graph.hpp \
cross_mwm_router.hpp \

View file

@ -0,0 +1,15 @@
#include "testing/testing.hpp"
#include "routing/routing_integration_tests/routing_test_tools.hpp"
#include "geometry/mercator.hpp"
using namespace routing;
using namespace routing::turns;
UNIT_TEST(RussiaMoscowSevTushinoParkPreferingBicycleWay)
{
integration::CalculateRouteAndTestRouteLength(
integration::GetBicycleComponents(), MercatorBounds::FromLatLon(55.87445, 37.43711), {0., 0.},
MercatorBounds::FromLatLon(55.87203, 37.44274), 460.);
}

View file

@ -0,0 +1,69 @@
#include "testing/testing.hpp"
#include "routing/routing_integration_tests/routing_test_tools.hpp"
#include "routing/route.hpp"
using namespace routing;
using namespace routing::turns;
UNIT_TEST(RussiaMoscowSevTushinoParkBicycleWayTurnTest)
{
TRouteResult const routeResult = integration::CalculateRoute(
integration::GetBicycleComponents(), MercatorBounds::FromLatLon(55.87467, 37.43658),
{0.0, 0.0}, MercatorBounds::FromLatLon(55.8719, 37.4464));
Route const & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
integration::TestTurnCount(route, 1);
integration::GetNthTurn(route, 0).TestValid().TestDirection(TurnDirection::TurnLeft);
integration::TestRouteLength(route, 752.);
}
UNIT_TEST(RussiaMoscowGerPanfilovtsev22BicycleWayTurnTest)
{
TRouteResult const routeResult = integration::CalculateRoute(
integration::GetBicycleComponents(), MercatorBounds::FromLatLon(55.85630, 37.41004),
{0.0, 0.0}, MercatorBounds::FromLatLon(55.85717, 37.41052));
Route const & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
integration::TestTurnCount(route, 2);
integration::GetNthTurn(route, 0).TestValid().TestDirection(TurnDirection::TurnLeft);
integration::GetNthTurn(route, 1).TestValid().TestDirection(TurnDirection::TurnLeft);
}
UNIT_TEST(RussiaMoscowSalameiNerisPossibleTurnCorrectionBicycleWayTurnTest)
{
TRouteResult const routeResult = integration::CalculateRoute(
integration::GetBicycleComponents(), MercatorBounds::FromLatLon(55.85159, 37.38903),
{0.0, 0.0}, MercatorBounds::FromLatLon(55.85157, 37.38813));
Route const & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
integration::TestTurnCount(route, 1);
integration::GetNthTurn(route, 0).TestValid().TestDirection(TurnDirection::TurnSlightRight);
}
UNIT_TEST(RussiaMoscowSevTushinoParkBicycleOnePointTurnTest)
{
m2::PointD const point = MercatorBounds::FromLatLon(55.8719, 37.4464);
TRouteResult const routeResult =
integration::CalculateRoute(integration::GetBicycleComponents(), point, {0.0, 0.0}, point);
Route const & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
TEST_EQUAL(route.GetTurns().size(), 0, ());
integration::TestRouteLength(route, 0.0);
}

View file

@ -515,3 +515,16 @@ UNIT_TEST(MoscowChistiePrudiSelectPointsInConnectedGraph)
MercatorBounds::FromLatLon(55.76613, 37.63769), {0., 0.},
MercatorBounds::FromLatLon(55.76593, 37.63893), 134.02);
}
UNIT_TEST(RussiaMoscowSevTushinoParkPedestrianOnePointTurnTest)
{
m2::PointD const point = MercatorBounds::FromLatLon(55.8719, 37.4464);
TRouteResult const routeResult =
integration::CalculateRoute(integration::GetPedestrianComponents(), point, {0.0, 0.0}, point);
Route const & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
integration::TestTurnCount(route, 0);
integration::TestRouteLength(route, 0.0);
}

View file

@ -24,6 +24,8 @@ QT *= core
SOURCES += \
../../testing/testingmain.cpp \
bicycle_route_test.cpp \
bicycle_turn_test.cpp \
cross_section_tests.cpp \
online_cross_tests.cpp \
osrm_route_test.cpp \

View file

@ -31,16 +31,21 @@
using namespace routing;
using TRouterFactory =
function<unique_ptr<IRouter>(Index & index, TCountryFileFn const & countryFileFn)>;
namespace
{
void ChangeMaxNumberOfOpenFiles(size_t n)
{
struct rlimit rlp;
getrlimit(RLIMIT_NOFILE, &rlp);
rlp.rlim_cur = n;
setrlimit(RLIMIT_NOFILE, &rlp);
}
double constexpr kErrorMeters = 1.0;
double constexpr kErrorSeconds = 1.0;
void ChangeMaxNumberOfOpenFiles(size_t n)
{
struct rlimit rlp;
getrlimit(RLIMIT_NOFILE, &rlp);
rlp.rlim_cur = n;
setrlimit(RLIMIT_NOFILE, &rlp);
}
} // namespace
namespace integration
{
@ -69,27 +74,28 @@ namespace integration
return storage::CountryInfoReader::CreateCountryInfoReader(platform);
}
shared_ptr<OsrmRouter> CreateOsrmRouter(Index & index,
unique_ptr<OsrmRouter> CreateOsrmRouter(Index & index,
storage::CountryInfoGetter const & infoGetter)
{
shared_ptr<OsrmRouter> osrmRouter(new OsrmRouter(
&index, [&infoGetter](m2::PointD const & pt)
{
return infoGetter.GetRegionCountryId(pt);
}
));
unique_ptr<OsrmRouter> osrmRouter(new OsrmRouter(&index, [&infoGetter](m2::PointD const & pt)
{
return infoGetter.GetRegionCountryId(pt);
}));
return osrmRouter;
}
shared_ptr<IRouter> CreatePedestrianRouter(Index & index,
storage::CountryInfoGetter const & infoGetter)
unique_ptr<IRouter> CreateAStarRouter(Index & index,
storage::CountryInfoGetter const & infoGetter,
TRouterFactory const & routerFactory)
{
// |infoGetter| should be a reference to an object which exists while the
// result of the function is used.
auto countryFileGetter = [&infoGetter](m2::PointD const & pt)
{
return infoGetter.GetRegionCountryId(pt);
};
unique_ptr<IRouter> router = CreatePedestrianAStarBidirectionalRouter(index, countryFileGetter);
return shared_ptr<IRouter>(move(router));
unique_ptr<IRouter> router = routerFactory(index, countryFileGetter);
return unique_ptr<IRouter>(move(router));
}
class OsrmRouterComponents : public IRouterComponents
@ -104,7 +110,7 @@ namespace integration
IRouter * GetRouter() const override { return m_osrmRouter.get(); }
private:
shared_ptr<OsrmRouter> m_osrmRouter;
unique_ptr<OsrmRouter> m_osrmRouter;
};
class PedestrianRouterComponents : public IRouterComponents
@ -112,14 +118,31 @@ namespace integration
public:
PedestrianRouterComponents(vector<LocalCountryFile> const & localFiles)
: IRouterComponents(localFiles)
, m_router(CreatePedestrianRouter(m_featuresFetcher->GetIndex(), *m_infoGetter))
, m_router(CreateAStarRouter(m_featuresFetcher->GetIndex(), *m_infoGetter,
CreatePedestrianAStarBidirectionalRouter))
{
}
IRouter * GetRouter() const override { return m_router.get(); }
private:
shared_ptr<IRouter> m_router;
unique_ptr<IRouter> m_router;
};
class BicycleRouterComponents : public IRouterComponents
{
public:
BicycleRouterComponents(vector<LocalCountryFile> const & localFiles)
: IRouterComponents(localFiles)
, m_router(CreateAStarRouter(m_featuresFetcher->GetIndex(), *m_infoGetter,
CreateBicycleAStarBidirectionalRouter))
{
}
IRouter * GetRouter() const override { return m_router.get(); }
private:
unique_ptr<IRouter> m_router;
};
template <typename TRouterComponents>
@ -151,21 +174,34 @@ namespace integration
IRouterComponents & GetOsrmComponents()
{
static shared_ptr<IRouterComponents> const inst = CreateAllMapsComponents<OsrmRouterComponents>();
ASSERT(inst, ());
return *inst;
static auto const instance = CreateAllMapsComponents<OsrmRouterComponents>();
ASSERT(instance, ());
return *instance;
}
shared_ptr<IRouterComponents> GetPedestrianComponents(vector<platform::LocalCountryFile> const & localFiles)
{
return shared_ptr<IRouterComponents>(new PedestrianRouterComponents(localFiles));
return make_shared<PedestrianRouterComponents>(localFiles);
}
IRouterComponents & GetPedestrianComponents()
{
static shared_ptr<IRouterComponents> const inst = CreateAllMapsComponents<PedestrianRouterComponents>();
ASSERT(inst, ());
return *inst;
static auto const instance = CreateAllMapsComponents<PedestrianRouterComponents>();
ASSERT(instance, ());
return *instance;
}
shared_ptr<IRouterComponents> GetBicycleComponents(
vector<platform::LocalCountryFile> const & localFiles)
{
return make_shared<BicycleRouterComponents>(localFiles);
}
IRouterComponents & GetBicycleComponents()
{
static auto const instance = CreateAllMapsComponents<BicycleRouterComponents>();
ASSERT(instance, ());
return *instance;
}
TRouteResult CalculateRoute(IRouterComponents const & routerComponents,
@ -208,7 +244,7 @@ namespace integration
void TestRouteLength(Route const & route, double expectedRouteMeters,
double relativeError)
{
double const delta = expectedRouteMeters * relativeError;
double const delta = max(expectedRouteMeters * relativeError, kErrorMeters);
double const routeMeters = route.GetTotalDistanceMeters();
TEST(my::AlmostEqualAbs(routeMeters, expectedRouteMeters, delta),
("Route length test failed. Expected:", expectedRouteMeters, "have:", routeMeters, "delta:", delta));
@ -216,7 +252,7 @@ namespace integration
void TestRouteTime(Route const & route, double expectedRouteSeconds, double relativeError)
{
double const delta = expectedRouteSeconds * relativeError;
double const delta = max(expectedRouteSeconds * relativeError, kErrorSeconds);
double const routeSeconds = route.GetTotalTimeSec();
TEST(my::AlmostEqualAbs(routeSeconds, expectedRouteSeconds, delta),
("Route time test failed. Expected:", expectedRouteSeconds, "have:", routeSeconds, "delta:", delta));

View file

@ -78,14 +78,19 @@ unique_ptr<storage::CountryInfoGetter> CreateCountryInfoGetter();
void TestOnlineFetcher(ms::LatLon const & startPoint, ms::LatLon const & finalPoint,
vector<string> const & expected, IRouterComponents & routerComponents);
/// Get OSRM router components
/// Gets OSRM router components
IRouterComponents & GetOsrmComponents();
shared_ptr<IRouterComponents> GetOsrmComponents(vector<platform::LocalCountryFile> const & localFiles);
/// Get pedestrian router components
/// Gets pedestrian router components
IRouterComponents & GetPedestrianComponents();
shared_ptr<IRouterComponents> GetPedestrianComponents(vector<platform::LocalCountryFile> const & localFiles);
/// Gets bicycle router components.
IRouterComponents & GetBicycleComponents();
shared_ptr<IRouterComponents> GetBicycleComponents(
vector<platform::LocalCountryFile> const & localFiles);
TRouteResult CalculateRoute(IRouterComponents const & routerComponents,
m2::PointD const & startPoint, m2::PointD const & startDirection,
m2::PointD const & finalPoint);

View file

@ -9,24 +9,25 @@ namespace routing
{
namespace turns
{
/*!
* \brief The IRoutingResultGraph interface for the routing result. Uncouple router from the
* \brief The IRoutingResult interface for the routing result. Uncouple router from the
* annotation code that describes turns. See routers for detail implementations.
*/
class IRoutingResultGraph
class IRoutingResult
{
public:
/// \returns information about all route segments.
virtual TUnpackedPathSegments const & GetSegments() const = 0;
/// \brief For a |node|, |junctionPoint| and |ingoingPoint| (point before the |node|)
/// this method computes number of ingoing ways to |junctionPoint| and fills |outgoingTurns|.
virtual void GetPossibleTurns(TNodeId node, m2::PointD const & ingoingPoint,
m2::PointD const & junctionPoint,
size_t & ingoingCount,
TTurnCandidates & outgoingTurns) const = 0;
virtual double GetShortestPathLength() const = 0;
m2::PointD const & junctionPoint, size_t & ingoingCount,
TurnCandidates & outgoingTurns) const = 0;
virtual double GetPathLength() const = 0;
virtual m2::PointD const & GetStartPoint() const = 0;
virtual m2::PointD const & GetEndPoint() const = 0;
virtual ~IRoutingResultGraph() = default;
virtual ~IRoutingResult() = default;
};
} // namespace routing
} // namespace turns

View file

@ -41,6 +41,13 @@ struct TurnCandidate
}
};
using TTurnCandidates = vector<TurnCandidate>;
struct TurnCandidates
{
vector<TurnCandidate> candidates;
bool isCandidatesAngleValid;
explicit TurnCandidates(bool angleValid = true) : isCandidatesAngleValid(angleValid) {}
};
} // namespace routing
} // namespace turns

View file

@ -1,5 +1,7 @@
#include "routing/turns.hpp"
#include "geometry/angles.hpp"
#include "base/internal/message.hpp"
#include "std/array.hpp"
@ -277,5 +279,11 @@ string DebugPrint(SingleLaneInfo const & singleLaneInfo)
<< ", m_lane == " << ::DebugPrint(singleLaneInfo.m_lane) << " ]" << endl;
return out.str();
}
double PiMinusTwoVectorsAngle(m2::PointD const & junctionPoint, m2::PointD const & ingoingPoint,
m2::PointD const & outgoingPoint)
{
return math::pi - ang::TwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint);
}
} // namespace turns
} // namespace routing

View file

@ -19,6 +19,8 @@ namespace turns
/// @todo(vbykoianko) It's a good idea to gather all the turns information into one entity.
/// For the time being several separate entities reflect the turn information. Like Route::TTurns
double constexpr kFeaturesNearTurnMeters = 3.0;
/*!
* \warning The order of values below shall not be changed.
* TurnRight(TurnLeft) must have a minimal value and
@ -202,5 +204,13 @@ bool IsLaneWayConformedTurnDirectionApproximately(LaneWay l, TurnDirection t);
bool ParseLanes(string lanesString, vector<SingleLaneInfo> & lanes);
void SplitLanes(string const & lanesString, char delimiter, vector<string> & lanes);
bool ParseSingleLane(string const & laneString, char delimiter, TSingleLane & lane);
/*!
* \returns pi minus angle from vector [junctionPoint, ingoingPoint]
* to vector [junctionPoint, outgoingPoint]. A counterclockwise rotation.
* Angle is in range [-pi, pi].
*/
double PiMinusTwoVectorsAngle(m2::PointD const & junctionPoint, m2::PointD const & ingoingPoint,
m2::PointD const & outgoingPoint);
} // namespace turns
} // namespace routing

View file

@ -21,37 +21,29 @@ using namespace routing::turns;
namespace
{
double const kFeaturesNearTurnMeters = 3.0;
size_t constexpr kMaxPointsCount = 5;
double constexpr kMinDistMeters = 200.;
size_t constexpr kNotSoCloseMaxPointsCount = 3;
double constexpr kNotSoCloseMinDistMeters = 30.;
using TGeomTurnCandidate = vector<double>;
double PiMinusTwoVectorsAngle(m2::PointD const & p, m2::PointD const & p1, m2::PointD const & p2)
{
return math::pi - ang::TwoVectorsAngle(p, p1, p2);
}
/*!
* \brief Returns false when
* - the route leads from one big road to another one;
* - and the other possible turns lead to small roads;
* - and the turn is GoStraight or TurnSlight*.
*/
bool KeepTurnByHighwayClass(TurnDirection turn, TTurnCandidates const & possibleTurns,
bool KeepTurnByHighwayClass(TurnDirection turn, TurnCandidates const & possibleTurns,
TurnInfo const & turnInfo)
{
if (!IsGoStraightOrSlightTurn(turn))
return true; // The road significantly changes its direction here. So this turn shall be kept.
// There's only one exit from this junction. NodeID of the exit is outgoingNode.
if (possibleTurns.size() == 1)
if (possibleTurns.candidates.size() == 1)
return true;
ftypes::HighwayClass maxClassForPossibleTurns = ftypes::HighwayClass::Error;
for (auto const & t : possibleTurns)
for (auto const & t : possibleTurns.candidates)
{
if (t.node == turnInfo.m_outgoing.m_nodeId)
continue;
@ -87,10 +79,10 @@ bool KeepTurnByHighwayClass(TurnDirection turn, TTurnCandidates const & possible
/*!
* \brief Returns false when other possible turns leads to service roads;
*/
bool KeepRoundaboutTurnByHighwayClass(TurnDirection turn, TTurnCandidates const & possibleTurns,
bool KeepRoundaboutTurnByHighwayClass(TurnDirection turn, TurnCandidates const & possibleTurns,
TurnInfo const & turnInfo)
{
for (auto const & t : possibleTurns)
for (auto const & t : possibleTurns.candidates)
{
if (t.node == turnInfo.m_outgoing.m_nodeId)
continue;
@ -261,14 +253,14 @@ bool TurnInfo::IsSegmentsValid() const
return true;
}
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResultGraph const & result,
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
RouterDelegate const & delegate, vector<m2::PointD> & points,
Route::TTurns & turnsDir, Route::TTimes & times,
Route::TStreets & streets)
{
double estimatedTime = 0;
LOG(LDEBUG, ("Shortest th length:", result.GetShortestPathLength()));
LOG(LDEBUG, ("Shortest th length:", result.GetPathLength()));
#ifdef DEBUG
size_t lastIdx = 0;
@ -569,7 +561,7 @@ TurnDirection IntermediateDirection(const double angle)
return FindDirectionByAngle(kLowerBounds, angle);
}
void GetTurnDirection(IRoutingResultGraph const & result, TurnInfo & turnInfo, TurnItem & turn)
void GetTurnDirection(IRoutingResult const & result, TurnInfo & turnInfo, TurnItem & turn)
{
if (!turnInfo.IsSegmentsValid())
return;
@ -601,26 +593,26 @@ void GetTurnDirection(IRoutingResultGraph const & result, TurnInfo & turnInfo, T
ASSERT_GREATER(turnInfo.m_ingoing.m_path.size(), 1, ());
m2::PointD const ingoingPointOneSegment = turnInfo.m_ingoing.m_path[turnInfo.m_ingoing.m_path.size() - 2];
TTurnCandidates nodes;
TurnCandidates nodes;
size_t ingoingCount;
result.GetPossibleTurns(turnInfo.m_ingoing.m_nodeId, ingoingPointOneSegment, junctionPoint,
ingoingCount, nodes);
size_t const numNodes = nodes.size();
size_t const numNodes = nodes.candidates.size();
bool const hasMultiTurns = numNodes > 1;
if (numNodes == 0)
return;
if (!hasMultiTurns)
if (!hasMultiTurns || !nodes.isCandidatesAngleValid)
{
turn.m_turn = intermediateDirection;
}
else
{
if (nodes.front().node == turnInfo.m_outgoing.m_nodeId)
if (nodes.candidates.front().node == turnInfo.m_outgoing.m_nodeId)
turn.m_turn = LeftmostDirection(turnAngle);
else if (nodes.back().node == turnInfo.m_outgoing.m_nodeId)
else if (nodes.candidates.back().node == turnInfo.m_outgoing.m_nodeId)
turn.m_turn = RightmostDirection(turnAngle);
else
turn.m_turn = intermediateDirection;
@ -648,7 +640,7 @@ void GetTurnDirection(IRoutingResultGraph const & result, TurnInfo & turnInfo, T
kNotSoCloseMinDistMeters, GetIngoingPointIndex);
if (!KeepTurnByIngoingEdges(junctionPoint, notSoCloseToTheTurnPoint, outgoingPoint, hasMultiTurns,
nodes.size() + ingoingCount))
nodes.candidates.size() + ingoingCount))
{
turn.m_turn = TurnDirection::NoTurn;
return;

View file

@ -42,7 +42,7 @@ using TGetIndexFunction = function<size_t(pair<size_t, size_t>)>;
* \param streets output street names along the path.
* \return routing operation result code.
*/
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResultGraph const & result,
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
RouterDelegate const & delegate, vector<m2::PointD> & points,
Route::TTurns & turnsDir, Route::TTimes & times,
Route::TStreets & streets);
@ -127,8 +127,7 @@ TurnDirection GetRoundaboutDirection(bool isIngoingEdgeRoundabout, bool isOutgoi
* \param turnInfo is used for cashing some information while turn calculation.
* \param turn is used for keeping the result of turn calculation.
*/
void GetTurnDirection(IRoutingResultGraph const & result, turns::TurnInfo & turnInfo,
TurnItem & turn);
void GetTurnDirection(IRoutingResult const & result, turns::TurnInfo & turnInfo, TurnItem & turn);
/*!
* \brief Finds an UTurn that starts from current segment and returns how many segments it lasts.

View file

@ -12,6 +12,9 @@
56099E2B1CC7C97D00A7772A /* turn_candidate.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56099E271CC7C97D00A7772A /* turn_candidate.hpp */; };
56099E2E1CC8FBDA00A7772A /* osrm_path_segment_factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56099E2C1CC8FBDA00A7772A /* osrm_path_segment_factory.cpp */; };
56099E2F1CC8FBDA00A7772A /* osrm_path_segment_factory.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56099E2D1CC8FBDA00A7772A /* osrm_path_segment_factory.hpp */; };
56099E331CC9247E00A7772A /* bicycle_directions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56099E301CC9247E00A7772A /* bicycle_directions.cpp */; };
56099E341CC9247E00A7772A /* bicycle_directions.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56099E311CC9247E00A7772A /* bicycle_directions.hpp */; };
56099E351CC9247E00A7772A /* directions_engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56099E321CC9247E00A7772A /* directions_engine.cpp */; };
563B91C51CC4F1DC00222BC1 /* bicycle_model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 563B91C31CC4F1DC00222BC1 /* bicycle_model.cpp */; };
563B91C61CC4F1DC00222BC1 /* bicycle_model.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 563B91C41CC4F1DC00222BC1 /* bicycle_model.hpp */; };
670B84C01A9381D900CE4492 /* cross_routing_context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670B84BE1A9381D900CE4492 /* cross_routing_context.cpp */; };
@ -188,6 +191,9 @@
56099E271CC7C97D00A7772A /* turn_candidate.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = turn_candidate.hpp; sourceTree = "<group>"; };
56099E2C1CC8FBDA00A7772A /* osrm_path_segment_factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = osrm_path_segment_factory.cpp; path = ../../routing/osrm_path_segment_factory.cpp; sourceTree = "<group>"; };
56099E2D1CC8FBDA00A7772A /* osrm_path_segment_factory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = osrm_path_segment_factory.hpp; path = ../../routing/osrm_path_segment_factory.hpp; sourceTree = "<group>"; };
56099E301CC9247E00A7772A /* bicycle_directions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bicycle_directions.cpp; sourceTree = "<group>"; };
56099E311CC9247E00A7772A /* bicycle_directions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bicycle_directions.hpp; sourceTree = "<group>"; };
56099E321CC9247E00A7772A /* directions_engine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = directions_engine.cpp; sourceTree = "<group>"; };
563B91C31CC4F1DC00222BC1 /* bicycle_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bicycle_model.cpp; sourceTree = "<group>"; };
563B91C41CC4F1DC00222BC1 /* bicycle_model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bicycle_model.hpp; sourceTree = "<group>"; };
670B84BE1A9381D900CE4492 /* cross_routing_context.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cross_routing_context.cpp; sourceTree = "<group>"; };
@ -549,6 +555,9 @@
675343FA1A3F640D00A0A8C3 /* routing */ = {
isa = PBXGroup;
children = (
56099E301CC9247E00A7772A /* bicycle_directions.cpp */,
56099E311CC9247E00A7772A /* bicycle_directions.hpp */,
56099E321CC9247E00A7772A /* directions_engine.cpp */,
56099E251CC7C97D00A7772A /* loaded_path_segment.hpp */,
56099E261CC7C97D00A7772A /* routing_result_graph.hpp */,
56099E271CC7C97D00A7772A /* turn_candidate.hpp */,
@ -665,6 +674,7 @@
A1616E2C1B6B60AB003F078E /* router_delegate.hpp in Headers */,
A17B42991BCFBD0E00A1EAE4 /* osrm_helpers.hpp in Headers */,
67C7D42E1B4EB48F00FE41AA /* turns_sound_settings.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 */,
@ -856,6 +866,7 @@
files = (
56099E2E1CC8FBDA00A7772A /* osrm_path_segment_factory.cpp in Sources */,
675344201A3F644F00A0A8C3 /* vehicle_model.cpp in Sources */,
56099E351CC9247E00A7772A /* directions_engine.cpp in Sources */,
A1616E2B1B6B60AB003F078E /* router_delegate.cpp in Sources */,
6741AA9C1BF35331002C974C /* turns_notification_manager.cpp in Sources */,
67C7D42B1B4EB48F00FE41AA /* pedestrian_model.cpp in Sources */,
@ -875,6 +886,7 @@
670EE55D1B6001E7001E8064 /* routing_session.cpp in Sources */,
A120B3451B4A7BE5002F3808 /* cross_mwm_road_graph.cpp in Sources */,
67C7D4291B4EB48F00FE41AA /* car_model.cpp in Sources */,
56099E331CC9247E00A7772A /* bicycle_directions.cpp in Sources */,
674A28B11B1605D2001A525C /* osrm_engine.cpp in Sources */,
674F9BD41B0A580E00704FFA /* road_graph.cpp in Sources */,
67AB92E61B7B3E6E00AB5194 /* turns_tts_text.cpp in Sources */,