Merge pull request #10676 from bykoianko/master-openlr-algo3-score

Openlr matching. Algorithm version 3.
This commit is contained in:
gmoryes 2019-04-29 15:19:40 +03:00 committed by GitHub
commit 83797b2070
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1543 additions and 229 deletions

View file

@ -29,6 +29,13 @@ set(
road_info_getter.hpp
router.cpp
router.hpp
score_candidate_paths_getter.cpp
score_candidate_paths_getter.hpp
score_candidate_points_getter.cpp
score_candidate_points_getter.hpp
score_paths_connector.cpp
score_paths_connector.hpp
score_types.hpp
stats.hpp
way_point.hpp
)

View file

@ -27,15 +27,6 @@ namespace
int const kNumBuckets = 256;
double const kAnglesInBucket = 360.0 / kNumBuckets;
m2::PointD PointAtSegmentM(m2::PointD const & p1, m2::PointD const & p2, double const distanceM)
{
auto const v = p2 - p1;
auto const l = v.Length();
auto const L = MercatorBounds::DistanceOnEarth(p1, p2);
auto const delta = distanceM * l / L;
return PointAtSegment(p1, p2, delta);
}
uint32_t Bearing(m2::PointD const & a, m2::PointD const & b)
{
auto const angle = location::AngleToBearing(base::RadToDeg(ang::AngleTo(a, b)));
@ -43,65 +34,9 @@ uint32_t Bearing(m2::PointD const & a, m2::PointD const & b)
CHECK_GREATER_OR_EQUAL(angle, 0, ("Angle should be greater than or equal to 0"));
return base::clamp(angle / kAnglesInBucket, 0.0, 255.0);
}
// This class is used to get correct points for further bearing calculations.
// Depending on |isLastPoint| it either calculates those points straightforwardly
// or reverses directions and then calculates.
class BearingPointsSelector
{
public:
BearingPointsSelector(uint32_t const bearDistM, bool const isLastPoint)
: m_bearDistM(bearDistM), m_isLastPoint(isLastPoint)
{
}
m2::PointD GetBearingStartPoint(Graph::Edge const & e) const
{
return m_isLastPoint ? e.GetEndPoint() : e.GetStartPoint();
}
m2::PointD GetBearingEndPoint(Graph::Edge const & e, double const distanceM)
{
if (distanceM < m_bearDistM && m_bearDistM <= distanceM + EdgeLength(e))
{
auto const edgeLen = EdgeLength(e);
auto const edgeBearDist = min(m_bearDistM - distanceM, edgeLen);
ASSERT_LESS_OR_EQUAL(edgeBearDist, edgeLen, ());
return m_isLastPoint ? PointAtSegmentM(e.GetEndPoint(), e.GetStartPoint(),
static_cast<double>(edgeBearDist))
: PointAtSegmentM(e.GetStartPoint(), e.GetEndPoint(),
static_cast<double>(edgeBearDist));
}
return m_isLastPoint ? e.GetStartPoint() : e.GetEndPoint();
}
private:
double m_bearDistM;
bool m_isLastPoint;
};
} // namespace
// CandidatePathsGetter::Link ----------------------------------------------------------------------
bool CandidatePathsGetter::Link::operator<(Link const & o) const
{
if (m_distanceM != o.m_distanceM)
return m_distanceM < o.m_distanceM;
if (m_edge != o.m_edge)
return m_edge < o.m_edge;
if (m_parent == o.m_parent)
return false;
if (m_parent && o.m_parent)
return *m_parent < *o.m_parent;
if (!m_parent)
return true;
return false;
}
Graph::Edge CandidatePathsGetter::Link::GetStartEdge() const
{
auto * start = this;
@ -135,7 +70,7 @@ bool CandidatePathsGetter::GetLineCandidatesForPoints(
if (i != points.size() - 1 && points[i].m_distanceToNextPoint == 0)
{
LOG(LINFO, ("Distance to next point is zero. Skipping the whole segment"));
++m_stats.m_dnpIsZero;
++m_stats.m_zeroDistToNextPointCount;
return false;
}
@ -249,7 +184,7 @@ void CandidatePathsGetter::GetBestCandidatePaths(
BearingPointsSelector pointsSelector(bearDistM, isLastPoint);
for (auto const & l : allPaths)
{
auto const bearStartPoint = pointsSelector.GetBearingStartPoint(l->GetStartEdge());
auto const bearStartPoint = pointsSelector.GetStartPoint(l->GetStartEdge());
auto const startPointsDistance = MercatorBounds::DistanceOnEarth(bearStartPoint, startPoint);
// Number of edges counting from the last one to check bearing on. Accorfing to OpenLR spec
@ -273,7 +208,7 @@ void CandidatePathsGetter::GetBestCandidatePaths(
--traceBackIterationsLeft;
auto const bearEndPoint =
pointsSelector.GetBearingEndPoint(part->m_edge, part->m_distanceM);
pointsSelector.GetEndPoint(part->m_edge, part->m_distanceM);
auto const bearing = Bearing(bearStartPoint, bearEndPoint);
auto const bearingDiff = AbsDifference(bearing, requiredBearing);

View file

@ -47,7 +47,6 @@ private:
{
}
bool operator<(Link const & o) const;
bool IsJunctionInPath(routing::Junction const & j) const;
Graph::Edge GetStartEdge() const;

View file

@ -12,8 +12,8 @@ using namespace routing;
namespace openlr
{
void CandidatePointsGetter::GetJunctionPointCandidates(m2::PointD const & p,
vector<m2::PointD> & candidates)
void CandidatePointsGetter::FillJunctionPointCandidates(m2::PointD const & p,
vector<m2::PointD> & candidates)
{
// TODO(mgsergio): Get optimal value using experiments on a sample.
// Or start with small radius and scale it up when there are too few points.

View file

@ -27,12 +27,12 @@ public:
void GetCandidatePoints(m2::PointD const & p, std::vector<m2::PointD> & candidates)
{
GetJunctionPointCandidates(p, candidates);
FillJunctionPointCandidates(p, candidates);
EnrichWithProjectionPoints(p, candidates);
}
private:
void GetJunctionPointCandidates(m2::PointD const & p, std::vector<m2::PointD> & candidates);
void FillJunctionPointCandidates(m2::PointD const & p, std::vector<m2::PointD> & candidates);
void EnrichWithProjectionPoints(m2::PointD const & p, std::vector<m2::PointD> & candidates);
size_t const m_maxJunctionCandidates;

View file

@ -67,11 +67,6 @@ void Graph::FindClosestEdges(m2::PointD const & point, uint32_t const count,
m_graph.FindClosestEdges(point, count, vicinities);
}
void Graph::AddFakeEdges(Junction const & junction, vector<pair<Edge, Junction>> const & vicinities)
{
m_graph.AddFakeEdges(junction, vicinities);
}
void Graph::AddIngoingFakeEdge(Edge const & e)
{
m_graph.AddIngoingFakeEdge(e);
@ -81,4 +76,9 @@ void Graph::AddOutgoingFakeEdge(Edge const & e)
{
m_graph.AddOutgoingFakeEdge(e);
}
void Graph::GetFeatureTypes(FeatureID const & featureId, feature::TypesHolder & types) const
{
m_graph.GetFeatureTypes(featureId, types);
}
} // namespace openlr

View file

@ -5,11 +5,14 @@
#include "routing_common/car_model.hpp"
#include "indexer/feature_data.hpp"
#include "geometry/point2d.hpp"
#include <cstddef>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
@ -42,14 +45,13 @@ public:
void FindClosestEdges(m2::PointD const & point, uint32_t const count,
std::vector<std::pair<Edge, Junction>> & vicinities) const;
void AddFakeEdges(Junction const & junction,
std::vector<std::pair<Edge, Junction>> const & vicinities);
void AddIngoingFakeEdge(Edge const & e);
void AddOutgoingFakeEdge(Edge const & e);
void ResetFakes() { m_graph.ResetFakes(); }
void GetFeatureTypes(FeatureID const & featureId, feature::TypesHolder & types) const;
private:
routing::FeaturesRoadGraph m_graph;
std::map<Junction, EdgeVector> m_outgoingCache;

View file

@ -6,12 +6,18 @@
#include "geometry/mercator.hpp"
#include <algorithm>
#include <sstream>
#include <string>
#include <type_traits>
#include "boost/optional.hpp"
namespace
{
using namespace openlr;
using namespace std;
openlr::FunctionalRoadClass HighwayClassToFunctionalRoadClass(ftypes::HighwayClass const & hwClass)
{
switch (hwClass)
@ -25,10 +31,86 @@ openlr::FunctionalRoadClass HighwayClassToFunctionalRoadClass(ftypes::HighwayCla
default: return openlr::FunctionalRoadClass::FRC7;
}
}
/// \returns boost::none if |e| doesn't conform to |functionalRoadClass| and score otherwise.
boost::optional<Score> GetFrcScore(Graph::Edge const & e, FunctionalRoadClass functionalRoadClass,
RoadInfoGetter & infoGetter)
{
CHECK(!e.IsFake(), ());
Score constexpr kMaxScoreForFrc = 25;
if (functionalRoadClass == FunctionalRoadClass::NotAValue)
return boost::none;
auto const hwClass = infoGetter.Get(e.GetFeatureId()).m_hwClass;
switch (functionalRoadClass)
{
case FunctionalRoadClass::FRC0:
// Note. HighwayClass::Trunk means motorway, motorway_link, trunk or trunk_link.
return hwClass == ftypes::HighwayClass::Trunk ? boost::optional<Score>(kMaxScoreForFrc)
: boost::none;
case FunctionalRoadClass::FRC1:
return (hwClass == ftypes::HighwayClass::Trunk || hwClass == ftypes::HighwayClass::Primary)
? boost::optional<Score>(kMaxScoreForFrc)
: boost::none;
case FunctionalRoadClass::FRC2:
case FunctionalRoadClass::FRC3:
if (hwClass == ftypes::HighwayClass::Secondary || hwClass == ftypes::HighwayClass::Tertiary)
return boost::optional<Score>(kMaxScoreForFrc);
return hwClass == ftypes::HighwayClass::Primary || hwClass == ftypes::HighwayClass::LivingStreet
? boost::optional<Score>(0)
: boost::none;
case FunctionalRoadClass::FRC4:
if (hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service)
return boost::optional<Score>(kMaxScoreForFrc);
return hwClass == ftypes::HighwayClass::Tertiary ? boost::optional<Score>(0) : boost::none;
case FunctionalRoadClass::FRC5:
case FunctionalRoadClass::FRC6:
case FunctionalRoadClass::FRC7:
return hwClass == ftypes::HighwayClass::LivingStreet || hwClass == ftypes::HighwayClass::Service
? boost::optional<Score>(kMaxScoreForFrc)
: boost::none;
case FunctionalRoadClass::NotAValue:
UNREACHABLE();
}
UNREACHABLE();
}
} // namespace
namespace openlr
{
// BearingPointsSelector ---------------------------------------------------------------------------
BearingPointsSelector::BearingPointsSelector(uint32_t bearDistM, bool isLastPoint)
: m_bearDistM(bearDistM), m_isLastPoint(isLastPoint)
{
}
m2::PointD BearingPointsSelector::GetStartPoint(Graph::Edge const & e) const
{
return m_isLastPoint ? e.GetEndPoint() : e.GetStartPoint();
}
m2::PointD BearingPointsSelector::GetEndPoint(Graph::Edge const & e, double distanceM)
{
if (distanceM < m_bearDistM && m_bearDistM <= distanceM + EdgeLength(e))
{
auto const edgeLen = EdgeLength(e);
auto const edgeBearDist = min(m_bearDistM - distanceM, edgeLen);
CHECK_LESS_OR_EQUAL(edgeBearDist, edgeLen, ());
return m_isLastPoint ? PointAtSegmentM(e.GetEndPoint(), e.GetStartPoint(), edgeBearDist)
: PointAtSegmentM(e.GetStartPoint(), e.GetEndPoint(), edgeBearDist);
}
return m_isLastPoint ? e.GetStartPoint() : e.GetEndPoint();
}
bool PointsAreClose(m2::PointD const & p1, m2::PointD const & p2)
{
double const kMwmRoadCrossingRadiusMeters = routing::GetRoadCrossingRadiusMeters();
@ -47,11 +129,11 @@ bool EdgesAreAlmostEqual(Graph::Edge const & e1, Graph::Edge const & e2)
PointsAreClose(e1.GetEndPoint(), e2.GetEndPoint());
}
std::string LogAs2GisPath(Graph::EdgeVector const & path)
string LogAs2GisPath(Graph::EdgeVector const & path)
{
CHECK(!path.empty(), ("Paths should not be empty"));
std::ostringstream ost;
ostringstream ost;
ost << "https://2gis.ru/moscow?queryState=";
auto ll = MercatorBounds::ToLatLon(path.front().GetStartPoint());
@ -69,9 +151,9 @@ std::string LogAs2GisPath(Graph::EdgeVector const & path)
return ost.str();
}
std::string LogAs2GisPath(Graph::Edge const & e) { return LogAs2GisPath(Graph::EdgeVector({e})); }
string LogAs2GisPath(Graph::Edge const & e) { return LogAs2GisPath(Graph::EdgeVector({e})); }
bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, FormOfWay fow,
bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, FormOfWay formOfWay,
int frcThreshold, RoadInfoGetter & infoGetter)
{
if (e.IsFake() || restriction == FunctionalRoadClass::NotAValue)
@ -81,6 +163,22 @@ bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, F
return static_cast<int>(frc) <= static_cast<int>(restriction) + frcThreshold;
}
bool PassesRestrictionV3(Graph::Edge const & e, FunctionalRoadClass functionalRoadClass,
FormOfWay formOfWay, RoadInfoGetter & infoGetter, Score & score)
{
CHECK(!e.IsFake(), ("Edges should not be fake:", e));
auto const frcScore = GetFrcScore(e, functionalRoadClass, infoGetter);
if (frcScore == boost::none)
return false;
score = frcScore.get();
Score constexpr kScoreForFormOfWay = 25;
if (formOfWay == FormOfWay::Roundabout && infoGetter.Get(e.GetFeatureId()).m_isRoundabout)
score += kScoreForFormOfWay;
return true;
}
bool ConformLfrcnp(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint,
int frcThreshold, RoadInfoGetter & infoGetter)
{
@ -90,4 +188,45 @@ bool ConformLfrcnp(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoi
auto const frc = HighwayClassToFunctionalRoadClass(infoGetter.Get(e.GetFeatureId()).m_hwClass);
return static_cast<int>(frc) <= static_cast<int>(lowestFrcToNextPoint) + frcThreshold;
}
bool ConformLfrcnpV3(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint,
RoadInfoGetter & infoGetter)
{
return GetFrcScore(e, lowestFrcToNextPoint, infoGetter) != boost::none;
}
size_t IntersectionLen(Graph::EdgeVector a, Graph::EdgeVector b)
{
sort(a.begin(), a.end());
sort(b.begin(), b.end());
return set_intersection(a.begin(), a.end(), b.begin(), b.end(), CounterIterator()).GetCount();
}
bool SuffixEqualsPrefix(Graph::EdgeVector const & a, Graph::EdgeVector const & b, size_t len)
{
CHECK_LESS_OR_EQUAL(len, a.size(), ());
CHECK_LESS_OR_EQUAL(len, b.size(), ());
return equal(a.end() - len, a.end(), b.begin());
}
// Returns a length of the longest suffix of |a| that matches any prefix of |b|.
// Neither |a| nor |b| can contain several repetitions of any edge.
// Returns -1 if |a| intersection |b| is not equal to some suffix of |a| and some prefix of |b|.
int32_t PathOverlappingLen(Graph::EdgeVector const & a, Graph::EdgeVector const & b)
{
auto const len = IntersectionLen(a, b);
if (SuffixEqualsPrefix(a, b, len))
return base::checked_cast<int32_t>(len);
return -1;
}
m2::PointD PointAtSegmentM(m2::PointD const & p1, m2::PointD const & p2, double const distanceM)
{
auto const v = p2 - p1;
auto const l = v.Length();
auto const L = MercatorBounds::DistanceOnEarth(p1, p2);
auto const delta = distanceM * l / L;
return PointAtSegment(p1, p2, delta);
}
} // namespace openlr

View file

@ -2,15 +2,32 @@
#include "openlr/graph.hpp"
#include "openlr/openlr_model.hpp"
#include "openlr/score_types.hpp"
#include "geometry/point2d.hpp"
#include <cstdint>
#include <string>
#include <type_traits>
namespace openlr
{
class RoadInfoGetter;
// This class is used to get points for further bearing calculations.
class BearingPointsSelector
{
public:
BearingPointsSelector(uint32_t bearDistM, bool isLastPoint);
m2::PointD GetStartPoint(Graph::Edge const & e) const;
m2::PointD GetEndPoint(Graph::Edge const & e, double distanceM);
private:
double m_bearDistM;
bool m_isLastPoint;
};
bool PointsAreClose(m2::PointD const & p1, m2::PointD const & p2);
double EdgeLength(Graph::Edge const & e);
@ -28,12 +45,29 @@ std::common_type_t<T, U> AbsDifference(T const a, U const b)
return a >= b ? a - b : b - a;
}
bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, FormOfWay fow,
bool PassesRestriction(Graph::Edge const & e, FunctionalRoadClass restriction, FormOfWay formOfWay,
int frcThreshold, RoadInfoGetter & infoGetter);
/// \returns true if |e| conforms |functionalRoadClass| and |formOfWay| and false otherwise.
/// \note If the method returns true |score| should be considered next.
bool PassesRestrictionV3(Graph::Edge const & e, FunctionalRoadClass functionalRoadClass,
FormOfWay formOfWay, RoadInfoGetter & infoGetter, Score & score);
/// \returns true if edge |e| conforms Lowest Functional Road Class to Next Point.
/// \note frc means Functional Road Class. Please see openlr documentation for details:
/// http://www.openlr.org/data/docs/whitepaper/1_0/OpenLR-Whitepaper_v1.0.pdf
bool ConformLfrcnp(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint,
int frcThreshold, RoadInfoGetter & infoGetter);
bool ConformLfrcnpV3(Graph::Edge const & e, FunctionalRoadClass lowestFrcToNextPoint,
RoadInfoGetter & infoGetter);
size_t IntersectionLen(Graph::EdgeVector a, Graph::EdgeVector b);
bool SuffixEqualsPrefix(Graph::EdgeVector const & a, Graph::EdgeVector const & b, size_t len);
// Returns a length of the longest suffix of |a| that matches any prefix of |b|.
// Neither |a| nor |b| can contain several repetitions of any edge.
// Returns -1 if |a| intersection |b| is not equal to some suffix of |a| and some prefix of |b|.
int32_t PathOverlappingLen(Graph::EdgeVector const & a, Graph::EdgeVector const & b);
m2::PointD PointAtSegmentM(m2::PointD const & p1, m2::PointD const & p2, double const distanceM);
} // namespace openlr

View file

@ -10,6 +10,10 @@
#include "openlr/paths_connector.hpp"
#include "openlr/road_info_getter.hpp"
#include "openlr/router.hpp"
#include "openlr/score_candidate_paths_getter.hpp"
#include "openlr/score_candidate_points_getter.hpp"
#include "openlr/score_paths_connector.hpp"
#include "openlr/score_types.hpp"
#include "openlr/way_point.hpp"
#include "routing/features_road_graph.hpp"
@ -17,11 +21,11 @@
#include "routing_common/car_model.hpp"
#include "storage/country_info_getter.hpp"
#include "indexer/classificator.hpp"
#include "indexer/data_source.hpp"
#include "storage/country_info_getter.hpp"
#include "platform/country_file.hpp"
#include "geometry/mercator.hpp"
@ -163,12 +167,13 @@ void ExpandFakes(DataSource const & dataSource, Graph & g, Graph::EdgeVector & p
// Returns an iterator pointing to the first edge that should not be cut off.
// Offsets denote a distance in meters one should travel from the start/end of the path
// to some point alog that path and drop everything form the start to that point or from
// to some point along that path and drop everything form the start to that point or from
// that point to the end.
template <typename InputIterator>
InputIterator CutOffset(InputIterator start, InputIterator const stop, double const offset)
InputIterator CutOffset(InputIterator start, InputIterator stop, double offset,
bool keepEnd)
{
if (offset == 0)
if (offset == 0.0)
return start;
for (double distance = 0.0; start != stop; ++start)
@ -176,8 +181,8 @@ InputIterator CutOffset(InputIterator start, InputIterator const stop, double co
auto const edgeLen = EdgeLength(*start);
if (distance <= offset && offset < distance + edgeLen)
{
// Throw out this edge if (offest - distance) is greater than edgeLength / 2.
if (2 * (offset - distance) >= edgeLen)
// Throw out this edge if (offset - distance) is greater than edgeLength / 2.
if (!keepEnd && offset - distance >= edgeLen / 2.0)
++start;
break;
}
@ -188,21 +193,23 @@ InputIterator CutOffset(InputIterator start, InputIterator const stop, double co
}
template <typename InputIterator, typename OutputIterator>
void CopyWithoutOffsets(InputIterator const start, InputIterator const stop, OutputIterator out,
uint32_t const positiveOffset, uint32_t const negativeOffset)
void CopyWithoutOffsets(InputIterator start, InputIterator stop, OutputIterator out,
uint32_t positiveOffset, uint32_t negativeOffset, bool keepEnds)
{
auto from = start;
auto to = stop;
if (distance(start, stop) > 1)
{
from = CutOffset(start, stop, positiveOffset);
from = CutOffset(start, stop, positiveOffset, keepEnds);
// |to| points past the last edge we need to take.
to = CutOffset(reverse_iterator<InputIterator>(stop), reverse_iterator<InputIterator>(start),
negativeOffset)
.base();
negativeOffset, keepEnds).base();
}
if (!keepEnds)
CHECK(from <= to, ("From iterator is less or equal than to."));
if (from >= to)
return;
@ -288,9 +295,9 @@ public:
bool DecodeSegment(LinearSegment const & segment, DecodedPath & path, v2::Stats & stat)
{
double const kPathLengthTolerance = 0.30;
uint32_t const kMaxJunctionCandidates = 10;
uint32_t const kMaxProjectionCandidates = 5;
double constexpr kPathLengthTolerance = 0.30;
uint32_t constexpr kMaxJunctionCandidates = 10;
uint32_t constexpr kMaxProjectionCandidates = 5;
m_graph.ResetFakes();
@ -302,8 +309,8 @@ public:
lineCandidates.reserve(points.size());
LOG(LDEBUG, ("Decoding segment:", segment.m_segmentId, "with", points.size(), "points"));
CandidatePointsGetter pointsGetter(kMaxJunctionCandidates, kMaxProjectionCandidates, m_dataSource,
m_graph);
CandidatePointsGetter pointsGetter(kMaxJunctionCandidates, kMaxProjectionCandidates,
m_dataSource, m_graph);
CandidatePathsGetter pathsGetter(pointsGetter, m_graph, m_infoGetter, stat);
if (!pathsGetter.GetLineCandidatesForPoints(points, lineCandidates))
@ -346,7 +353,99 @@ public:
ExpandFakes(m_dataSource, m_graph, route);
ASSERT(none_of(begin(route), end(route), mem_fn(&Graph::Edge::IsFake)), (segment.m_segmentId));
CopyWithoutOffsets(begin(route), end(route), back_inserter(path.m_path), positiveOffsetM,
negativeOffsetM);
negativeOffsetM, false /* keep ends */);
if (path.m_path.empty())
{
++stat.m_wrongOffsets;
LOG(LINFO, ("Path is empty after offsets cutting. segmentId:", segment.m_segmentId));
return false;
}
return true;
}
private:
DataSource const & m_dataSource;
Graph m_graph;
RoadInfoGetter m_infoGetter;
};
// The idea behind the third version of matching algorithm is to collect a lot of candidates (paths)
// which correspond an openlr edges with some score. And on the final stage to decide which
// candidate is better.
class SegmentsDecoderV3
{
public:
SegmentsDecoderV3(DataSource const & dataSource, unique_ptr<CarModelFactory> carModelFactory)
: m_dataSource(dataSource), m_graph(dataSource, move(carModelFactory)), m_infoGetter(dataSource)
{
}
bool DecodeSegment(LinearSegment const & segment, DecodedPath & path, v2::Stats & stat)
{
LOG(LDEBUG, ("DecodeSegment(...) seg id:", segment.m_segmentId, ", point num:", segment.GetLRPs().size()));
uint32_t constexpr kMaxJunctionCandidates = 10;
uint32_t constexpr kMaxProjectionCandidates = 5;
path.m_segmentId.Set(segment.m_segmentId);
auto const & points = segment.GetLRPs();
CHECK_GREATER(points.size(), 1, ("A segment cannot consist of less than two points"));
vector<ScorePathVec> lineCandidates;
lineCandidates.reserve(points.size());
LOG(LDEBUG, ("Decoding segment:", segment.m_segmentId, "with", points.size(), "points"));
ScoreCandidatePointsGetter pointsGetter(kMaxJunctionCandidates, kMaxProjectionCandidates,
m_dataSource, m_graph);
ScoreCandidatePathsGetter pathsGetter(pointsGetter, m_graph, m_infoGetter, stat);
if (!pathsGetter.GetLineCandidatesForPoints(points, lineCandidates))
return false;
vector<Graph::EdgeVector> resultPath;
ScorePathsConnector connector(m_graph, m_infoGetter, stat);
if (!connector.FindBestPath(points, lineCandidates, resultPath))
{
LOG(LINFO, ("Connections not found:", segment.m_segmentId));
return false;
}
Graph::EdgeVector route;
for (auto const & part : resultPath)
route.insert(route.end(), part.begin(), part.end());
double requiredRouteDistanceM = 0.0;
// Sum up all distances between points. Last point's m_distanceToNextPoint
// should be equal to zero, but let's skip it just in case.
CHECK(!points.empty(), ());
for (auto it = points.begin(); it != prev(points.end()); ++it)
requiredRouteDistanceM += it->m_distanceToNextPoint;
double actualRouteDistanceM = 0.0;
for (auto const & e : route)
actualRouteDistanceM += EdgeLength(e);
auto const scale = actualRouteDistanceM / requiredRouteDistanceM;
LOG(LDEBUG, ("actualRouteDistance:", actualRouteDistanceM,
"requiredRouteDistance:", requiredRouteDistanceM, "scale:", scale));
if (segment.m_locationReference.m_positiveOffsetMeters +
segment.m_locationReference.m_negativeOffsetMeters >=
requiredRouteDistanceM)
{
++stat.m_wrongOffsets;
LOG(LINFO, ("Wrong offsets for segment:", segment.m_segmentId));
return false;
}
auto const positiveOffsetM = segment.m_locationReference.m_positiveOffsetMeters * scale;
auto const negativeOffsetM = segment.m_locationReference.m_negativeOffsetMeters * scale;
CHECK(none_of(route.begin(), route.end(), mem_fn(&Graph::Edge::IsFake)), (segment.m_segmentId));
CopyWithoutOffsets(route.begin(), route.end(), back_inserter(path.m_path), positiveOffsetM,
negativeOffsetM, true /* keep ends */);
if (path.m_path.empty())
{
@ -419,6 +518,12 @@ void OpenLRDecoder::DecodeV2(vector<LinearSegment> const & segments, uint32_t co
Decode<SegmentsDecoderV2, v2::Stats>(segments, numThreads, paths);
}
void OpenLRDecoder::DecodeV3(vector<LinearSegment> const & segments, uint32_t numThreads,
vector<DecodedPath> & paths)
{
Decode<SegmentsDecoderV3, v2::Stats>(segments, numThreads, paths);
}
template <typename Decoder, typename Stats>
void OpenLRDecoder::Decode(vector<LinearSegment> const & segments,
uint32_t const numThreads, vector<DecodedPath> & paths)
@ -450,6 +555,7 @@ void OpenLRDecoder::Decode(vector<LinearSegment> const & segments,
}
};
base::Timer timer;
vector<Stats> stats(numThreads);
vector<thread> workers;
for (size_t i = 1; i < numThreads; ++i)
@ -464,5 +570,6 @@ void OpenLRDecoder::Decode(vector<LinearSegment> const & segments,
allStats.Add(s);
allStats.Report();
LOG(LINFO, ("Matching tool:", timer.ElapsedSeconds(), "seconds."));
}
} // namespace openlr

View file

@ -50,6 +50,9 @@ public:
void DecodeV2(std::vector<LinearSegment> const & segments, uint32_t const numThreads,
std::vector<DecodedPath> & paths);
void DecodeV3(std::vector<LinearSegment> const & segments, uint32_t numThreads,
std::vector<DecodedPath> & paths);
private:
template <typename Decoder, typename Stats>
void Decode(std::vector<LinearSegment> const & segments, uint32_t const numThreads,

View file

@ -110,8 +110,8 @@ bool ValidateLimit(char const * flagname, int32_t value)
{
if (value < -1)
{
printf("Invalid value for --%s: %d, must be greater or equal to -1\n", flagname,
static_cast<int>(value));
LOG(LINFO, ("Valid value for --", std::string(flagname), ":", value,
"must be greater or equal to -1."));
return false;
}
@ -122,9 +122,8 @@ bool ValidateNumThreads(char const * flagname, int32_t value)
{
if (value < kMinNumThreads || value > kMaxNumThreads)
{
printf("Invalid value for --%s: %d, must be between %d and %d inclusively\n", flagname,
static_cast<int>(value), static_cast<int>(kMinNumThreads),
static_cast<int>(kMaxNumThreads));
LOG(LINFO, ("Valid value for --", std::string(flagname), ":", value, "must be between",
kMinNumThreads, "and", kMaxNumThreads));
return false;
}
@ -135,7 +134,7 @@ bool ValidateMwmPath(char const * flagname, std::string const & value)
{
if (value.empty())
{
printf("--%s should be specified\n", flagname);
LOG(LINFO, ("--", std::string(flagname), "should be specified."));
return false;
}
@ -146,13 +145,13 @@ bool ValidateVersion(char const * flagname, int32_t value)
{
if (value == 0)
{
printf("--%s should be specified\n", flagname);
LOG(LINFO, ("--", std::string(flagname), "should be specified."));
return false;
}
if (value != 1 && value != 2)
if (value != 1 && value != 2 && value != 3)
{
printf("--%s should be one of 1 or 2\n", flagname);
LOG(LINFO, ("--", std::string(flagname), "should be one of 1, 2 or 3."));
return false;
}
@ -282,7 +281,8 @@ int main(int argc, char * argv[])
{
case 1: decoder.DecodeV1(segments, numThreads, paths); break;
case 2: decoder.DecodeV2(segments, numThreads, paths); break;
default: ASSERT(false, ("There should be no way to fall here"));
case 3: decoder.DecodeV3(segments, numThreads, paths); break;
default: CHECK(false, ("Wrong algorithm version."));
}
SaveNonMatchedIds(FLAGS_non_matched_ids, paths);

View file

@ -1,4 +1,5 @@
#include "openlr/paths_connector.hpp"
#include "openlr/helpers.hpp"
#include "base/checked_cast.hpp"
@ -15,31 +16,6 @@ namespace openlr
{
namespace
{
size_t IntersectionLen(Graph::EdgeVector a, Graph::EdgeVector b)
{
sort(begin(a), end(a));
sort(begin(b), end(b));
return set_intersection(begin(a), end(a), begin(b), end(b), CounterIterator()).GetCount();
}
bool PrefEqualsSuff(Graph::EdgeVector const & a, Graph::EdgeVector const & b, size_t const len)
{
ASSERT_LESS_OR_EQUAL(len, a.size(), ());
ASSERT_LESS_OR_EQUAL(len, b.size(), ());
return equal(end(a) - len, end(a), begin(b));
}
// Returns a length of the longest suffix of |a| that matches any prefix of |b|.
// Neither |a| nor |b| can contain several repetitions of any edge.
// Returns -1 if |a| intersection |b| is not equal to some suffix of |a| and some prefix of |b|.
int32_t PathOverlappingLen(Graph::EdgeVector const & a, Graph::EdgeVector const & b)
{
auto const len = IntersectionLen(a, b);
if (PrefEqualsSuff(a, b, len))
return base::checked_cast<int32_t>(len);
return -1;
}
bool ValidatePath(Graph::EdgeVector const & path,
double const distanceToNextPoint,
double const pathLengthTolerance)

View file

@ -13,6 +13,7 @@ RoadInfoGetter::RoadInfo::RoadInfo(FeatureType & ft)
: m_hwClass(ftypes::GetHighwayClass(feature::TypesHolder(ft)))
, m_link(ftypes::IsLinkChecker::Instance()(ft))
, m_oneWay(ftypes::IsOneWayChecker::Instance()(ft))
, m_isRoundabout(ftypes::IsRoundAboutChecker::Instance()(ft))
{
}

View file

@ -27,6 +27,7 @@ public:
ftypes::HighwayClass m_hwClass = ftypes::HighwayClass::Undefined;
bool m_link = false;
bool m_oneWay = false;
bool m_isRoundabout = false;
};
explicit RoadInfoGetter(DataSource const & dataSource);

View file

@ -41,83 +41,44 @@ uint32_t Bearing(m2::PointD const & a, m2::PointD const & b)
CHECK_GREATER_OR_EQUAL(angle, 0, ("Angle should be greater than or equal to 0"));
return base::clamp(angle / kAnglesInBucket, 0.0, 255.0);
}
class Score final
{
public:
// A weight for total length of true fake edges.
static const int kTrueFakeCoeff = 10;
// A weight for total length of fake edges that are parts of some
// real edges.
static constexpr double kFakeCoeff = 0.001;
// A weight for passing too far from pivot points.
static const int kIntermediateErrorCoeff = 3;
// A weight for excess of distance limit.
static const int kDistanceErrorCoeff = 3;
// A weight for deviation from bearing.
static const int kBearingErrorCoeff = 5;
void AddDistance(double p) { m_distance += p; }
void AddFakePenalty(double p, bool partOfReal)
{
m_penalty += (partOfReal ? kFakeCoeff : kTrueFakeCoeff) * p;
}
void AddIntermediateErrorPenalty(double p) { m_penalty += kIntermediateErrorCoeff * p; }
void AddDistanceErrorPenalty(double p) { m_penalty += kDistanceErrorCoeff * p; }
void AddBearingPenalty(int expected, int actual)
{
ASSERT_LESS(expected, kNumBuckets, ());
ASSERT_GREATER_OR_EQUAL(expected, 0, ());
ASSERT_LESS(actual, kNumBuckets, ());
ASSERT_GREATER_OR_EQUAL(actual, 0, ());
int const diff = abs(expected - actual);
double angle = base::DegToRad(std::min(diff, kNumBuckets - diff) * kAnglesInBucket);
m_penalty += kBearingErrorCoeff * angle * kBearingDist;
}
double GetDistance() const { return m_distance; }
double GetPenalty() const { return m_penalty; }
double GetScore() const { return m_distance + m_penalty; }
bool operator<(Score const & rhs) const
{
auto const ls = GetScore();
auto const rs = rhs.GetScore();
if (ls != rs)
return ls < rs;
if (m_distance != rhs.m_distance)
return m_distance < rhs.m_distance;
return m_penalty < rhs.m_penalty;
}
bool operator>(Score const & rhs) const { return rhs < *this; }
bool operator==(Score const & rhs) const
{
return m_distance == rhs.m_distance && m_penalty == rhs.m_penalty;
}
bool operator!=(Score const & rhs) const { return !(*this == rhs); }
private:
// Reduced length of path in meters.
double m_distance = 0.0;
double m_penalty = 0.0;
};
} // namespace
// Router::Vertex::Score ---------------------------------------------------------------------------
void Router::Vertex::Score::AddFakePenalty(double p, bool partOfReal)
{
m_penalty += (partOfReal ? kFakeCoeff : kTrueFakeCoeff) * p;
}
void Router::Vertex::Score::AddBearingPenalty(int expected, int actual)
{
ASSERT_LESS(expected, kNumBuckets, ());
ASSERT_GREATER_OR_EQUAL(expected, 0, ());
ASSERT_LESS(actual, kNumBuckets, ());
ASSERT_GREATER_OR_EQUAL(actual, 0, ());
int const diff = abs(expected - actual);
double angle = base::DegToRad(std::min(diff, kNumBuckets - diff) * kAnglesInBucket);
m_penalty += kBearingErrorCoeff * angle * kBearingDist;
}
bool Router::Vertex::Score::operator<(Score const & rhs) const
{
auto const ls = GetScore();
auto const rs = rhs.GetScore();
if (ls != rs)
return ls < rs;
if (m_distance != rhs.m_distance)
return m_distance < rhs.m_distance;
return m_penalty < rhs.m_penalty;
}
bool Router::Vertex::Score::operator==(Score const & rhs) const
{
return m_distance == rhs.m_distance && m_penalty == rhs.m_penalty;
}
// Router::Vertex ----------------------------------------------------------------------------------
Router::Vertex::Vertex(routing::Junction const & junction, routing::Junction const & stageStart,
double stageStartDistance, size_t stage, bool bearingChecked)
@ -245,18 +206,19 @@ bool Router::Init(std::vector<WayPoint> const & points, double positiveOffsetM,
bool Router::FindPath(std::vector<routing::Edge> & path)
{
using State = std::pair<Score, Vertex>;
using State = std::pair<Vertex::Score, Vertex>;
std::priority_queue<State, std::vector<State>, greater<State>> queue;
std::map<Vertex, Score> scores;
std::map<Vertex, Vertex::Score> scores;
Links links;
auto pushVertex = [&queue, &scores, &links](Vertex const & u, Vertex const & v, Score const & sv,
Edge const & e) {
if ((scores.count(v) == 0 || scores[v].GetScore() > sv.GetScore() + kEps) && u != v)
auto const pushVertex = [&queue, &scores, &links](Vertex const & u, Vertex const & v,
Vertex::Score const & vertexScore,
Edge const & e) {
if ((scores.count(v) == 0 || scores[v].GetScore() > vertexScore.GetScore() + kEps) && u != v)
{
scores[v] = sv;
scores[v] = vertexScore;
links[v] = make_pair(u, e);
queue.emplace(sv, v);
queue.emplace(vertexScore, v);
}
};
@ -264,7 +226,7 @@ bool Router::FindPath(std::vector<routing::Edge> & path)
false /* bearingChecked */);
CHECK(!NeedToCheckBearing(s, 0 /* distance */), ());
scores[s] = Score();
scores[s] = Vertex::Score();
queue.emplace(scores[s], s);
double const piS = GetPotential(s);
@ -274,7 +236,7 @@ bool Router::FindPath(std::vector<routing::Edge> & path)
auto const p = queue.top();
queue.pop();
Score const & su = p.first;
Vertex::Score const & su = p.first;
Vertex const & u = p.second;
if (su != scores[u])
@ -314,7 +276,7 @@ bool Router::FindPath(std::vector<routing::Edge> & path)
{
Vertex v = u;
Score sv = su;
Vertex::Score sv = su;
if (u.m_junction != u.m_stageStart)
{
int const expected = m_points[stage].m_bearing;
@ -332,7 +294,7 @@ bool Router::FindPath(std::vector<routing::Edge> & path)
false /* bearingChecked */);
double const piV = GetPotential(v);
Score sv = su;
Vertex::Score sv = su;
sv.AddDistance(std::max(piV - piU, 0.0));
sv.AddIntermediateErrorPenalty(
MercatorBounds::DistanceOnEarth(v.m_junction.GetPoint(), m_points[v.m_stage].m_point));
@ -353,7 +315,7 @@ bool Router::FindPath(std::vector<routing::Edge> & path)
double const piV = GetPotential(v);
Score sv = su;
Vertex::Score sv = su;
double const w = GetWeight(edge);
sv.AddDistance(std::max(w + piV - piU, 0.0));

View file

@ -31,6 +31,47 @@ public:
private:
struct Vertex final
{
class Score final
{
public:
// A weight for total length of true fake edges.
static const int kTrueFakeCoeff = 10;
// A weight for total length of fake edges that are parts of some
// real edges.
static constexpr double kFakeCoeff = 0.001;
// A weight for passing too far from pivot points.
static const int kIntermediateErrorCoeff = 3;
// A weight for excess of distance limit.
static const int kDistanceErrorCoeff = 3;
// A weight for deviation from bearing.
static const int kBearingErrorCoeff = 5;
void AddDistance(double p) { m_distance += p; }
void AddFakePenalty(double p, bool partOfReal);
void AddIntermediateErrorPenalty(double p) { m_penalty += kIntermediateErrorCoeff * p; }
void AddDistanceErrorPenalty(double p) { m_penalty += kDistanceErrorCoeff * p; }
void AddBearingPenalty(int expected, int actual);
double GetDistance() const { return m_distance; }
double GetPenalty() const { return m_penalty; }
double GetScore() const { return m_distance + m_penalty; }
bool operator<(Score const & rhs) const;
bool operator>(Score const & rhs) const { return rhs < *this; }
bool operator==(Score const & rhs) const;
bool operator!=(Score const & rhs) const { return !(*this == rhs); }
private:
// Reduced length of path in meters.
double m_distance = 0.0;
double m_penalty = 0.0;
};
Vertex() = default;
Vertex(routing::Junction const & junction, routing::Junction const & stageStart,
double stageStartDistance, size_t stage, bool bearingChecked);

View file

@ -0,0 +1,288 @@
#include "openlr/score_candidate_paths_getter.hpp"
#include "openlr/graph.hpp"
#include "openlr/helpers.hpp"
#include "openlr/openlr_model.hpp"
#include "openlr/score_candidate_points_getter.hpp"
#include "routing/road_graph.hpp"
#include "platform/location.hpp"
#include "geometry/angles.hpp"
#include "geometry/mercator.hpp"
#include "base/logging.hpp"
#include "base/stl_helpers.hpp"
#include <algorithm>
#include <functional>
#include <iterator>
#include <queue>
#include <set>
#include <tuple>
#include <utility>
using namespace routing;
using namespace std;
namespace openlr
{
namespace
{
int constexpr kNumBuckets = 256;
double constexpr kAnglesInBucket = 360.0 / kNumBuckets;
double ToAngleInDeg(uint32_t angleInBuckets)
{
CHECK_GREATER_OR_EQUAL(angleInBuckets, 0, ());
CHECK_LESS_OR_EQUAL(angleInBuckets, 255, ());
return base::clamp(kAnglesInBucket * static_cast<double>(angleInBuckets), 0.0, 360.0);
}
uint32_t BearingInDeg(m2::PointD const & a, m2::PointD const & b)
{
auto const angle = location::AngleToBearing(base::RadToDeg(ang::AngleTo(a, b)));
CHECK_LESS_OR_EQUAL(angle, 360.0, ());
CHECK_GREATER_OR_EQUAL(angle, 0.0, ());
return angle;
}
double DifferenceInDeg(double a1, double a2)
{
auto const diff = 180.0 - abs(abs(a1 - a2) - 180.0);
CHECK_LESS_OR_EQUAL(diff, 180.0, ());
CHECK_GREATER_OR_EQUAL(diff, 0.0, ());
return diff;
}
} // namespace
// ScoreCandidatePathsGetter::Link ----------------------------------------------------------------------
Graph::Edge ScoreCandidatePathsGetter::Link::GetStartEdge() const
{
auto * start = this;
while (start->m_parent)
start = start->m_parent.get();
return start->m_edge;
}
bool ScoreCandidatePathsGetter::Link::IsJunctionInPath(Junction const & j) const
{
for (auto * l = this; l; l = l->m_parent.get())
{
if (l->m_edge.GetEndJunction() == j)
{
LOG(LDEBUG, ("A loop detected, skipping..."));
return true;
}
}
return false;
}
// ScoreCandidatePathsGetter ----------------------------------------------------------------------------
bool ScoreCandidatePathsGetter::GetLineCandidatesForPoints(
vector<LocationReferencePoint> const & points, vector<ScorePathVec> & lineCandidates)
{
CHECK_GREATER(points.size(), 1, ());
for (size_t i = 0; i < points.size(); ++i)
{
if (i != points.size() - 1 && points[i].m_distanceToNextPoint == 0)
{
LOG(LINFO, ("Distance to next point is zero. Skipping the whole segment"));
++m_stats.m_zeroDistToNextPointCount;
return false;
}
lineCandidates.emplace_back();
auto const isLastPoint = i == points.size() - 1;
double const distanceToNextPointM =
(isLastPoint ? points[i - 1] : points[i]).m_distanceToNextPoint;
ScoreEdgeVec edgesCandidates;
m_pointsGetter.GetEdgeCandidates(MercatorBounds::FromLatLon(points[i].m_latLon),
isLastPoint, edgesCandidates);
GetLineCandidates(points[i], isLastPoint, distanceToNextPointM, edgesCandidates,
lineCandidates.back());
if (lineCandidates.back().empty())
{
LOG(LINFO, ("No candidate lines found for point", points[i].m_latLon, "Giving up"));
++m_stats.m_noCandidateFound;
return false;
}
}
CHECK_EQUAL(lineCandidates.size(), points.size(), ());
return true;
}
void ScoreCandidatePathsGetter::GetAllSuitablePaths(ScoreEdgeVec const & startLines,
bool isLastPoint, double bearDistM,
FunctionalRoadClass functionalRoadClass,
FormOfWay formOfWay,
vector<shared_ptr<Link>> & allPaths)
{
queue<shared_ptr<Link>> q;
for (auto const & e : startLines)
{
Score roadScore = 0; // Score based on functional road class and form of way.
if (!PassesRestrictionV3(e.m_edge, functionalRoadClass, formOfWay, m_infoGetter, roadScore))
continue;
q.push(
make_shared<Link>(nullptr /* parent */, e.m_edge, 0 /* distanceM */, e.m_score, roadScore));
}
// Filling |allPaths| staring from |startLines| which have passed functional road class
// and form of way restrictions. All paths in |allPaths| are shorter then |bearDistM| plus
// one segment length.
while (!q.empty())
{
auto const u = q.front();
q.pop();
auto const & currentEdge = u->m_edge;
auto const currentEdgeLen = EdgeLength(currentEdge);
if (u->m_distanceM + currentEdgeLen >= bearDistM)
{
allPaths.emplace_back(move(u));
continue;
}
CHECK_LESS(u->m_distanceM + currentEdgeLen, bearDistM, ());
Graph::EdgeVector edges;
if (!isLastPoint)
m_graph.GetOutgoingEdges(currentEdge.GetEndJunction(), edges);
else
m_graph.GetIngoingEdges(currentEdge.GetStartJunction(), edges);
for (auto const & e : edges)
{
CHECK(!e.IsFake(), ());
if (EdgesAreAlmostEqual(e.GetReverseEdge(), currentEdge))
continue;
CHECK(currentEdge.HasRealPart(), ());
Score roadScore = 0;
if (!PassesRestrictionV3(e, functionalRoadClass, formOfWay, m_infoGetter, roadScore))
continue;
if (u->IsJunctionInPath(e.GetEndJunction()))
continue;
// Road score for a path is minimum value of score of segments based on functional road class
// of the segments and form of way of the segments.
q.emplace(make_shared<Link>(u, e, u->m_distanceM + currentEdgeLen, u->m_pointScore,
min(roadScore, u->m_minRoadScore)));
}
}
}
void ScoreCandidatePathsGetter::GetBestCandidatePaths(
vector<shared_ptr<Link>> const & allPaths, bool isLastPoint, uint32_t requiredBearing,
double bearDistM, m2::PointD const & startPoint, ScorePathVec & candidates)
{
CHECK_GREATER_OR_EQUAL(requiredBearing, 0, ());
CHECK_LESS_OR_EQUAL(requiredBearing, 255, ());
multiset<CandidatePath, greater<>> candidatePaths;
BearingPointsSelector pointsSelector(static_cast<uint32_t>(bearDistM), isLastPoint);
for (auto const & link : allPaths)
{
auto const bearStartPoint = pointsSelector.GetStartPoint(link->GetStartEdge());
// Number of edges counting from the last one to check bearing on. According to OpenLR spec
// we have to check bearing on a point that is no longer than 25 meters traveling down the path.
// But sometimes we may skip the best place to stop and generate a candidate. So we check several
// edges before the last one to avoid such a situation. Number of iterations is taken
// by intuition.
// Example:
// o -------- o { Partners segment. }
// o ------- o --- o { Our candidate. }
// ^ 25m
// ^ This one may be better than
// ^ this one.
// So we want to check them all.
uint32_t traceBackIterationsLeft = 3;
for (auto part = link; part; part = part->m_parent)
{
if (traceBackIterationsLeft == 0)
break;
--traceBackIterationsLeft;
auto const bearEndPoint = pointsSelector.GetEndPoint(part->m_edge, part->m_distanceM);
auto const bearingDeg = BearingInDeg(bearStartPoint, bearEndPoint);
double const requiredBearingDeg = ToAngleInDeg(requiredBearing);
double const angleDeviationDeg = DifferenceInDeg(bearingDeg, requiredBearingDeg);
// If the bearing according to osm segments (|bearingDeg|) is significantly different
// from the bearing set in openlr (|requiredBearingDeg|) the candidate should be skipped.
double constexpr kMinAngleDeviationDeg = 50.0;
if (angleDeviationDeg > kMinAngleDeviationDeg)
continue;
double constexpr kMaxScoreForBearing = 60.0;
double constexpr kAngleDeviationFactor = 4.3;
auto const bearingScore = static_cast<Score>(
kMaxScoreForBearing / (1.0 + angleDeviationDeg / kAngleDeviationFactor));
candidatePaths.emplace(part, part->m_pointScore, part->m_minRoadScore, bearingScore);
}
}
size_t constexpr kMaxCandidates = 7;
vector<CandidatePath> paths;
copy_n(candidatePaths.begin(), min(static_cast<size_t>(kMaxCandidates), candidatePaths.size()),
back_inserter(paths));
for (auto const & path : paths)
{
Graph::EdgeVector edges;
for (auto * p = path.m_path.get(); p; p = p->m_parent.get())
edges.push_back(p->m_edge);
if (!isLastPoint)
reverse(edges.begin(), edges.end());
candidates.emplace_back(path.GetScore(), move(edges));
}
}
void ScoreCandidatePathsGetter::GetLineCandidates(openlr::LocationReferencePoint const & p,
bool isLastPoint,
double distanceToNextPointM,
ScoreEdgeVec const & edgeCandidates,
ScorePathVec & candidates)
{
double constexpr kDefaultBearDistM = 25.0;
double const bearDistM = min(kDefaultBearDistM, distanceToNextPointM);
ScoreEdgeVec const & startLines = edgeCandidates;
LOG(LDEBUG, ("Listing start lines:"));
for (auto const & e : startLines)
LOG(LDEBUG, (LogAs2GisPath(e.m_edge)));
auto const startPoint = MercatorBounds::FromLatLon(p.m_latLon);
vector<shared_ptr<Link>> allPaths;
GetAllSuitablePaths(startLines, isLastPoint, bearDistM, p.m_functionalRoadClass, p.m_formOfWay,
allPaths);
GetBestCandidatePaths(allPaths, isLastPoint, p.m_bearing, bearDistM, startPoint, candidates);
// Sorting by increasing order.
sort(candidates.begin(), candidates.end(),
[](ScorePath const & s1, ScorePath const & s2) { return s1.m_score > s2.m_score; });
LOG(LDEBUG, (candidates.size(), "Candidate paths found for point:", p.m_latLon));
}
} // namespace openlr

View file

@ -0,0 +1,115 @@
#pragma once
#include "openlr/graph.hpp"
#include "openlr/openlr_model.hpp"
#include "openlr/road_info_getter.hpp"
#include "openlr/score_types.hpp"
#include "openlr/stats.hpp"
#include "geometry/point2d.hpp"
#include "base/assert.hpp"
#include <cstdint>
#include <limits>
#include <memory>
#include <vector>
namespace openlr
{
class ScoreCandidatePointsGetter;
class ScoreCandidatePathsGetter
{
public:
ScoreCandidatePathsGetter(ScoreCandidatePointsGetter & pointsGetter, Graph & graph,
RoadInfoGetter & infoGetter, v2::Stats & stat)
: m_pointsGetter(pointsGetter), m_graph(graph), m_infoGetter(infoGetter), m_stats(stat)
{
}
bool GetLineCandidatesForPoints(std::vector<LocationReferencePoint> const & points,
std::vector<ScorePathVec> & lineCandidates);
private:
struct Link
{
Link(std::shared_ptr<Link> const & parent, Graph::Edge const & edge, double distanceM,
Score pointScore, Score rfcScore)
: m_parent(parent)
, m_edge(edge)
, m_distanceM(distanceM)
, m_pointScore(pointScore)
, m_minRoadScore(rfcScore)
{
CHECK(!edge.IsFake(), ("Edge should not be fake:", edge));
}
bool IsJunctionInPath(routing::Junction const & j) const;
Graph::Edge GetStartEdge() const;
std::shared_ptr<Link> const m_parent;
Graph::Edge const m_edge;
double const m_distanceM;
Score const m_pointScore;
// Minimum score of segments of the path going along |m_parent| based on functional road class
// and form of way.
Score const m_minRoadScore;
};
struct CandidatePath
{
CandidatePath() = default;
CandidatePath(std::shared_ptr<Link> const path, Score pointScore, Score rfcScore, Score bearingScore)
: m_path(path)
, m_pointScore(pointScore)
, m_roadScore(rfcScore)
, m_bearingScore(bearingScore)
{
}
bool operator>(CandidatePath const & o) const { return GetScore() > o.GetScore(); }
Score GetScore() const { return m_pointScore + m_roadScore + m_bearingScore; }
std::shared_ptr<Link> m_path = nullptr;
Score m_pointScore = 0;
Score m_roadScore = 0;
Score m_bearingScore = 0;
};
// Note: In all methods below if |isLastPoint| is true than algorithm should
// calculate all parameters (such as bearing, distance to next point, etc.)
// relative to the last point.
// o ----> o ----> o <---- o.
// 1 2 3 4
// ^ isLastPoint = true.
// To calculate bearing for points 1 to 3 one have to go beardist from
// previous point to the next one (eg. from 1 to 2 and from 2 to 3).
// For point 4 one have to go from 4 to 3 reversing directions. And
// distance-to-next point is taken from point 3. You can learn more in
// TomTom OpenLR spec.
/// \brief Fills |allPaths| with paths near start or finish point starting from |startLines|.
/// To extract a path from |allPaths| a item from |allPaths| should be taken,
/// then should be taken the member |m_parent| of the item and so on till the beginning.
void GetAllSuitablePaths(ScoreEdgeVec const & startLines, bool isLastPoint,
double bearDistM, FunctionalRoadClass functionalRoadClass,
FormOfWay formOfWay, std::vector<std::shared_ptr<Link>> & allPaths);
void GetBestCandidatePaths(std::vector<std::shared_ptr<Link>> const & allPaths, bool isLastPoint,
uint32_t requiredBearing, double bearDistM,
m2::PointD const & startPoint, ScorePathVec & candidates);
void GetLineCandidates(openlr::LocationReferencePoint const & p, bool isLastPoint,
double distanceToNextPointM, ScoreEdgeVec const & edgeCandidates,
ScorePathVec & candidates);
ScoreCandidatePointsGetter & m_pointsGetter;
Graph & m_graph;
RoadInfoGetter & m_infoGetter;
v2::Stats & m_stats;
};
} // namespace openlr

View file

@ -0,0 +1,135 @@
#include "openlr/score_candidate_points_getter.hpp"
#include "openlr/helpers.hpp"
#include "routing/road_graph.hpp"
#include "routing/routing_helpers.hpp"
#include "storage/country_info_getter.hpp"
#include "indexer/feature.hpp"
#include "indexer/scales.hpp"
#include "geometry/mercator.hpp"
#include "base/assert.hpp"
#include "base/stl_helpers.hpp"
#include <algorithm>
#include <set>
#include <utility>
using namespace routing;
namespace
{
// Ends of segments and intermediate points of segments are considered only within this radius.
double const kRadius = 30.0;
} // namespace
namespace openlr
{
void ScoreCandidatePointsGetter::GetJunctionPointCandidates(m2::PointD const & p, bool isLastPoint,
ScoreEdgeVec & edgeCandidates)
{
ScorePointVec pointCandidates;
auto const selectCandidates = [&p, &pointCandidates, this](FeatureType & ft) {
ft.ParseGeometry(FeatureType::BEST_GEOMETRY);
ft.ForEachPoint(
[&p, &pointCandidates, this](m2::PointD const & candidate) {
if (MercatorBounds::DistanceOnEarth(p, candidate) < kRadius)
pointCandidates.emplace_back(GetScoreByDistance(p, candidate), candidate);
},
scales::GetUpperScale());
};
m_dataSource.ForEachInRect(selectCandidates,
MercatorBounds::RectByCenterXYAndSizeInMeters(p, kRadius),
scales::GetUpperScale());
base::SortUnique(pointCandidates);
std::reverse(pointCandidates.begin(), pointCandidates.end());
pointCandidates.resize(min(m_maxJunctionCandidates, pointCandidates.size()));
for (auto const & pc : pointCandidates)
{
Graph::EdgeVector edges;
if (!isLastPoint)
m_graph.GetOutgoingEdges(Junction(pc.m_point, 0 /* altitude */), edges);
else
m_graph.GetIngoingEdges(Junction(pc.m_point, 0 /* altitude */), edges);
for (auto const & e : edges)
edgeCandidates.emplace_back(pc.m_score, e);
}
}
void ScoreCandidatePointsGetter::EnrichWithProjectionPoints(m2::PointD const & p,
ScoreEdgeVec & edgeCandidates)
{
m_graph.ResetFakes();
vector<pair<Graph::Edge, Junction>> vicinities;
m_graph.FindClosestEdges(p, static_cast<uint32_t>(m_maxProjectionCandidates), vicinities);
for (auto const & v : vicinities)
{
auto const & edge = v.first;
auto const & proj = v.second;
CHECK(edge.HasRealPart(), ());
CHECK(!edge.IsFake(), ());
if (MercatorBounds::DistanceOnEarth(p, proj.GetPoint()) >= kRadius)
continue;
edgeCandidates.emplace_back(GetScoreByDistance(p, proj.GetPoint()), edge);
}
}
bool ScoreCandidatePointsGetter::IsJunction(m2::PointD const & p)
{
Graph::EdgeVector outgoing;
m_graph.GetRegularOutgoingEdges(Junction(p, 0 /* altitude */), outgoing);
Graph::EdgeVector ingoing;
m_graph.GetRegularIngoingEdges(Junction(p, 0 /* altitude */), ingoing);
// Note. At mwm borders the size of |ids| may be bigger than two in case of straight
// road because of road feature duplication at borders.
std::set<std::pair<uint32_t, uint32_t>> ids;
for (auto const & e : outgoing)
ids.insert(std::make_pair(e.GetFeatureId().m_index, e.GetSegId()));
for (auto const & e : ingoing)
ids.insert(std::make_pair(e.GetFeatureId().m_index, e.GetSegId()));
// Size of |ids| is number of different pairs of (feature id, segment id) starting from
// |p| plus going to |p|. The size 0, 1 or 2 is considered |p| is not a junction of roads.
// If the size is 3 or more it means |p| is an intersection of 3 or more roads.
return ids.size() >= 3;
}
Score ScoreCandidatePointsGetter::GetScoreByDistance(m2::PointD const & point,
m2::PointD const & candidate)
{
// Maximum possible score for the distance between an openlr segment ends and an osm segments.
Score constexpr kMaxScoreForDist = 70;
// If the distance between an openlr segments end and an osm segments end is less or equal
// |kMaxScoreDistM| the point gets |kMaxScoreForDist| score.
double constexpr kMaxScoreDistM = 5.0;
// According to the standard openlr edge should be started from a junction. Despite the fact
// that openlr and osm are based on different graphs, the score of junction should be increased.
double const junctionFactor = IsJunction(candidate) ? 1.1 : 1.0;
double const distM = MercatorBounds::DistanceOnEarth(point, candidate);
double const score =
distM <= kMaxScoreDistM
? kMaxScoreForDist * junctionFactor
: static_cast<double>(kMaxScoreForDist) * junctionFactor / (1.0 + distM - kMaxScoreDistM);
CHECK_GREATER_OR_EQUAL(score, 0.0, ());
CHECK_LESS_OR_EQUAL(score, static_cast<double>(kMaxScoreForDist) * junctionFactor, ());
return static_cast<Score>(score);
}
} // namespace openlr

View file

@ -0,0 +1,50 @@
#pragma once
#include "openlr/graph.hpp"
#include "openlr/score_types.hpp"
#include "openlr/stats.hpp"
#include "indexer/data_source.hpp"
#include "geometry/point2d.hpp"
#include <cstdint>
#include <functional>
#include <vector>
namespace openlr
{
class ScoreCandidatePointsGetter
{
public:
ScoreCandidatePointsGetter(size_t maxJunctionCandidates, size_t maxProjectionCandidates,
DataSource const & dataSource, Graph & graph)
: m_maxJunctionCandidates(maxJunctionCandidates)
, m_maxProjectionCandidates(maxProjectionCandidates)
, m_dataSource(dataSource)
, m_graph(graph)
{
}
void GetEdgeCandidates(m2::PointD const & p, bool isLastPoint, ScoreEdgeVec & edges)
{
GetJunctionPointCandidates(p, isLastPoint, edges);
EnrichWithProjectionPoints(p, edges);
}
private:
void GetJunctionPointCandidates(m2::PointD const & p, bool isLastPoint,
ScoreEdgeVec & edgeCandidates);
void EnrichWithProjectionPoints(m2::PointD const & p, ScoreEdgeVec & edgeCandidates);
/// \returns true if |p| is a junction and false otherwise.
bool IsJunction(m2::PointD const & p);
Score GetScoreByDistance(m2::PointD const & point, m2::PointD const & candidate);
size_t const m_maxJunctionCandidates;
size_t const m_maxProjectionCandidates;
DataSource const & m_dataSource;
Graph & m_graph;
};
} // namespace openlr

View file

@ -0,0 +1,341 @@
#include "openlr/score_paths_connector.hpp"
#include "openlr/helpers.hpp"
#include "indexer/feature_data.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "base/assert.hpp"
#include "base/checked_cast.hpp"
#include "base/stl_helpers.hpp"
#include "base/stl_iterator.hpp"
#include <algorithm>
#include <cmath>
#include <map>
#include <string>
#include <queue>
#include <tuple>
using namespace std;
namespace openlr
{
namespace
{
class EdgeContainer
{
public:
explicit EdgeContainer(Graph const & graph) : m_graph(graph) {}
void ProcessEdge(Graph::Edge const & edge)
{
CHECK(!edge.IsFake(), ());
feature::TypesHolder types;
m_graph.GetFeatureTypes(edge.GetFeatureId(), types);
ftypes::HighwayClass const hwClass = ftypes::GetHighwayClass(types);
if (m_minHwClass == ftypes::HighwayClass::Undefined)
{
m_minHwClass = hwClass;
m_maxHwClass = m_minHwClass;
m_oneWay.m_field = ftypes::IsOneWayChecker::Instance()(types);
m_roundabout.m_field = ftypes::IsRoundAboutChecker::Instance()(types);
m_link.m_field = ftypes::IsLinkChecker::Instance()(types);
}
else
{
m_minHwClass = static_cast<ftypes::HighwayClass>(
min(base::Underlying(m_minHwClass), base::Underlying(hwClass)));
m_maxHwClass = static_cast<ftypes::HighwayClass>(
max(base::Underlying(m_maxHwClass), base::Underlying(hwClass)));
if (m_oneWay.m_allEqual && m_oneWay.m_field != ftypes::IsOneWayChecker::Instance()(types))
m_oneWay.m_allEqual = false;
if (m_roundabout.m_allEqual && m_roundabout.m_field != ftypes::IsRoundAboutChecker::Instance()(types))
m_roundabout.m_allEqual = false;
if (m_link.m_allEqual && m_link.m_field != ftypes::IsLinkChecker::Instance()(types))
m_link.m_allEqual = false;
}
}
uint8_t GetHighwayClassDiff() const
{
CHECK_NOT_EQUAL(m_minHwClass, ftypes::HighwayClass::Undefined, ());
CHECK_GREATER_OR_EQUAL(m_maxHwClass, m_minHwClass, ());
uint8_t const hwClassDiff = static_cast<uint8_t>(m_maxHwClass) - static_cast<uint8_t>(m_minHwClass);
return hwClassDiff;
}
bool AreAllOneWaysEqual() const { return m_oneWay.m_allEqual; }
bool AreAllRoundaboutEqual() const { return m_roundabout.m_allEqual; }
bool AreAllLinksEqual() const { return m_link.m_allEqual; }
private:
struct Field
{
bool m_field = false;
bool m_allEqual = true;
};
Graph const & m_graph;
ftypes::HighwayClass m_minHwClass = ftypes::HighwayClass::Undefined;
ftypes::HighwayClass m_maxHwClass = ftypes::HighwayClass::Undefined;
Field m_oneWay;
Field m_roundabout;
Field m_link;
};
/// \returns true if |path| may be used as a candidate. In that case |lenScore| is filled
/// with score of this candidate based on length. The closer length of the |path| to
/// |distanceToNextPoint| the more score.
bool ValidatePathByLength(Graph::EdgeVector const & path, double distanceToNextPoint,
Score & lenScore)
{
CHECK(!path.empty(), ());
Score const kMaxScoreForRouteLen = 110;
double pathLen = 0.0;
for (auto const & e : path)
pathLen += EdgeLength(e);
// 0 <= |pathDiffRatio| <= 1. The more pathDiffRatio the closer |distanceToNextPoint| and |pathLen|.
double const pathDiffRatio =
1.0 - abs(distanceToNextPoint - pathLen) / max(distanceToNextPoint, pathLen);
bool const shortPath = path.size() <= 2;
double constexpr kMinValidPathDiffRation = 0.6;
if (pathDiffRatio <= kMinValidPathDiffRation && !shortPath)
return false;
lenScore = static_cast<Score>(kMaxScoreForRouteLen * (pathDiffRatio - kMinValidPathDiffRation) /
(1.0 - kMinValidPathDiffRation));
return true;
}
} // namespace
ScorePathsConnector::ScorePathsConnector(Graph & graph, RoadInfoGetter & infoGetter, v2::Stats & stat)
: m_graph(graph), m_infoGetter(infoGetter), m_stat(stat)
{
}
bool ScorePathsConnector::FindBestPath(vector<LocationReferencePoint> const & points,
vector<vector<ScorePath>> const & lineCandidates,
vector<Graph::EdgeVector> & resultPath)
{
CHECK_GREATER_OR_EQUAL(points.size(), 2, ());
resultPath.resize(points.size() - 1);
for (size_t i = 1; i < points.size(); ++i)
{
// @TODO It's possible that size of |points| is more then two. In that case two or more edges
// should be approximated with routes. If so only |toCandidates| which may be reached from
// |fromCandidates| should be used for the next edge.
auto const & point = points[i - 1];
auto const distanceToNextPoint = static_cast<double>(point.m_distanceToNextPoint);
auto const & fromCandidates = lineCandidates[i - 1];
auto const & toCandidates = lineCandidates[i];
auto & resultPathPart = resultPath[i - 1];
vector<ScorePath> result;
for (size_t fromInd = 0; fromInd < fromCandidates.size(); ++fromInd)
{
for (size_t toInd = 0; toInd < toCandidates.size(); ++toInd)
{
Graph::EdgeVector path;
if (!ConnectAdjacentCandidateLines(fromCandidates[fromInd].m_path,
toCandidates[toInd].m_path, point.m_lfrcnp,
distanceToNextPoint, path))
{
continue;
}
Score pathLenScore = 0;
if (!ValidatePathByLength(path, distanceToNextPoint, pathLenScore))
continue;
auto const score = pathLenScore + GetScoreForUniformity(path) +
fromCandidates[fromInd].m_score + toCandidates[toInd].m_score;
result.emplace_back(score, move(path));
}
}
for (auto const & p : result)
CHECK(!p.m_path.empty(), ());
if (result.empty())
{
LOG(LINFO, ("No shortest path found"));
++m_stat.m_noShortestPathFound;
resultPathPart.clear();
return false;
}
auto const it = max_element(
result.cbegin(), result.cend(),
[](ScorePath const & o1, ScorePath const & o2) { return o1.m_score < o2.m_score; });
Score constexpr kMinValidScore = 240;
if (it->m_score < kMinValidScore)
{
LOG(LINFO, ("The shortest path found but it is no good."));
return false;
}
resultPathPart = it->m_path;
LOG(LDEBUG, ("Best score:", it->m_score, "resultPathPart.size():", resultPathPart.size()));
}
CHECK_EQUAL(resultPath.size(), points.size() - 1, ());
return true;
}
bool ScorePathsConnector::FindShortestPath(Graph::Edge const & from, Graph::Edge const & to,
FunctionalRoadClass lowestFrcToNextPoint,
uint32_t maxPathLength, Graph::EdgeVector & path)
{
double constexpr kLengthToleranceFactor = 1.1;
uint32_t constexpr kMinLengthTolerance = 20;
uint32_t const lengthToleranceM =
max(static_cast<uint32_t>(kLengthToleranceFactor * maxPathLength), kMinLengthTolerance);
struct State
{
State(Graph::Edge const & e, uint32_t const s) : m_edge(e), m_score(s) {}
bool operator>(State const & o) const
{
return tie(m_score, m_edge) > tie(o.m_score, o.m_edge);
}
Graph::Edge m_edge;
uint32_t m_score;
};
CHECK(from.HasRealPart() && to.HasRealPart(), ());
priority_queue<State, vector<State>, greater<>> q;
map<Graph::Edge, double> scores;
map<Graph::Edge, Graph::Edge> links;
q.emplace(from, 0);
scores[from] = 0;
while (!q.empty())
{
auto const state = q.top();
q.pop();
auto const & stateEdge = state.m_edge;
// TODO(mgsergio): Unify names: use either score or distance.
auto const stateScore = state.m_score;
if (stateScore > maxPathLength + lengthToleranceM)
continue;
if (stateScore > scores[stateEdge])
continue;
if (stateEdge == to)
{
for (auto edge = stateEdge; edge != from; edge = links[edge])
path.emplace_back(edge);
path.emplace_back(from);
reverse(begin(path), end(path));
return true;
}
Graph::EdgeVector edges;
m_graph.GetOutgoingEdges(stateEdge.GetEndJunction(), edges);
for (auto const & edge : edges)
{
if (!ConformLfrcnpV3(edge, lowestFrcToNextPoint, m_infoGetter))
continue;
CHECK(!stateEdge.IsFake(), ());
CHECK(!edge.IsFake(), ());
auto const it = scores.find(edge);
auto const eScore = stateScore + EdgeLength(edge);
if (it == end(scores) || it->second > eScore)
{
scores[edge] = eScore;
links[edge] = stateEdge;
q.emplace(edge, eScore);
}
}
}
return false;
}
bool ScorePathsConnector::ConnectAdjacentCandidateLines(Graph::EdgeVector const & from,
Graph::EdgeVector const & to,
FunctionalRoadClass lowestFrcToNextPoint,
double distanceToNextPoint,
Graph::EdgeVector & resultPath)
{
CHECK(!to.empty(), ());
if (auto const skip = PathOverlappingLen(from, to))
{
if (skip == -1)
return false;
resultPath.insert(resultPath.end(), from.cbegin(), from.cend());
resultPath.insert(resultPath.end(), to.cbegin() + skip, to.cend());
return true;
}
CHECK_NOT_EQUAL(from, to, ());
Graph::EdgeVector shortestPath;
auto const found =
FindShortestPath(from.back(), to.front(), lowestFrcToNextPoint,
static_cast<uint32_t>(ceil(distanceToNextPoint)), shortestPath);
if (!found)
return false;
CHECK_EQUAL(from.back(), shortestPath.front(), ());
resultPath.insert(resultPath.end(), from.cbegin(), prev(from.cend()));
resultPath.insert(resultPath.end(), shortestPath.cbegin(), shortestPath.cend());
CHECK_EQUAL(to.front(), shortestPath.back(), ());
resultPath.insert(resultPath.end(), next(to.begin()), to.end());
return !resultPath.empty();
}
Score ScorePathsConnector::GetScoreForUniformity(Graph::EdgeVector const & path)
{
EdgeContainer edgeContainer(m_graph);
for (auto const & edge : path)
edgeContainer.ProcessEdge(edge);
auto const hwClassDiff = edgeContainer.GetHighwayClassDiff();
Score constexpr kScoreForTheSameHwClass = 40;
Score constexpr kScoreForNeighboringHwClasses = 15;
Score const hwClassScore = hwClassDiff == 0
? kScoreForTheSameHwClass
: hwClassDiff == 1 ? kScoreForNeighboringHwClasses : 0;
Score constexpr kScoreForOneWayOnly = 17;
Score constexpr kScoreForRoundaboutOnly = 18;
Score constexpr kScoreForLinkOnly = 10;
Score const allEqualScore =
(edgeContainer.AreAllOneWaysEqual() ? kScoreForOneWayOnly : 0) +
(edgeContainer.AreAllRoundaboutEqual() ? kScoreForRoundaboutOnly : 0) +
(edgeContainer.AreAllLinksEqual() ? kScoreForLinkOnly : 0);
return hwClassScore + allEqualScore;
}
} // namespace openlr

View file

@ -0,0 +1,42 @@
#pragma once
#include "openlr/graph.hpp"
#include "openlr/openlr_model.hpp"
#include "openlr/road_info_getter.hpp"
#include "openlr/score_types.hpp"
#include "openlr/stats.hpp"
#include <cstddef>
#include <cstdint>
#include <vector>
namespace openlr
{
class ScorePathsConnector
{
public:
ScorePathsConnector(Graph & graph, RoadInfoGetter & infoGetter, v2::Stats & stat);
/// \brief Connects |lineCandidates| and fills |resultPath| with the path with maximum score
/// if there's a good enough.
/// \returns true if the best path is found and false otherwise.
bool FindBestPath(std::vector<LocationReferencePoint> const & points,
std::vector<std::vector<ScorePath>> const & lineCandidates,
std::vector<Graph::EdgeVector> & resultPath);
private:
bool FindShortestPath(Graph::Edge const & from, Graph::Edge const & to,
FunctionalRoadClass lowestFrcToNextPoint, uint32_t maxPathLength,
Graph::EdgeVector & path);
bool ConnectAdjacentCandidateLines(Graph::EdgeVector const & from, Graph::EdgeVector const & to,
FunctionalRoadClass lowestFrcToNextPoint,
double distanceToNextPoint, Graph::EdgeVector & resultPath);
Score GetScoreForUniformity(Graph::EdgeVector const & path);
Graph & m_graph;
RoadInfoGetter & m_infoGetter;
v2::Stats & m_stat;
};
} // namespace openlr

58
openlr/score_types.hpp Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include "routing/road_graph.hpp"
#include "geometry/point2d.hpp"
#include <cstdint>
#include <tuple>
#include <vector>
namespace openlr
{
using Edge = routing::Edge;
using EdgeVector = routing::RoadGraphBase::TEdgeVector;
using Score = uint32_t;
struct ScorePoint
{
ScorePoint() = default;
ScorePoint(Score score, m2::PointD const & point) : m_score(score), m_point(point) {}
bool operator<(ScorePoint const & o) const
{
return std::tie(m_score, m_point) < std::tie(o.m_score, o.m_point);
}
bool operator==(ScorePoint const & o) const
{
return m_score == o.m_score && m_point == o.m_point;
}
Score m_score = 0;
m2::PointD m_point;
};
using ScorePointVec = std::vector<ScorePoint>;
struct ScoreEdge
{
ScoreEdge(Score score, Edge const & edge) : m_score(score), m_edge(edge) {}
Score m_score;
Edge m_edge;
};
using ScoreEdgeVec = std::vector<ScoreEdge>;
struct ScorePath
{
ScorePath(Score score, EdgeVector && path) : m_score(score), m_path(move(path)) {}
Score m_score;
EdgeVector m_path;
};
using ScorePathVec = std::vector<ScorePath>;
} // namespace openlr

View file

@ -20,7 +20,7 @@ struct alignas(kCacheLineSize) Stats
m_noCandidateFound += s.m_noCandidateFound;
m_noShortestPathFound += s.m_noShortestPathFound;
m_wrongOffsets += s.m_wrongOffsets;
m_dnpIsZero += s.m_dnpIsZero;
m_zeroDistToNextPointCount += s.m_zeroDistToNextPointCount;
}
void Report() const
@ -28,7 +28,7 @@ struct alignas(kCacheLineSize) Stats
LOG(LINFO, ("Total routes handled:", m_routesHandled));
LOG(LINFO, ("Failed:", m_routesFailed));
LOG(LINFO, ("No candidate lines:", m_noCandidateFound));
LOG(LINFO, ("Wrong distance to next point:", m_dnpIsZero));
LOG(LINFO, ("Wrong distance to next point:", m_zeroDistToNextPointCount));
LOG(LINFO, ("Wrong offsets:", m_wrongOffsets));
LOG(LINFO, ("No shortest path:", m_noShortestPathFound));
}
@ -39,7 +39,7 @@ struct alignas(kCacheLineSize) Stats
uint32_t m_noShortestPathFound = 0;
uint32_t m_wrongOffsets = 0;
// Number of zeroed distance-to-next point values in the input.
uint32_t m_dnpIsZero = 0;
uint32_t m_zeroDistToNextPointCount = 0;
};
} // namespace V2
} // namespace openlr

View file

@ -15,6 +15,7 @@
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

View file

@ -7,6 +7,25 @@
objects = {
/* Begin PBXBuildFile section */
56B1DDC2225E04A100F88086 /* graph.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDAF225E04A000F88086 /* graph.hpp */; };
56B1DDC3225E04A100F88086 /* score_types.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDB0225E04A000F88086 /* score_types.hpp */; };
56B1DDC4225E04A100F88086 /* graph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDB1225E04A000F88086 /* graph.cpp */; };
56B1DDC5225E04A100F88086 /* candidate_paths_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDB2225E04A000F88086 /* candidate_paths_getter.cpp */; };
56B1DDC6225E04A100F88086 /* paths_connector.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDB3225E04A000F88086 /* paths_connector.hpp */; };
56B1DDC7225E04A100F88086 /* score_candidate_paths_getter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDB4225E04A000F88086 /* score_candidate_paths_getter.hpp */; };
56B1DDC8225E04A100F88086 /* candidate_points_getter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDB5225E04A100F88086 /* candidate_points_getter.hpp */; };
56B1DDC9225E04A100F88086 /* candidate_paths_getter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDB6225E04A100F88086 /* candidate_paths_getter.hpp */; };
56B1DDCA225E04A100F88086 /* candidate_points_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDB7225E04A100F88086 /* candidate_points_getter.cpp */; };
56B1DDCB225E04A100F88086 /* paths_connector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDB8225E04A100F88086 /* paths_connector.cpp */; };
56B1DDCC225E04A100F88086 /* helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDB9225E04A100F88086 /* helpers.cpp */; };
56B1DDCD225E04A100F88086 /* score_candidate_points_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDBA225E04A100F88086 /* score_candidate_points_getter.cpp */; };
56B1DDCE225E04A100F88086 /* score_candidate_points_getter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDBB225E04A100F88086 /* score_candidate_points_getter.hpp */; };
56B1DDCF225E04A100F88086 /* score_paths_connector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDBC225E04A100F88086 /* score_paths_connector.cpp */; };
56B1DDD0225E04A100F88086 /* score_paths_connector.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDBD225E04A100F88086 /* score_paths_connector.hpp */; };
56B1DDD1225E04A100F88086 /* cache_line_size.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDBE225E04A100F88086 /* cache_line_size.hpp */; };
56B1DDD2225E04A100F88086 /* stats.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDBF225E04A100F88086 /* stats.hpp */; };
56B1DDD3225E04A100F88086 /* score_candidate_paths_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1DDC0225E04A100F88086 /* score_candidate_paths_getter.cpp */; };
56B1DDD4225E04A100F88086 /* helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1DDC1225E04A100F88086 /* helpers.hpp */; };
671E79101E6A502200B2859B /* openlr_model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 671E79011E6A502200B2859B /* openlr_model.cpp */; };
671E79111E6A502200B2859B /* openlr_model.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 671E79021E6A502200B2859B /* openlr_model.hpp */; };
671E79181E6A502200B2859B /* road_info_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 671E79091E6A502200B2859B /* road_info_getter.cpp */; };
@ -23,6 +42,25 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
56B1DDAF225E04A000F88086 /* graph.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = graph.hpp; sourceTree = "<group>"; };
56B1DDB0225E04A000F88086 /* score_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = score_types.hpp; sourceTree = "<group>"; };
56B1DDB1225E04A000F88086 /* graph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = graph.cpp; sourceTree = "<group>"; };
56B1DDB2225E04A000F88086 /* candidate_paths_getter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = candidate_paths_getter.cpp; sourceTree = "<group>"; };
56B1DDB3225E04A000F88086 /* paths_connector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = paths_connector.hpp; sourceTree = "<group>"; };
56B1DDB4225E04A000F88086 /* score_candidate_paths_getter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = score_candidate_paths_getter.hpp; sourceTree = "<group>"; };
56B1DDB5225E04A100F88086 /* candidate_points_getter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = candidate_points_getter.hpp; sourceTree = "<group>"; };
56B1DDB6225E04A100F88086 /* candidate_paths_getter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = candidate_paths_getter.hpp; sourceTree = "<group>"; };
56B1DDB7225E04A100F88086 /* candidate_points_getter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = candidate_points_getter.cpp; sourceTree = "<group>"; };
56B1DDB8225E04A100F88086 /* paths_connector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = paths_connector.cpp; sourceTree = "<group>"; };
56B1DDB9225E04A100F88086 /* helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = helpers.cpp; sourceTree = "<group>"; };
56B1DDBA225E04A100F88086 /* score_candidate_points_getter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = score_candidate_points_getter.cpp; sourceTree = "<group>"; };
56B1DDBB225E04A100F88086 /* score_candidate_points_getter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = score_candidate_points_getter.hpp; sourceTree = "<group>"; };
56B1DDBC225E04A100F88086 /* score_paths_connector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = score_paths_connector.cpp; sourceTree = "<group>"; };
56B1DDBD225E04A100F88086 /* score_paths_connector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = score_paths_connector.hpp; sourceTree = "<group>"; };
56B1DDBE225E04A100F88086 /* cache_line_size.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cache_line_size.hpp; sourceTree = "<group>"; };
56B1DDBF225E04A100F88086 /* stats.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stats.hpp; sourceTree = "<group>"; };
56B1DDC0225E04A100F88086 /* score_candidate_paths_getter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = score_candidate_paths_getter.cpp; sourceTree = "<group>"; };
56B1DDC1225E04A100F88086 /* helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = helpers.hpp; sourceTree = "<group>"; };
671E78F31E6A4FE400B2859B /* libopenlr.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopenlr.a; sourceTree = BUILT_PRODUCTS_DIR; };
671E79011E6A502200B2859B /* openlr_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = openlr_model.cpp; sourceTree = "<group>"; };
671E79021E6A502200B2859B /* openlr_model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = openlr_model.hpp; sourceTree = "<group>"; };
@ -73,6 +111,25 @@
671E78F51E6A4FE400B2859B /* openlr */ = {
isa = PBXGroup;
children = (
56B1DDBE225E04A100F88086 /* cache_line_size.hpp */,
56B1DDB2225E04A000F88086 /* candidate_paths_getter.cpp */,
56B1DDB6225E04A100F88086 /* candidate_paths_getter.hpp */,
56B1DDB7225E04A100F88086 /* candidate_points_getter.cpp */,
56B1DDB5225E04A100F88086 /* candidate_points_getter.hpp */,
56B1DDB1225E04A000F88086 /* graph.cpp */,
56B1DDAF225E04A000F88086 /* graph.hpp */,
56B1DDB9225E04A100F88086 /* helpers.cpp */,
56B1DDC1225E04A100F88086 /* helpers.hpp */,
56B1DDB8225E04A100F88086 /* paths_connector.cpp */,
56B1DDB3225E04A000F88086 /* paths_connector.hpp */,
56B1DDC0225E04A100F88086 /* score_candidate_paths_getter.cpp */,
56B1DDB4225E04A000F88086 /* score_candidate_paths_getter.hpp */,
56B1DDBA225E04A100F88086 /* score_candidate_points_getter.cpp */,
56B1DDBB225E04A100F88086 /* score_candidate_points_getter.hpp */,
56B1DDBC225E04A100F88086 /* score_paths_connector.cpp */,
56B1DDBD225E04A100F88086 /* score_paths_connector.hpp */,
56B1DDB0225E04A000F88086 /* score_types.hpp */,
56B1DDBF225E04A100F88086 /* stats.hpp */,
E92EE07E1F98E8EB00B57D20 /* decoded_path.cpp */,
E92EE07D1F98E8EB00B57D20 /* decoded_path.hpp */,
E92EE0811F98E8EC00B57D20 /* openlr_decoder.cpp */,
@ -99,12 +156,23 @@
buildActionMask = 2147483647;
files = (
671E791D1E6A502200B2859B /* router.hpp in Headers */,
56B1DDC6225E04A100F88086 /* paths_connector.hpp in Headers */,
56B1DDC7225E04A100F88086 /* score_candidate_paths_getter.hpp in Headers */,
56B1DDC3225E04A100F88086 /* score_types.hpp in Headers */,
56B1DDC2225E04A100F88086 /* graph.hpp in Headers */,
E92EE0821F98E8EC00B57D20 /* openlr_model_xml.hpp in Headers */,
671E79111E6A502200B2859B /* openlr_model.hpp in Headers */,
671E79191E6A502200B2859B /* road_info_getter.hpp in Headers */,
56B1DDD2225E04A100F88086 /* stats.hpp in Headers */,
56B1DDD0225E04A100F88086 /* score_paths_connector.hpp in Headers */,
56B1DDD4225E04A100F88086 /* helpers.hpp in Headers */,
56B1DDC8225E04A100F88086 /* candidate_points_getter.hpp in Headers */,
E92EE0851F98E8EC00B57D20 /* openlr_decoder.hpp in Headers */,
E92EE0831F98E8EC00B57D20 /* decoded_path.hpp in Headers */,
56B1DDC9225E04A100F88086 /* candidate_paths_getter.hpp in Headers */,
671E791E1E6A502200B2859B /* way_point.hpp in Headers */,
56B1DDD1225E04A100F88086 /* cache_line_size.hpp in Headers */,
56B1DDCE225E04A100F88086 /* score_candidate_points_getter.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -148,6 +216,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = 671E78EA1E6A4FE400B2859B;
@ -165,12 +234,20 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
56B1DDCF225E04A100F88086 /* score_paths_connector.cpp in Sources */,
56B1DDCD225E04A100F88086 /* score_candidate_points_getter.cpp in Sources */,
56B1DDD3225E04A100F88086 /* score_candidate_paths_getter.cpp in Sources */,
671E79101E6A502200B2859B /* openlr_model.cpp in Sources */,
56B1DDCC225E04A100F88086 /* helpers.cpp in Sources */,
56B1DDC5225E04A100F88086 /* candidate_paths_getter.cpp in Sources */,
56B1DDCA225E04A100F88086 /* candidate_points_getter.cpp in Sources */,
E92EE0861F98E8EC00B57D20 /* openlr_model_xml.cpp in Sources */,
E92EE0841F98E8EC00B57D20 /* decoded_path.cpp in Sources */,
671E79181E6A502200B2859B /* road_info_getter.cpp in Sources */,
56B1DDCB225E04A100F88086 /* paths_connector.cpp in Sources */,
671E791C1E6A502200B2859B /* router.cpp in Sources */,
E92EE0871F98E8EC00B57D20 /* openlr_decoder.cpp in Sources */,
56B1DDC4225E04A100F88086 /* graph.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};