forked from organicmaps/organicmaps
Merge pull request #5508 from bykoianko/master-ingoing-wave
Backward wave in CrossMwmGraph
This commit is contained in:
commit
87d8637509
11 changed files with 435 additions and 122 deletions
|
@ -35,6 +35,8 @@ set(
|
|||
newtype.hpp
|
||||
normalize_unicode.cpp
|
||||
observer_list.hpp
|
||||
random.cpp
|
||||
random.hpp
|
||||
range_iterator.hpp
|
||||
ref_counted.hpp
|
||||
rolling_hash.hpp
|
||||
|
|
|
@ -18,6 +18,7 @@ SOURCES += \
|
|||
logging.cpp \
|
||||
lower_case.cpp \
|
||||
normalize_unicode.cpp \
|
||||
random.cpp \
|
||||
shared_buffer_manager.cpp \
|
||||
src_point.cpp \
|
||||
string_format.cpp \
|
||||
|
@ -59,6 +60,7 @@ HEADERS += \
|
|||
mutex.hpp \
|
||||
newtype.hpp \
|
||||
observer_list.hpp \
|
||||
random.hpp \
|
||||
range_iterator.hpp \
|
||||
ref_counted.hpp \
|
||||
regexp.hpp \
|
||||
|
|
22
base/random.cpp
Normal file
22
base/random.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "base/random.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace base
|
||||
{
|
||||
std::vector<size_t> RandomSample(size_t n, size_t k, std::minstd_rand & rng)
|
||||
{
|
||||
std::vector<size_t> result(std::min(k, n));
|
||||
std::iota(result.begin(), result.end(), 0);
|
||||
|
||||
for (size_t i = k; i < n; ++i)
|
||||
{
|
||||
size_t const j = rng() % (i + 1);
|
||||
if (j < k)
|
||||
result[j] = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // base
|
10
base/random.hpp
Normal file
10
base/random.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Selects a fair random subset of size min(|n|, |k|) from [0, 1, 2, ..., n - 1].
|
||||
std::vector<size_t> RandomSample(size_t n, size_t k, std::minstd_rand & rng);
|
||||
} // base
|
|
@ -3,11 +3,134 @@
|
|||
|
||||
#include "geometry/distance_on_sphere.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace routing;
|
||||
|
||||
inline bool IsValidEdgeWeight(EdgeWeight const & w) { return w != INVALID_EDGE_WEIGHT; }
|
||||
template <class Node>
|
||||
class ClosestNodeFinder
|
||||
{
|
||||
public:
|
||||
ClosestNodeFinder(CrossNode const & node, double & minDistance, Node & resultingCrossNode)
|
||||
: m_node(node), m_minDistance(minDistance), m_resultingCrossNode(resultingCrossNode)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(Node const & crossNode) const
|
||||
{
|
||||
if (crossNode.m_nodeId != m_node.node)
|
||||
return;
|
||||
|
||||
double const dist = ms::DistanceOnEarth(m_node.point, crossNode.m_point);
|
||||
if (dist < m_minDistance)
|
||||
{
|
||||
m_minDistance = dist;
|
||||
m_resultingCrossNode = crossNode;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CrossNode const & m_node;
|
||||
double & m_minDistance;
|
||||
Node & m_resultingCrossNode;
|
||||
};
|
||||
|
||||
double GetAdjacencyCost(CrossRoutingContextReader const & currentContext,
|
||||
IngoingCrossNode const & ingoingCrossNode,
|
||||
OutgoingCrossNode const & outgoingCrossNode)
|
||||
{
|
||||
return currentContext.GetAdjacencyCost(ingoingCrossNode, outgoingCrossNode);
|
||||
}
|
||||
|
||||
double GetAdjacencyCost(CrossRoutingContextReader const & currentContext,
|
||||
OutgoingCrossNode const & outgoingCrossNode,
|
||||
IngoingCrossNode const & ingoingCrossNode)
|
||||
{
|
||||
return GetAdjacencyCost(currentContext, ingoingCrossNode, outgoingCrossNode);
|
||||
}
|
||||
|
||||
template <class SourceNode, class TargetNode>
|
||||
class EdgesFiller
|
||||
{
|
||||
public:
|
||||
EdgesFiller(TRoutingMappingPtr const & currentMapping,
|
||||
CrossRoutingContextReader const & currentContext, SourceNode const & startingNode,
|
||||
CrossMwmGraph const & crossMwmGraph, vector<CrossWeightedEdge> & adj)
|
||||
: m_currentMapping(currentMapping)
|
||||
, m_currentContext(currentContext)
|
||||
, m_startingNode(startingNode)
|
||||
, m_crossMwmGraph(crossMwmGraph)
|
||||
, m_adj(adj)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(TargetNode const & node) const
|
||||
{
|
||||
TWrittenEdgeWeight const outWeight = GetAdjacencyCost(m_currentContext, m_startingNode, node);
|
||||
if (outWeight != kInvalidContextEdgeWeight && outWeight != 0)
|
||||
{
|
||||
vector<BorderCross> const & targets =
|
||||
m_crossMwmGraph.ConstructBorderCross(m_currentMapping, node);
|
||||
for (auto const & target : targets)
|
||||
{
|
||||
if (target.toNode.IsValid())
|
||||
m_adj.emplace_back(target, outWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TRoutingMappingPtr const & m_currentMapping;
|
||||
CrossRoutingContextReader const & m_currentContext;
|
||||
SourceNode const & m_startingNode;
|
||||
CrossMwmGraph const & m_crossMwmGraph;
|
||||
vector<CrossWeightedEdge> & m_adj;
|
||||
};
|
||||
|
||||
bool ForEachNodeNearPoint(CrossRoutingContextReader const & currentContext,
|
||||
CrossNode const & crossNode,
|
||||
ClosestNodeFinder<IngoingCrossNode> const & findingNode)
|
||||
{
|
||||
return currentContext.ForEachIngoingNodeNearPoint(crossNode.point, findingNode);
|
||||
}
|
||||
|
||||
bool ForEachNodeNearPoint(CrossRoutingContextReader const & currentContext,
|
||||
CrossNode const & crossNode,
|
||||
ClosestNodeFinder<OutgoingCrossNode> const & findingNode)
|
||||
{
|
||||
return currentContext.ForEachOutgoingNodeNearPoint(crossNode.point, findingNode);
|
||||
}
|
||||
|
||||
template <class Node>
|
||||
void FindCrossNode(CrossRoutingContextReader const & currentContext, CrossNode const & crossNode,
|
||||
Node & node)
|
||||
{
|
||||
double minDistance = std::numeric_limits<double>::max();
|
||||
ClosestNodeFinder<Node> findingNode(crossNode, minDistance, node);
|
||||
CHECK(ForEachNodeNearPoint(currentContext, crossNode, findingNode), ());
|
||||
CHECK_NOT_EQUAL(minDistance, std::numeric_limits<double>::max(),
|
||||
("crossNode.point:", crossNode.point));
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
vector<BorderCross> const & ConstructBorderCrossImpl(
|
||||
TWrittenNodeId nodeId, TRoutingMappingPtr const & currentMapping,
|
||||
unordered_map<CrossMwmGraph::TCachingKey, vector<BorderCross>, CrossMwmGraph::Hash> const &
|
||||
cachedNextNodes,
|
||||
Fn && borderCrossConstructor /*bool & result*/)
|
||||
{
|
||||
auto const key = make_pair(nodeId, currentMapping->GetMwmId());
|
||||
auto const it = cachedNextNodes.find(key);
|
||||
if (it != cachedNextNodes.end())
|
||||
return it->second;
|
||||
borderCrossConstructor(key);
|
||||
return cachedNextNodes.find(key)->second;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
{
|
||||
IRouter::ResultCode CrossMwmGraph::SetStartNode(CrossNode const & startNode)
|
||||
|
@ -54,14 +177,15 @@ IRouter::ResultCode CrossMwmGraph::SetStartNode(CrossNode const & startNode)
|
|||
{
|
||||
if (IsValidEdgeWeight(weights[i]))
|
||||
{
|
||||
vector<BorderCross> const & nextCrosses = ConstructBorderCross(outgoingNodes[i], startMapping);
|
||||
vector<BorderCross> const & nextCrosses =
|
||||
ConstructBorderCross(startMapping, outgoingNodes[i]);
|
||||
for (auto const & nextCross : nextCrosses)
|
||||
{
|
||||
if (nextCross.toNode.IsValid())
|
||||
dummyEdges.emplace_back(nextCross, weights[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_virtualEdges.insert(make_pair(startNode, dummyEdges));
|
||||
return IRouter::NoError;
|
||||
|
@ -129,24 +253,9 @@ IRouter::ResultCode CrossMwmGraph::SetFinalNode(CrossNode const & finalNode)
|
|||
return IRouter::NoError;
|
||||
}
|
||||
|
||||
vector<BorderCross> const & CrossMwmGraph::ConstructBorderCross(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping) const
|
||||
{
|
||||
// Check cached crosses.
|
||||
auto const key = make_pair(startNode.m_nodeId, currentMapping->GetMwmId());
|
||||
auto const it = m_cachedNextNodes.find(key);
|
||||
if (it != m_cachedNextNodes.end())
|
||||
return it->second;
|
||||
|
||||
// Cache miss case.
|
||||
auto & crosses = m_cachedNextNodes[key];
|
||||
ConstructBorderCrossImpl(startNode, currentMapping, crosses);
|
||||
return crosses;
|
||||
}
|
||||
|
||||
bool CrossMwmGraph::ConstructBorderCrossImpl(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & crosses) const
|
||||
bool CrossMwmGraph::ConstructBorderCrossByOutgoingImpl(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & crosses) const
|
||||
{
|
||||
auto const fromCross = CrossNode(startNode.m_nodeId, currentMapping->GetMwmId(), startNode.m_point);
|
||||
string const & nextMwm = currentMapping->m_crossContext.GetOutgoingMwmName(startNode);
|
||||
|
@ -156,71 +265,131 @@ bool CrossMwmGraph::ConstructBorderCrossImpl(OutgoingCrossNode const & startNode
|
|||
return false;
|
||||
ASSERT(crosses.empty(), ());
|
||||
nextMapping->LoadCrossContext();
|
||||
nextMapping->m_crossContext.ForEachIngoingNodeNearPoint(startNode.m_point, [&](IngoingCrossNode const & node)
|
||||
{
|
||||
if (node.m_point.EqualDxDy(startNode.m_point, kMwmCrossingNodeEqualityMeters * MercatorBounds::degreeInMetres))
|
||||
{
|
||||
auto const toCross = CrossNode(node.m_nodeId, nextMapping->GetMwmId(), node.m_point);
|
||||
if (toCross.IsValid())
|
||||
crosses.emplace_back(fromCross, toCross);
|
||||
}
|
||||
});
|
||||
nextMapping->m_crossContext.ForEachIngoingNodeNearPoint(
|
||||
startNode.m_point, [&](IngoingCrossNode const & node) {
|
||||
if (node.m_nodeId == INVALID_NODE_ID)
|
||||
return;
|
||||
|
||||
if (!node.m_point.EqualDxDy(
|
||||
startNode.m_point, kMwmCrossingNodeEqualityMeters * MercatorBounds::degreeInMetres))
|
||||
return;
|
||||
|
||||
crosses.emplace_back(fromCross,
|
||||
CrossNode(node.m_nodeId, nextMapping->GetMwmId(), node.m_point));
|
||||
});
|
||||
return !crosses.empty();
|
||||
}
|
||||
|
||||
void CrossMwmGraph::GetOutgoingEdgesList(BorderCross const & v,
|
||||
vector<CrossWeightedEdge> & adj) const
|
||||
bool CrossMwmGraph::ConstructBorderCrossByIngoingImpl(IngoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & crosses) const
|
||||
{
|
||||
ASSERT(crosses.empty(), ());
|
||||
auto const toCross = CrossNode(startNode.m_nodeId, currentMapping->GetMwmId(), startNode.m_point);
|
||||
vector<string> const & neighboringMwms = currentMapping->m_crossContext.GetNeighboringMwmList();
|
||||
string const & currentMwm = currentMapping->GetMwmId().GetInfo()->GetCountryName();
|
||||
// Note. There's no field |m_ingoingIndex| in class IngoingCrossNode. Because this
|
||||
// index is not saved in osrm routing section. So we need to write a workaround and
|
||||
// to check all neighboring mwms.
|
||||
for (string const & prevMwm : neighboringMwms)
|
||||
{
|
||||
TRoutingMappingPtr prevMapping = m_indexManager.GetMappingByName(prevMwm);
|
||||
if (!prevMapping->IsValid())
|
||||
continue;
|
||||
|
||||
prevMapping->LoadCrossContext();
|
||||
prevMapping->m_crossContext.ForEachOutgoingNodeNearPoint(
|
||||
startNode.m_point, [&](OutgoingCrossNode const & node) {
|
||||
if (node.m_nodeId == INVALID_NODE_ID)
|
||||
return;
|
||||
|
||||
if (prevMapping->m_crossContext.GetOutgoingMwmName(node) != currentMwm)
|
||||
return;
|
||||
|
||||
if (!node.m_point.EqualDxDy(startNode.m_point, kMwmCrossingNodeEqualityMeters *
|
||||
MercatorBounds::degreeInMetres))
|
||||
return;
|
||||
|
||||
crosses.emplace_back(CrossNode(node.m_nodeId, prevMapping->GetMwmId(), node.m_point),
|
||||
toCross);
|
||||
});
|
||||
}
|
||||
return !crosses.empty();
|
||||
}
|
||||
|
||||
vector<BorderCross> const & CrossMwmGraph::ConstructBorderCross(
|
||||
TRoutingMappingPtr const & currentMapping, OutgoingCrossNode const & node) const
|
||||
{
|
||||
return ConstructBorderCrossImpl(node.m_nodeId, currentMapping, m_cachedNextNodesByOutgoing,
|
||||
[&](std::pair<TWrittenNodeId, Index::MwmId const &> const & key) {
|
||||
vector<BorderCross> crosses;
|
||||
ConstructBorderCrossByOutgoingImpl(node, currentMapping,
|
||||
crosses);
|
||||
m_cachedNextNodesByOutgoing[key] = move(crosses);
|
||||
});
|
||||
}
|
||||
|
||||
vector<BorderCross> const & CrossMwmGraph::ConstructBorderCross(
|
||||
TRoutingMappingPtr const & currentMapping, IngoingCrossNode const & node) const
|
||||
{
|
||||
return ConstructBorderCrossImpl(node.m_nodeId, currentMapping, m_cachedNextNodesByIngoing,
|
||||
[&](std::pair<TWrittenNodeId, Index::MwmId const &> const & key) {
|
||||
vector<BorderCross> crosses;
|
||||
ConstructBorderCrossByIngoingImpl(node, currentMapping,
|
||||
crosses);
|
||||
m_cachedNextNodesByIngoing[key] = move(crosses);
|
||||
});
|
||||
}
|
||||
|
||||
void CrossMwmGraph::GetEdgesList(BorderCross const & v, bool isOutgoing,
|
||||
vector<CrossWeightedEdge> & adj) const
|
||||
{
|
||||
// Check for virtual edges.
|
||||
adj.clear();
|
||||
|
||||
// Note. Code below processes virtual edges. This code does not work properly if isOutgoing ==
|
||||
// false.
|
||||
// At the same time when this method is called with isOutgoing == false |m_virtualEdges| is empty.
|
||||
if (!isOutgoing && !m_virtualEdges.empty())
|
||||
{
|
||||
NOTIMPLEMENTED();
|
||||
return;
|
||||
}
|
||||
|
||||
auto const it = m_virtualEdges.find(v.toNode);
|
||||
if (it != m_virtualEdges.end())
|
||||
{
|
||||
adj.insert(adj.end(), it->second.begin(), it->second.end());
|
||||
// For last map we need to load virtual shortcuts and real cross roads. It takes to account case
|
||||
// when we have a path from the mwmw border to the point inside the map throuh another map.
|
||||
// when we have a path from the mwm border to the point inside the map throuh another map.
|
||||
// See Ust-Katav test for more.
|
||||
if (it->second.empty() || !it->second.front().GetTarget().toNode.isVirtual)
|
||||
return;
|
||||
}
|
||||
|
||||
// Loading cross routing section.
|
||||
TRoutingMappingPtr currentMapping = m_indexManager.GetMappingById(v.toNode.mwmId);
|
||||
TRoutingMappingPtr currentMapping =
|
||||
m_indexManager.GetMappingById(isOutgoing ? v.toNode.mwmId : v.fromNode.mwmId);
|
||||
ASSERT(currentMapping->IsValid(), ());
|
||||
currentMapping->LoadCrossContext();
|
||||
currentMapping->FreeFileIfPossible();
|
||||
|
||||
CrossRoutingContextReader const & currentContext = currentMapping->m_crossContext;
|
||||
|
||||
// Find income node.
|
||||
IngoingCrossNode ingoingNode;
|
||||
bool found = false;
|
||||
auto findingFn = [&ingoingNode, &v, &found](IngoingCrossNode const & node)
|
||||
if (isOutgoing)
|
||||
{
|
||||
if (node.m_nodeId == v.toNode.node)
|
||||
{
|
||||
found = true;
|
||||
ingoingNode = node;
|
||||
IngoingCrossNode ingoingNode;
|
||||
FindCrossNode<IngoingCrossNode>(currentContext, v.toNode, ingoingNode);
|
||||
currentContext.ForEachOutgoingNode(EdgesFiller<IngoingCrossNode, OutgoingCrossNode>(
|
||||
currentMapping, currentContext, ingoingNode, *this, adj));
|
||||
}
|
||||
else
|
||||
{
|
||||
OutgoingCrossNode outgoingNode;
|
||||
FindCrossNode<OutgoingCrossNode>(currentContext, v.fromNode, outgoingNode);
|
||||
currentContext.ForEachIngoingNode(EdgesFiller<OutgoingCrossNode, IngoingCrossNode>(
|
||||
currentMapping, currentContext, outgoingNode, *this, adj));
|
||||
}
|
||||
};
|
||||
CHECK(currentContext.ForEachIngoingNodeNearPoint(v.toNode.point, findingFn), ());
|
||||
|
||||
CHECK(found, ());
|
||||
|
||||
// Find outs. Generate adjacency list.
|
||||
currentContext.ForEachOutgoingNode([&, this](OutgoingCrossNode const & node)
|
||||
{
|
||||
EdgeWeight const outWeight = currentContext.GetAdjacencyCost(ingoingNode, node);
|
||||
if (outWeight != kInvalidContextEdgeWeight && outWeight != 0)
|
||||
{
|
||||
vector<BorderCross> const & targets = ConstructBorderCross(node, currentMapping);
|
||||
for (auto const & target : targets)
|
||||
{
|
||||
if (target.toNode.IsValid())
|
||||
adj.emplace_back(target, outWeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double CrossMwmGraph::HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const
|
||||
|
|
|
@ -72,6 +72,7 @@ struct BorderCross
|
|||
BorderCross(CrossNode const & from, CrossNode const & to) : fromNode(from), toNode(to) {}
|
||||
BorderCross() = default;
|
||||
|
||||
// TODO(bykoianko) Consider using fields |fromNode| and |toNode| in operator== and operator<.
|
||||
inline bool operator==(BorderCross const & a) const { return toNode == a.toNode; }
|
||||
inline bool operator<(BorderCross const & a) const { return toNode < a.toNode; }
|
||||
};
|
||||
|
@ -101,48 +102,10 @@ private:
|
|||
class CrossMwmGraph
|
||||
{
|
||||
public:
|
||||
using TCachingKey = pair<TWrittenNodeId, Index::MwmId>;
|
||||
using TVertexType = BorderCross;
|
||||
using TEdgeType = CrossWeightedEdge;
|
||||
|
||||
explicit CrossMwmGraph(RoutingIndexManager & indexManager) : m_indexManager(indexManager) {}
|
||||
|
||||
void GetOutgoingEdgesList(BorderCross const & v, vector<CrossWeightedEdge> & adj) const;
|
||||
void GetIngoingEdgesList(BorderCross const & /* v */,
|
||||
vector<CrossWeightedEdge> & /* adj */) const
|
||||
{
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
double HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const;
|
||||
|
||||
IRouter::ResultCode SetStartNode(CrossNode const & startNode);
|
||||
IRouter::ResultCode SetFinalNode(CrossNode const & finalNode);
|
||||
|
||||
private:
|
||||
// Cashing wrapper for the ConstructBorderCrossImpl function.
|
||||
vector<BorderCross> const & ConstructBorderCross(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping) const;
|
||||
|
||||
// Pure function to construct boder cross by outgoing cross node.
|
||||
bool ConstructBorderCrossImpl(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & cross) const;
|
||||
/*!
|
||||
* Adds a virtual edge to the graph so that it is possible to represent
|
||||
* the final segment of the path that leads from the map's border
|
||||
* to finalNode. Addition of such virtual edges for the starting node is
|
||||
* inlined elsewhere.
|
||||
*/
|
||||
void AddVirtualEdge(IngoingCrossNode const & node, CrossNode const & finalNode,
|
||||
EdgeWeight weight);
|
||||
|
||||
map<CrossNode, vector<CrossWeightedEdge> > m_virtualEdges;
|
||||
|
||||
mutable RoutingIndexManager m_indexManager;
|
||||
|
||||
// Caching stuff.
|
||||
using TCachingKey = pair<TWrittenNodeId, Index::MwmId>;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
size_t operator()(TCachingKey const & p) const
|
||||
|
@ -151,7 +114,53 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
mutable unordered_map<TCachingKey, vector<BorderCross>, Hash> m_cachedNextNodes;
|
||||
explicit CrossMwmGraph(RoutingIndexManager & indexManager) : m_indexManager(indexManager) {}
|
||||
void GetOutgoingEdgesList(BorderCross const & v, vector<CrossWeightedEdge> & adj) const
|
||||
{
|
||||
GetEdgesList(v, true /* isOutgoing */, adj);
|
||||
}
|
||||
|
||||
void GetIngoingEdgesList(BorderCross const & v, vector<CrossWeightedEdge> & adj) const
|
||||
{
|
||||
GetEdgesList(v, false /* isOutgoing */, adj);
|
||||
}
|
||||
|
||||
double HeuristicCostEstimate(BorderCross const & v, BorderCross const & w) const;
|
||||
|
||||
IRouter::ResultCode SetStartNode(CrossNode const & startNode);
|
||||
IRouter::ResultCode SetFinalNode(CrossNode const & finalNode);
|
||||
|
||||
vector<BorderCross> const & ConstructBorderCross(TRoutingMappingPtr const & currentMapping,
|
||||
OutgoingCrossNode const & node) const;
|
||||
vector<BorderCross> const & ConstructBorderCross(TRoutingMappingPtr const & currentMapping,
|
||||
IngoingCrossNode const & node) const;
|
||||
|
||||
private:
|
||||
// Pure function to construct boder cross by outgoing cross node.
|
||||
bool ConstructBorderCrossByOutgoingImpl(OutgoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & cross) const;
|
||||
bool ConstructBorderCrossByIngoingImpl(IngoingCrossNode const & startNode,
|
||||
TRoutingMappingPtr const & currentMapping,
|
||||
vector<BorderCross> & crosses) const;
|
||||
|
||||
/*!
|
||||
* Adds a virtual edge to the graph so that it is possible to represent
|
||||
* the final segment of the path that leads from the map's border
|
||||
* to finalNode. Addition of such virtual edges for the starting node is
|
||||
* inlined elsewhere.
|
||||
*/
|
||||
void AddVirtualEdge(IngoingCrossNode const & node, CrossNode const & finalNode,
|
||||
EdgeWeight weight);
|
||||
void GetEdgesList(BorderCross const & v, bool isOutgoing, vector<CrossWeightedEdge> & adj) const;
|
||||
|
||||
map<CrossNode, vector<CrossWeightedEdge> > m_virtualEdges;
|
||||
|
||||
mutable RoutingIndexManager m_indexManager;
|
||||
|
||||
// @TODO(bykoianko) Consider removing key work mutable.
|
||||
mutable unordered_map<TCachingKey, vector<BorderCross>, Hash> m_cachedNextNodesByIngoing;
|
||||
mutable unordered_map<TCachingKey, vector<BorderCross>, Hash> m_cachedNextNodesByOutgoing;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -105,8 +105,8 @@ const string & CrossRoutingContextReader::GetOutgoingMwmName(
|
|||
return m_neighborMwmList[outgoingNode.m_outgoingIndex];
|
||||
}
|
||||
|
||||
TWrittenEdgeWeight CrossRoutingContextReader::GetAdjacencyCost(IngoingCrossNode const & ingoing,
|
||||
OutgoingCrossNode const & outgoing) const
|
||||
TWrittenEdgeWeight CrossRoutingContextReader::GetAdjacencyCost(
|
||||
IngoingCrossNode const & ingoing, OutgoingCrossNode const & outgoing) const
|
||||
{
|
||||
if (ingoing.m_adjacencyIndex == kInvalidAdjacencyIndex ||
|
||||
outgoing.m_adjacencyIndex == kInvalidAdjacencyIndex)
|
||||
|
|
|
@ -91,7 +91,7 @@ class CrossRoutingContextReader
|
|||
public:
|
||||
void Load(Reader const & r);
|
||||
|
||||
const string & GetOutgoingMwmName(OutgoingCrossNode const & mwmIndex) const;
|
||||
const string & GetOutgoingMwmName(OutgoingCrossNode const & outgoingNode) const;
|
||||
|
||||
TWrittenEdgeWeight GetAdjacencyCost(IngoingCrossNode const & ingoing,
|
||||
OutgoingCrossNode const & outgoing) const;
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/cross_mwm_road_graph.hpp"
|
||||
#include "routing/cross_routing_context.hpp"
|
||||
#include "routing/osrm2feature_map.hpp"
|
||||
#include "routing/routing_mapping.hpp"
|
||||
|
||||
#include "storage/country_info_getter.hpp"
|
||||
|
||||
#include "indexer/index.hpp"
|
||||
|
||||
#include "platform/local_country_file.hpp"
|
||||
#include "platform/local_country_file_utils.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
|
@ -12,9 +17,12 @@
|
|||
|
||||
#include "base/buffer_vector.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/random.hpp"
|
||||
|
||||
#include "std/limits.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace routing;
|
||||
|
||||
namespace
|
||||
|
@ -123,4 +131,98 @@ UNIT_TEST(CheckOsrmToFeatureMapping)
|
|||
LOG(LINFO, ("Found", localFiles.size(), "countries. In", checked, "maps with routing have", errors, "with errors."));
|
||||
TEST_EQUAL(errors, 0, ("Some countries have osrm and features mismatch."));
|
||||
}
|
||||
|
||||
// The idea behind the test is
|
||||
// 1. to go through all ingoing nodes of all mwms
|
||||
// 2. to find all cross mwm outgoing edges for each ingoing node
|
||||
// 3. to find all edges which are ingoing for the outgoing edges
|
||||
// 4. to check that an edge mentioned in (1) there's between the ingoing edges
|
||||
// Note. This test may take more than 3 hours.
|
||||
UNIT_TEST(CrossMwmGraphTest)
|
||||
{
|
||||
vector<platform::LocalCountryFile> localFiles;
|
||||
Index index;
|
||||
platform::FindAllLocalMapsAndCleanup(numeric_limits<int64_t>::max() /* latestVersion */,
|
||||
localFiles);
|
||||
|
||||
for (platform::LocalCountryFile & file : localFiles)
|
||||
{
|
||||
file.SyncWithDisk();
|
||||
index.RegisterMap(file);
|
||||
}
|
||||
|
||||
for (auto it = localFiles.begin(); it != localFiles.end();)
|
||||
{
|
||||
string const & countryName = it->GetCountryName();
|
||||
MwmSet::MwmId const mwmId = index.GetMwmIdByCountryFile(it->GetCountryFile());
|
||||
if (countryName == "minsk-pass" || mwmId.GetInfo()->GetType() != MwmInfo::COUNTRY)
|
||||
it = localFiles.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
Platform p;
|
||||
unique_ptr<storage::CountryInfoGetter> infoGetter =
|
||||
storage::CountryInfoReader::CreateCountryInfoReader(p);
|
||||
auto countryFileGetter = [&infoGetter](m2::PointD const & pt) {
|
||||
return infoGetter->GetRegionCountryId(pt);
|
||||
};
|
||||
|
||||
RoutingIndexManager manager(countryFileGetter, index);
|
||||
CrossMwmGraph crossMwmGraph(manager);
|
||||
|
||||
auto const seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
LOG(LINFO, ("Seed for RandomSample:", seed));
|
||||
std::minstd_rand rng(static_cast<unsigned int>(seed));
|
||||
std::vector<size_t> subset = base::RandomSample(localFiles.size(), 10 /* mwm number */, rng);
|
||||
std::vector<platform::LocalCountryFile> subsetCountryFiles;
|
||||
for (size_t i : subset)
|
||||
subsetCountryFiles.push_back(localFiles[i]);
|
||||
|
||||
for (platform::LocalCountryFile const & file : subsetCountryFiles)
|
||||
{
|
||||
string const & countryName = file.GetCountryName();
|
||||
LOG(LINFO, ("Processing", countryName));
|
||||
MwmSet::MwmId const mwmId = index.GetMwmIdByCountryFile(file.GetCountryFile());
|
||||
|
||||
TEST(mwmId.IsAlive(), ("Mwm name:", countryName, "Subset:", subsetCountryFiles));
|
||||
TRoutingMappingPtr currentMapping = manager.GetMappingById(mwmId);
|
||||
if (!currentMapping->IsValid())
|
||||
continue; // No routing sections in the mwm.
|
||||
|
||||
currentMapping->LoadCrossContext();
|
||||
currentMapping->FreeFileIfPossible();
|
||||
CrossRoutingContextReader const & currentContext = currentMapping->m_crossContext;
|
||||
|
||||
size_t ingoingCounter = 0;
|
||||
currentContext.ForEachIngoingNode([&](IngoingCrossNode const & node) {
|
||||
++ingoingCounter;
|
||||
vector<BorderCross> const & targets =
|
||||
crossMwmGraph.ConstructBorderCross(currentMapping, node);
|
||||
for (BorderCross const & t : targets)
|
||||
{
|
||||
vector<CrossWeightedEdge> outAdjs;
|
||||
crossMwmGraph.GetOutgoingEdgesList(t, outAdjs);
|
||||
for (CrossWeightedEdge const & out : outAdjs)
|
||||
{
|
||||
vector<CrossWeightedEdge> inAdjs;
|
||||
crossMwmGraph.GetIngoingEdgesList(out.GetTarget(), inAdjs);
|
||||
TEST(find_if(inAdjs.cbegin(), inAdjs.cend(),
|
||||
[&](CrossWeightedEdge const & e) {
|
||||
return e.GetTarget() == t && out.GetWeight() == e.GetWeight();
|
||||
}) != inAdjs.cend(),
|
||||
("ForEachOutgoingNodeNearPoint() and ForEachIngoingNodeNearPoint() arn't "
|
||||
"correlated. Mwm:",
|
||||
countryName, "Subset:", subsetCountryFiles));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
size_t outgoingCounter = 0;
|
||||
currentContext.ForEachOutgoingNode(
|
||||
[&](OutgoingCrossNode const & /* node */) { ++outgoingCounter; });
|
||||
|
||||
LOG(LINFO, ("Processed:", countryName, "Exits:", outgoingCounter, "Enters:", ingoingCounter));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "indexer/rank_table.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "base/random.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include "std/iterator.hpp"
|
||||
|
@ -46,22 +47,6 @@ struct ComparePreResult1
|
|||
}
|
||||
};
|
||||
|
||||
// Selects a fair random subset of size min(|n|, |k|) from [0, 1, 2, ..., n - 1].
|
||||
vector<size_t> RandomSample(size_t n, size_t k, minstd_rand & rng)
|
||||
{
|
||||
vector<size_t> result(std::min(k, n));
|
||||
iota(result.begin(), result.end(), 0);
|
||||
|
||||
for (size_t i = k; i < n; ++i)
|
||||
{
|
||||
size_t const j = rng() % (i + 1);
|
||||
if (j < k)
|
||||
result[j] = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SweepNearbyResults(double eps, vector<PreResult1> & results)
|
||||
{
|
||||
NearbyPointsSweeper sweeper(eps);
|
||||
|
@ -281,7 +266,7 @@ void PreRanker::FilterForViewportSearch()
|
|||
|
||||
if (m <= old)
|
||||
{
|
||||
for (size_t i : RandomSample(old, m, m_rng))
|
||||
for (size_t i : base::RandomSample(old, m, m_rng))
|
||||
results.push_back(m_results[bucket[i]]);
|
||||
}
|
||||
else
|
||||
|
@ -289,7 +274,7 @@ void PreRanker::FilterForViewportSearch()
|
|||
for (size_t i = 0; i < old; ++i)
|
||||
results.push_back(m_results[bucket[i]]);
|
||||
|
||||
for (size_t i : RandomSample(bucket.size() - old, m - old, m_rng))
|
||||
for (size_t i : base::RandomSample(bucket.size() - old, m - old, m_rng))
|
||||
results.push_back(m_results[bucket[old + i]]);
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +286,7 @@ void PreRanker::FilterForViewportSearch()
|
|||
else
|
||||
{
|
||||
m_results.clear();
|
||||
for (size_t i : RandomSample(results.size(), BatchSize(), m_rng))
|
||||
for (size_t i : base::RandomSample(results.size(), BatchSize(), m_rng))
|
||||
m_results.push_back(results[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
39FD27381CC65AD000AFF551 /* timegm_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD26E21CC65A0E00AFF551 /* timegm_test.cpp */; };
|
||||
39FD27391CC65AD000AFF551 /* timer_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD26E31CC65A0E00AFF551 /* timer_test.cpp */; };
|
||||
39FD273B1CC65B1000AFF551 /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 675341771A3F57BF00A0A8C3 /* libbase.a */; };
|
||||
56B1A0741E69DE4D00395022 /* random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56B1A0711E69DE4D00395022 /* random.cpp */; };
|
||||
56B1A0751E69DE4D00395022 /* random.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1A0721E69DE4D00395022 /* random.hpp */; };
|
||||
56B1A0761E69DE4D00395022 /* small_set.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56B1A0731E69DE4D00395022 /* small_set.hpp */; };
|
||||
670E39441C46C76900E9C0A6 /* sunrise_sunset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670E39421C46C76900E9C0A6 /* sunrise_sunset.cpp */; };
|
||||
670E39451C46C76900E9C0A6 /* sunrise_sunset.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670E39431C46C76900E9C0A6 /* sunrise_sunset.hpp */; };
|
||||
671182F01C807C0A00CB8177 /* gmtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 671182EE1C807C0A00CB8177 /* gmtime.cpp */; };
|
||||
|
@ -157,6 +160,9 @@
|
|||
39FD273D1CC65B1000AFF551 /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform.a; path = "../../../omim-xcode-build/Debug/libplatform.a"; sourceTree = "<group>"; };
|
||||
39FD27401CC65B2800AFF551 /* libindexer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libindexer.a; path = "../../../omim-xcode-build/Debug/libindexer.a"; sourceTree = "<group>"; };
|
||||
39FD27421CC65B4800AFF551 /* libcoding.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcoding.a; path = "../../../omim-xcode-build/Debug/libcoding.a"; sourceTree = "<group>"; };
|
||||
56B1A0711E69DE4D00395022 /* random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = random.cpp; sourceTree = "<group>"; };
|
||||
56B1A0721E69DE4D00395022 /* random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = random.hpp; sourceTree = "<group>"; };
|
||||
56B1A0731E69DE4D00395022 /* small_set.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = small_set.hpp; sourceTree = "<group>"; };
|
||||
670E39421C46C76900E9C0A6 /* sunrise_sunset.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sunrise_sunset.cpp; sourceTree = "<group>"; };
|
||||
670E39431C46C76900E9C0A6 /* sunrise_sunset.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sunrise_sunset.hpp; sourceTree = "<group>"; };
|
||||
671182EE1C807C0A00CB8177 /* gmtime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gmtime.cpp; sourceTree = "<group>"; };
|
||||
|
@ -311,6 +317,9 @@
|
|||
675341791A3F57BF00A0A8C3 /* base */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
56B1A0711E69DE4D00395022 /* random.cpp */,
|
||||
56B1A0721E69DE4D00395022 /* random.hpp */,
|
||||
56B1A0731E69DE4D00395022 /* small_set.hpp */,
|
||||
675341851A3F57E400A0A8C3 /* array_adapters.hpp */,
|
||||
675341861A3F57E400A0A8C3 /* assert.hpp */,
|
||||
675341871A3F57E400A0A8C3 /* base.cpp */,
|
||||
|
@ -420,6 +429,7 @@
|
|||
675341D11A3F57E400A0A8C3 /* cache.hpp in Headers */,
|
||||
675341E31A3F57E400A0A8C3 /* math.hpp in Headers */,
|
||||
3446C6731DDCA96300146687 /* levenshtein_dfa.hpp in Headers */,
|
||||
56B1A0751E69DE4D00395022 /* random.hpp in Headers */,
|
||||
675341E21A3F57E400A0A8C3 /* macros.hpp in Headers */,
|
||||
672DD4C51E0425600078E13C /* observer_list.hpp in Headers */,
|
||||
675341EF1A3F57E400A0A8C3 /* rolling_hash.hpp in Headers */,
|
||||
|
@ -452,6 +462,7 @@
|
|||
675341FA1A3F57E400A0A8C3 /* src_point.hpp in Headers */,
|
||||
674A7E2F1C0DB03D003D48E1 /* timegm.hpp in Headers */,
|
||||
675341F71A3F57E400A0A8C3 /* shared_buffer_manager.hpp in Headers */,
|
||||
56B1A0761E69DE4D00395022 /* small_set.hpp in Headers */,
|
||||
67B52B611AD3C84E00664C17 /* thread_checker.hpp in Headers */,
|
||||
672DD4BE1E0425600078E13C /* cancellable.hpp in Headers */,
|
||||
675341CB1A3F57E400A0A8C3 /* array_adapters.hpp in Headers */,
|
||||
|
@ -601,6 +612,7 @@
|
|||
67E40EC81E4DC0D500A6D200 /* small_set_test.cpp in Sources */,
|
||||
6753420E1A3F57E400A0A8C3 /* timer.cpp in Sources */,
|
||||
675341F61A3F57E400A0A8C3 /* shared_buffer_manager.cpp in Sources */,
|
||||
56B1A0741E69DE4D00395022 /* random.cpp in Sources */,
|
||||
675341DA1A3F57E400A0A8C3 /* exception.cpp in Sources */,
|
||||
675341F91A3F57E400A0A8C3 /* src_point.cpp in Sources */,
|
||||
675342031A3F57E400A0A8C3 /* strings_bundle.cpp in Sources */,
|
||||
|
|
Loading…
Add table
Reference in a new issue