From 4d66ab38cec6c276a4bb21abb6a33664d040470c Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Fri, 3 Mar 2017 10:09:39 +0300 Subject: [PATCH] Concistency test on CrossMwmGraph::GetOutgoingEdgesList() and CrossMwmGraph::GetIngoingEdgesList(). --- routing/cross_mwm_road_graph.cpp | 189 ++++++++++++------ routing/cross_mwm_road_graph.hpp | 20 +- routing/cross_routing_context.cpp | 2 +- .../cross_section_tests.cpp | 74 +++++++ 4 files changed, 214 insertions(+), 71 deletions(-) diff --git a/routing/cross_mwm_road_graph.cpp b/routing/cross_mwm_road_graph.cpp index efa4e62c21..7306ddfd0b 100644 --- a/routing/cross_mwm_road_graph.cpp +++ b/routing/cross_mwm_road_graph.cpp @@ -3,40 +3,135 @@ #include "geometry/distance_on_sphere.hpp" +#include + namespace { using namespace routing; inline bool IsValidEdgeWeight(EdgeWeight const & w) { return w != INVALID_EDGE_WEIGHT; } +template +struct FindingNode +{ + FindingNode(CrossNode const & node, double & minDistance, CrossNodeType & resultingCrossNode) + : m_node(node), m_minDistance(minDistance), m_resultingCrossNode(resultingCrossNode) + { + } + + void operator()(CrossNodeType const & crossNode) const + { + if (crossNode.m_nodeId == m_node.node) + { + double const dist = ms::DistanceOnEarth(m_node.point, crossNode.m_point); + if (dist < m_minDistance) + { + m_minDistance = dist; + m_resultingCrossNode = crossNode; + } + } + } + + CrossNode const & m_node; + double & m_minDistance; + CrossNodeType & m_resultingCrossNode; +}; + +double GetAdjacencyCost(CrossRoutingContextReader const & m_currentContext, + IngoingCrossNode const & ingoingCrossNode, + OutgoingCrossNode const & outgoingCrossNode) +{ + return m_currentContext.GetAdjacencyCost(ingoingCrossNode, outgoingCrossNode); +} + +double GetAdjacencyCost(CrossRoutingContextReader const & m_currentContext, + OutgoingCrossNode const & outgoingCrossNode, + IngoingCrossNode const & ingoingCrossNode) +{ + return m_currentContext.GetAdjacencyCost(ingoingCrossNode, outgoingCrossNode); +} + +vector const & ConstructBorderCross(CrossMwmGraph const & crossMwmGraph, + TRoutingMappingPtr const & currentMapping, + OutgoingCrossNode const & node) +{ + return crossMwmGraph.ConstructBorderCrossByOutgoing(node, currentMapping); +} + +vector const & ConstructBorderCross(CrossMwmGraph const & crossMwmGraph, + TRoutingMappingPtr const & currentMapping, + IngoingCrossNode const & node) +{ + return crossMwmGraph.ConstructBorderCrossByIngoing(node, currentMapping); +} + +template +struct FillingEdges +{ + FillingEdges(TRoutingMappingPtr const & currentMapping, CrossRoutingContextReader const & currentContext, + CrossNodeType2 const & startingNode, CrossMwmGraph const & crossMwmGraph, vector & adj) + : m_currentMapping(currentMapping), m_currentContext(currentContext), m_startingNode(startingNode), + m_crossMwmGraph(crossMwmGraph), m_adj(adj) + { + } + + void operator()(CrossNodeType1 const & node) const + { + TWrittenEdgeWeight const outWeight = GetAdjacencyCost(m_currentContext, m_startingNode, node); + if (outWeight != kInvalidContextEdgeWeight && outWeight != 0) + { + vector const & targets = ConstructBorderCross(m_crossMwmGraph, m_currentMapping, node); + for (auto const & target : targets) + { + if (target.toNode.IsValid()) + m_adj.emplace_back(target, outWeight); + } + } + } + + TRoutingMappingPtr const & m_currentMapping; + CrossRoutingContextReader const & m_currentContext; + CrossNodeType2 const & m_startingNode; + CrossMwmGraph const & m_crossMwmGraph; + vector & m_adj; +}; + void FindIngoingCrossNode(CrossRoutingContextReader const & currentContext, CrossNode const & toNode, IngoingCrossNode & ingoingNode) { - bool found = false; - auto const findingFn = [&ingoingNode, &toNode, &found](IngoingCrossNode const & node){ - if (node.m_nodeId == toNode.node) - { - found = true; - ingoingNode = node; - } - }; - CHECK(currentContext.ForEachIngoingNodeNearPoint(toNode.point, findingFn), ()); - CHECK(found, ()); + double minDistance = std::numeric_limits::max(); + FindingNode findingNode(toNode, minDistance, ingoingNode); + + CHECK(currentContext.ForEachIngoingNodeNearPoint(toNode.point, findingNode), ("toNode.point:", toNode.point)); + CHECK_NOT_EQUAL(minDistance, std::numeric_limits::max(), ("toNode.point:", toNode.point)); } void FindOutgoingCrossNode(CrossRoutingContextReader const & currentContext, CrossNode const & fromNode, OutgoingCrossNode & outgoingNode) { - bool found = false; - auto const findingFn = [&outgoingNode, &fromNode, &found](OutgoingCrossNode const & node){ - if (node.m_nodeId == fromNode.node) - { - found = true; - outgoingNode = node; - } - }; - CHECK(currentContext.ForEachOutgoingNodeNearPoint(fromNode.point, findingFn), ()); - CHECK(found, ()); + double minDistance = std::numeric_limits::max(); + FindingNode findingNode(fromNode, minDistance, outgoingNode); + CHECK(currentContext.ForEachOutgoingNodeNearPoint(fromNode.point, findingNode), ("fromNode.point:", fromNode.point)); + CHECK_NOT_EQUAL(minDistance, std::numeric_limits::max(), ("toNode.point:", fromNode.point)); +} + +vector & FindBorderCross(TWrittenNodeId nodeId, + TRoutingMappingPtr const & currentMapping, + unordered_map, + CrossMwmGraph::Hash> & cachedNextNodes, + bool & result) +{ + auto const key = make_pair(nodeId, currentMapping->GetMwmId()); + auto it = cachedNextNodes.find(key); + result = (it == cachedNextNodes.end()) ? false : true; + if (it != cachedNextNodes.end()) + { + result = true; + return it->second; + } + + result = false; + return cachedNextNodes[key]; } } // namespace @@ -164,12 +259,11 @@ IRouter::ResultCode CrossMwmGraph::SetFinalNode(CrossNode const & finalNode) vector const & CrossMwmGraph::ConstructBorderCrossByOutgoing(OutgoingCrossNode const & startNode, TRoutingMappingPtr const & currentMapping) const { - auto const key = make_pair(startNode.m_nodeId, currentMapping->GetMwmId()); - auto const it = m_cachedNextNodesByOutgoing.find(key); - if (it != m_cachedNextNodesByOutgoing.end()) - return it->second; + bool result = false; + vector & crosses = FindBorderCross(startNode.m_nodeId, currentMapping, m_cachedNextNodesByOutgoing, result); + if (result) + return crosses; - auto & crosses = m_cachedNextNodesByOutgoing[key]; ConstructBorderCrossByOutgoingImpl(startNode, currentMapping, crosses); return crosses; } @@ -177,12 +271,11 @@ vector const & CrossMwmGraph::ConstructBorderCrossByOutgoing(Outgoi vector const & CrossMwmGraph::ConstructBorderCrossByIngoing(IngoingCrossNode const & startNode, TRoutingMappingPtr const & currentMapping) const { - auto const key = make_pair(startNode.m_nodeId, currentMapping->GetMwmId()); - auto const it = m_cachedNextNodesByIngoing.find(key); - if (it != m_cachedNextNodesByIngoing.end()) - return it->second; + bool result = false; + vector & crosses = FindBorderCross(startNode.m_nodeId, currentMapping, m_cachedNextNodesByIngoing, result); + if (result) + return crosses; - auto & crosses = m_cachedNextNodesByIngoing[key]; ConstructBorderCrossByIngoingImpl(startNode, currentMapping, crosses); return crosses; } @@ -221,7 +314,7 @@ bool CrossMwmGraph::ConstructBorderCrossByIngoingImpl(IngoingCrossNode const & s vector 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 work around and + // index is not saved in osrm routing section. So we need to write a workaround and // to check all neighboring mwms. for (string const & nextMwm : neighboringMwms) { @@ -283,39 +376,17 @@ void CrossMwmGraph::GetEdgesList(BorderCross const & v, bool isOutgoing, vector< { IngoingCrossNode ingoingNode; FindIngoingCrossNode(currentContext, v.toNode, ingoingNode); - currentContext.ForEachOutgoingNode([&, this](OutgoingCrossNode const & node) - { - EdgeWeight const outWeight = currentContext.GetAdjacencyCost(ingoingNode, node); - if (outWeight != kInvalidContextEdgeWeight && outWeight != 0) - { - vector const & targets = ConstructBorderCrossByOutgoing( - node, currentMapping); - for (auto const & target : targets) - { - if (target.toNode.IsValid()) - adj.emplace_back(target, outWeight); - } - } - }); + currentContext.ForEachOutgoingNode( + FillingEdges(currentMapping, currentContext, ingoingNode, + *this, adj)); } else { OutgoingCrossNode outgoingNode; FindOutgoingCrossNode(currentContext, v.fromNode, outgoingNode); - currentContext.ForEachIngoingNode([&, this](IngoingCrossNode const & node) - { - EdgeWeight const weight = currentContext.GetAdjacencyCost(node, outgoingNode); - if (weight != kInvalidContextEdgeWeight && weight != 0) - { - vector const & targets = ConstructBorderCrossByIngoing( - node, currentMapping); - for (auto const & target : targets) - { - if (target.fromNode.IsValid()) - adj.emplace_back(target, weight); - } - } - }); + currentContext.ForEachIngoingNode( + FillingEdges(currentMapping, currentContext, outgoingNode, + *this, adj)); } } diff --git a/routing/cross_mwm_road_graph.hpp b/routing/cross_mwm_road_graph.hpp index 4b597a62ff..4b60c780de 100644 --- a/routing/cross_mwm_road_graph.hpp +++ b/routing/cross_mwm_road_graph.hpp @@ -102,9 +102,18 @@ private: class CrossMwmGraph { public: + using TCachingKey = pair; using TVertexType = BorderCross; using TEdgeType = CrossWeightedEdge; + struct Hash + { + size_t operator()(TCachingKey const & p) const + { + return hash()(p.first) ^ hash()(p.second.GetInfo()->GetCountryName()); + } + }; + explicit CrossMwmGraph(RoutingIndexManager & indexManager) : m_indexManager(indexManager) {} void GetOutgoingEdgesList(BorderCross const & v, vector & adj) const @@ -150,17 +159,6 @@ private: mutable RoutingIndexManager m_indexManager; - // Caching stuff. - using TCachingKey = pair; - - struct Hash - { - size_t operator()(TCachingKey const & p) const - { - return hash()(p.first) ^ hash()(p.second.GetInfo()->GetCountryName()); - } - }; - // @TODO(bykoianko) Consider removing key work mutable. mutable unordered_map, Hash> m_cachedNextNodesByIngoing; mutable unordered_map, Hash> m_cachedNextNodesByOutgoing; diff --git a/routing/cross_routing_context.cpp b/routing/cross_routing_context.cpp index 4fc6dcf9c3..e3115ed37c 100644 --- a/routing/cross_routing_context.cpp +++ b/routing/cross_routing_context.cpp @@ -106,7 +106,7 @@ const string & CrossRoutingContextReader::GetOutgoingMwmName( } TWrittenEdgeWeight CrossRoutingContextReader::GetAdjacencyCost(IngoingCrossNode const & ingoing, - OutgoingCrossNode const & outgoing) const + OutgoingCrossNode const & outgoing) const { if (ingoing.m_adjacencyIndex == kInvalidAdjacencyIndex || outgoing.m_adjacencyIndex == kInvalidAdjacencyIndex) diff --git a/routing/routing_integration_tests/cross_section_tests.cpp b/routing/routing_integration_tests/cross_section_tests.cpp index b248ed583a..93e1445749 100644 --- a/routing/routing_integration_tests/cross_section_tests.cpp +++ b/routing/routing_integration_tests/cross_section_tests.cpp @@ -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" @@ -123,4 +128,73 @@ 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 localFiles; + Index index; + platform::FindAllLocalMapsAndCleanup(numeric_limits::max() /* latestVersion */, + localFiles); + + for (platform::LocalCountryFile & file : localFiles) + { + file.SyncWithDisk(); + index.RegisterMap(file); + } + + Platform p; + unique_ptr infoGetter = storage::CountryInfoReader::CreateCountryInfoReader(p); + auto countryFileGetter = [&infoGetter](m2::PointD const & pt) + { + return infoGetter->GetRegionCountryId(pt); + }; + + RoutingIndexManager manager(countryFileGetter, index); + CrossMwmGraph crossMwmGraph(manager); + + for (platform::LocalCountryFile const & file : localFiles) + { + string const & countryName = file.GetCountryName(); + MwmSet::MwmId const mwmId = index.GetMwmIdByCountryFile(file.GetCountryFile()); + + if (countryName == "minsk-pass" || mwmId.GetInfo()->GetType() != MwmInfo::COUNTRY) + continue; + + TEST(mwmId.IsAlive(), ()); + 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; + + currentContext.ForEachIngoingNode([&](IngoingCrossNode const & node) + { + vector const & targets = crossMwmGraph.ConstructBorderCrossByIngoing(node, currentMapping); + for (BorderCross const & t : targets) + { + vector outAdjs; + crossMwmGraph.GetOutgoingEdgesList(t, outAdjs); + for (CrossWeightedEdge const & out : outAdjs) + { + vector 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:", + file.GetCountryName())); + } + } + }); + LOG(LINFO, ("Processed", file.GetCountryName())); + } +} } // namespace