From eb088e3e7fd1b13c424584f2d84cd7eb5ca7d981 Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Mon, 13 Jul 2015 18:11:57 +0300 Subject: [PATCH] Implemented crossmwm routing, fixed crash of routing on overlapped mwms. --- integration_tests/pedestrian_route_test.cpp | 16 ++ integration_tests/routing_test_tools.cpp | 13 +- map/framework.cpp | 3 +- .../pedestrian_routing_benchmarks.cpp | 38 +--- routing/features_road_graph.cpp | 174 +++++++++++++++--- routing/features_road_graph.hpp | 64 +++++-- routing/nearest_edge_finder.cpp | 22 +-- routing/nearest_edge_finder.hpp | 13 +- routing/road_graph.cpp | 8 +- routing/road_graph.hpp | 31 ++-- routing/road_graph_router.cpp | 81 ++------ routing/road_graph_router.hpp | 25 +-- .../nearest_edge_finder_tests.cpp | 15 +- routing/routing_tests/road_graph_builder.cpp | 68 ++++++- routing/routing_tests/road_graph_builder.hpp | 8 +- .../road_graph_nearest_edges_test.cpp | 16 +- 16 files changed, 372 insertions(+), 223 deletions(-) diff --git a/integration_tests/pedestrian_route_test.cpp b/integration_tests/pedestrian_route_test.cpp index 967ee10fea..f5b4a682a3 100644 --- a/integration_tests/pedestrian_route_test.cpp +++ b/integration_tests/pedestrian_route_test.cpp @@ -367,3 +367,19 @@ UNIT_TEST(USANewYorkEmpireStateBuildingToUnitedNations) MercatorBounds::FromLatLon(40.74844, -73.98566), {0., 0.}, MercatorBounds::FromLatLon(40.75047, -73.96759), 2265.); } + +UNIT_TEST(CrossMwmEgyptTabaToJordanAqaba) +{ + integration::CalculateRouteAndTestRouteLength( + integration::GetPedestrianComponents(), + MercatorBounds::FromLatLon(29.49271, 34.89571), {0., 0.}, + MercatorBounds::FromLatLon(29.52774, 35.00324), 29016); +} + +UNIT_TEST(CrossMwmRussiaPStaiToBelarusDrazdy) +{ + integration::CalculateRouteAndTestRouteLength( + integration::GetPedestrianComponents(), + MercatorBounds::FromLatLon(55.014, 30.95552), {0., 0.}, + MercatorBounds::FromLatLon(55.01437, 30.8858), 4834.5); +} diff --git a/integration_tests/routing_test_tools.cpp b/integration_tests/routing_test_tools.cpp index 292f5fe323..4d45a68d3f 100644 --- a/integration_tests/routing_test_tools.cpp +++ b/integration_tests/routing_test_tools.cpp @@ -96,16 +96,9 @@ namespace integration return osrmRouter; } - shared_ptr CreatePedestrianRouter(Index & index, search::Engine & searchEngine) + shared_ptr CreatePedestrianRouter(Index & index) { - auto const countryFileFn = [&searchEngine](m2::PointD const & pt) - { - return searchEngine.GetCountryFile(pt); - }; - - unique_ptr router = CreatePedestrianAStarBidirectionalRouter(index, - countryFileFn, - nullptr); + unique_ptr router = CreatePedestrianAStarBidirectionalRouter(index, nullptr); return shared_ptr(move(router)); } @@ -141,7 +134,7 @@ namespace integration PedestrianRouterComponents(vector const & localFiles) : m_featuresFetcher(CreateFeaturesFetcher(localFiles)), m_searchEngine(CreateSearchEngine(m_featuresFetcher)), - m_router(CreatePedestrianRouter(m_featuresFetcher->GetIndex(), *m_searchEngine.get())) + m_router(CreatePedestrianRouter(m_featuresFetcher->GetIndex())) { } IRouter * GetRouter() const override { return m_router.get(); } diff --git a/map/framework.cpp b/map/framework.cpp index 0d1b67dbca..870dc289af 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -2172,8 +2172,7 @@ void Framework::SetRouter(RouterType type) unique_ptr fetcher; if (type == RouterType::Pedestrian) { - router = CreatePedestrianAStarBidirectionalRouter(m_model.GetIndex(), countryFileGetter, - routingVisualizerFn); + router = CreatePedestrianAStarBidirectionalRouter(m_model.GetIndex(), routingVisualizerFn); } else { diff --git a/pedestrian_routing_benchmarks/pedestrian_routing_benchmarks.cpp b/pedestrian_routing_benchmarks/pedestrian_routing_benchmarks.cpp index 56ca1eed6c..0abd488c93 100644 --- a/pedestrian_routing_benchmarks/pedestrian_routing_benchmarks.cpp +++ b/pedestrian_routing_benchmarks/pedestrian_routing_benchmarks.cpp @@ -4,7 +4,6 @@ #include "indexer/classificator_loader.hpp" #include "routing/features_road_graph.hpp" -#include "routing/nearest_edge_finder.hpp" #include "routing/road_graph_router.hpp" #include "routing/route.hpp" #include "routing/pedestrian_model.hpp" @@ -58,21 +57,19 @@ private: shared_ptr const m_model; }; -unique_ptr CreatePedestrianAStarTestRouter( - Index & index, routing::TMwmFileByPointFn const & countryFileFn) +unique_ptr CreatePedestrianAStarTestRouter(Index & index) { unique_ptr vehicleModelFactory(new SimplifiedPedestrianModelFactory()); unique_ptr algorithm(new routing::AStarRoutingAlgorithm(nullptr)); - unique_ptr router(new routing::RoadGraphRouter("test-astar-pedestrian", index, move(vehicleModelFactory), move(algorithm), countryFileFn)); + unique_ptr router(new routing::RoadGraphRouter("test-astar-pedestrian", index, move(vehicleModelFactory), move(algorithm))); return router; } -unique_ptr CreatePedestrianAStarBidirectionalTestRouter( - Index & index, routing::TMwmFileByPointFn const & countryFileFn) +unique_ptr CreatePedestrianAStarBidirectionalTestRouter(Index & index) { unique_ptr vehicleModelFactory(new SimplifiedPedestrianModelFactory()); unique_ptr algorithm(new routing::AStarBidirectionalRoutingAlgorithm(nullptr)); - unique_ptr router(new routing::RoadGraphRouter("test-astar-bidirectional-pedestrian", index, move(vehicleModelFactory), move(algorithm), countryFileFn)); + unique_ptr router(new routing::RoadGraphRouter("test-astar-bidirectional-pedestrian", index, move(vehicleModelFactory), move(algorithm))); return router; } @@ -88,25 +85,10 @@ m2::PointD GetPointOnEdge(routing::Edge & e, double posAlong) void GetNearestPedestrianEdges(Index & index, m2::PointD const & pt, vector> & edges) { - MwmSet::MwmId const id = index.GetMwmIdByCountryFile(CountryFile(MAP_NAME)); - TEST(id.IsAlive(), ()); + unique_ptr vehicleModelFactory(new SimplifiedPedestrianModelFactory()); + routing::FeaturesRoadGraph roadGraph(index, move(vehicleModelFactory)); - routing::PedestrianModel const vehicleModel; - routing::FeaturesRoadGraph roadGraph(vehicleModel, index, id); - - routing::NearestEdgeFinder finder(pt, roadGraph); - - auto const f = [&finder, &vehicleModel](FeatureType & ft) - { - if (ft.GetFeatureType() == feature::GEOM_LINE && vehicleModel.GetSpeed(ft) > 0.0) - finder.AddInformationSource(ft.GetID().m_offset); - }; - - index.ForEachInRect( - f, MercatorBounds::RectByCenterXYAndSizeInMeters(pt, 100.0 /* radiusM */), - routing::FeaturesRoadGraph::GetStreetReadScale()); - - finder.MakeResult(edges, 1 /* maxCount */); + roadGraph.FindClosestEdges(pt, 1 /*count*/, edges); } void TestRouter(routing::IRouter & router, m2::PointD const & startPos, m2::PointD const & finalPos, routing::Route & foundRoute) @@ -126,16 +108,14 @@ void TestRouter(routing::IRouter & router, m2::PointD const & startPos, m2::Poin void TestRouters(Index & index, m2::PointD const & startPos, m2::PointD const & finalPos) { - auto const countryFileFn = [](m2::PointD const & /* point */) { return MAP_NAME; }; - // find route by A*-bidirectional algorithm routing::Route routeFoundByAstarBidirectional(""); - unique_ptr router = CreatePedestrianAStarBidirectionalTestRouter(index, countryFileFn); + unique_ptr router = CreatePedestrianAStarBidirectionalTestRouter(index); TestRouter(*router, startPos, finalPos, routeFoundByAstarBidirectional); // find route by A* algorithm routing::Route routeFoundByAstar(""); - router = CreatePedestrianAStarTestRouter(index, countryFileFn); + router = CreatePedestrianAStarTestRouter(index); TestRouter(*router, startPos, finalPos, routeFoundByAstar); double constexpr kEpsilon = 1e-6; diff --git a/routing/features_road_graph.cpp b/routing/features_road_graph.cpp index 412170717d..54d1df4cff 100644 --- a/routing/features_road_graph.cpp +++ b/routing/features_road_graph.cpp @@ -1,4 +1,5 @@ #include "routing/features_road_graph.hpp" +#include "routing/nearest_edge_finder.hpp" #include "routing/route.hpp" #include "routing/vehicle_model.hpp" @@ -10,6 +11,7 @@ #include "geometry/distance_on_sphere.hpp" #include "base/logging.hpp" +#include "base/macros.hpp" namespace routing { @@ -17,14 +19,95 @@ namespace routing namespace { uint32_t constexpr kPowOfTwoForFeatureCacheSize = 10; // cache contains 2 ^ kPowOfTwoForFeatureCacheSize elements + double constexpr kReadCrossEpsilon = 1.0E-4; + +double constexpr kMwmCrossingNodeEqualityRadiusMeters = 100.0; + +string GetFeatureCountryName(FeatureID const featureId) +{ + /// @todo Rework this function when storage will provide information about mwm's country + // MwmInfo.GetCountryName returns country name as 'Country' or 'Country_Region', but only 'Country' is needed + ASSERT(featureId.IsValid(), ()); + string countryName = featureId.m_mwmId.GetInfo()->GetCountryName(); + size_t const pos = countryName.find('_'); + if (string::npos == pos) + return countryName; + return countryName.substr(0, pos); +} + +MwmSet::MwmHandle GetMwmHandleByMwmId(Index & index, MwmSet::MwmId const & mwmId) +{ + /// @todo Rework this function when storage will provide appropriate function + ASSERT(mwmId.IsAlive(), ()); + string const countryName = mwmId.GetInfo()->GetCountryName(); + return index.GetMwmHandleByCountryFile(platform::CountryFile(countryName)); +} } // namespace -FeaturesRoadGraph::FeaturesRoadGraph(IVehicleModel const & vehicleModel, Index const & index, MwmSet::MwmId const & mwmID) + +FeaturesRoadGraph::CrossCountryVehicleModel::CrossCountryVehicleModel(unique_ptr && vehicleModelFactory) + : m_vehicleModelFactory(move(vehicleModelFactory)) + , m_maxSpeedKMPH(m_vehicleModelFactory->GetVehicleModel()->GetMaxSpeed()) +{ +} + +double FeaturesRoadGraph::CrossCountryVehicleModel::GetSpeed(FeatureType const & f) const +{ + return GetVehicleModel(f.GetID())->GetSpeed(f); +} + +double FeaturesRoadGraph::CrossCountryVehicleModel::GetMaxSpeed() const +{ + return m_maxSpeedKMPH; +} + +bool FeaturesRoadGraph::CrossCountryVehicleModel::IsOneWay(FeatureType const & f) const +{ + return GetVehicleModel(f.GetID())->IsOneWay(f); +} + +IVehicleModel * FeaturesRoadGraph::CrossCountryVehicleModel::GetVehicleModel(FeatureID const & featureId) const +{ + auto itr = m_cache.find(featureId.m_mwmId); + if (itr != m_cache.end()) + return itr->second.get(); + + string const country = GetFeatureCountryName(featureId); + auto const vehicleModel = m_vehicleModelFactory->GetVehicleModelForCountry(country); + + ASSERT(nullptr != vehicleModel, ()); + ASSERT_EQUAL(m_maxSpeedKMPH, vehicleModel->GetMaxSpeed(), ()); + + itr = m_cache.insert(make_pair(featureId.m_mwmId, move(vehicleModel))).first; + return itr->second.get(); +} + +void FeaturesRoadGraph::CrossCountryVehicleModel::Clear() +{ + m_cache.clear(); +} + + +IRoadGraph::RoadInfo & FeaturesRoadGraph::RoadInfoCache::Find(FeatureID const & featureId, bool & found) +{ + auto itr = m_cache.find(featureId.m_mwmId); + if (itr != m_cache.end()) + return itr->second->Find(featureId.m_offset, found); + + itr = m_cache.insert(make_pair(featureId.m_mwmId, make_unique(kPowOfTwoForFeatureCacheSize))).first; + return itr->second->Find(featureId.m_offset, found); +} + +void FeaturesRoadGraph::RoadInfoCache::Clear() +{ + m_cache.clear(); +} + + +FeaturesRoadGraph::FeaturesRoadGraph(Index & index, unique_ptr && vehicleModelFactory) : m_index(index), - m_mwmID(mwmID), - m_vehicleModel(vehicleModel), - m_cache(kPowOfTwoForFeatureCacheSize) + m_vehicleModel(move(vehicleModelFactory)) { } @@ -40,23 +123,18 @@ public: void operator()(FeatureType & ft) { - // check type to skip not line objects if (ft.GetFeatureType() != feature::GEOM_LINE) return; - // skip roads with null speed double const speedKMPH = m_graph.GetSpeedKMPHFromFt(ft); if (speedKMPH <= 0.0) return; - FeatureID const fID = ft.GetID(); - if (fID.m_mwmId != m_graph.GetMwmID()) - return; + FeatureID const featureId = ft.GetID(); - // load feature from cache - IRoadGraph::RoadInfo const & ri = m_graph.GetCachedRoadInfo(fID.m_offset, ft, speedKMPH); + IRoadGraph::RoadInfo const & roadInfo = m_graph.GetCachedRoadInfo(featureId, ft, speedKMPH); - m_edgesLoader(fID.m_offset, ri); + m_edgesLoader(featureId, roadInfo); } private: @@ -64,14 +142,14 @@ private: IRoadGraph::CrossEdgesLoader & m_edgesLoader; }; -IRoadGraph::RoadInfo FeaturesRoadGraph::GetRoadInfo(uint32_t featureId) const +IRoadGraph::RoadInfo FeaturesRoadGraph::GetRoadInfo(FeatureID const & featureId) const { RoadInfo const & ri = GetCachedRoadInfo(featureId); ASSERT_GREATER(ri.m_speedKMPH, 0.0, ()); return ri; } -double FeaturesRoadGraph::GetSpeedKMPH(uint32_t featureId) const +double FeaturesRoadGraph::GetSpeedKMPH(FeatureID const & featureId) const { double const speedKMPH = GetCachedRoadInfo(featureId).m_speedKMPH; ASSERT_GREATER(speedKMPH, 0.0, ()); @@ -90,7 +168,42 @@ void FeaturesRoadGraph::ForEachFeatureClosestToCross(m2::PointD const & cross, m_index.ForEachInRect(featuresLoader, m2::RectD(cross.x - kReadCrossEpsilon, cross.y - kReadCrossEpsilon, cross.x + kReadCrossEpsilon, cross.y + kReadCrossEpsilon), - scales::GetUpperScale()); + GetStreetReadScale()); +} + +void FeaturesRoadGraph::FindClosestEdges(m2::PointD const & point, uint32_t count, + vector> & vicinities) const +{ + NearestEdgeFinder finder(point); + + auto const f = [&finder, this](FeatureType & ft) + { + if (ft.GetFeatureType() != feature::GEOM_LINE) + return; + + double const speedKMPH = m_vehicleModel.GetSpeed(ft); + if (speedKMPH <= 0.0) + return; + + FeatureID const featureId = ft.GetID(); + + IRoadGraph::RoadInfo const & roadInfo = GetCachedRoadInfo(featureId, ft, speedKMPH); + + finder.AddInformationSource(featureId, roadInfo); + }; + + m_index.ForEachInRect( + f, MercatorBounds::RectByCenterXYAndSizeInMeters(point, kMwmCrossingNodeEqualityRadiusMeters), + GetStreetReadScale()); + + finder.MakeResult(vicinities, count); +} + +void FeaturesRoadGraph::ClearState() +{ + m_cache.Clear(); + m_vehicleModel.Clear(); + m_mwmLocks.clear(); } bool FeaturesRoadGraph::IsOneWay(FeatureType const & ft) const @@ -103,7 +216,7 @@ double FeaturesRoadGraph::GetSpeedKMPHFromFt(FeatureType const & ft) const return m_vehicleModel.GetSpeed(ft); } -IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featureId) const +IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(FeatureID const & featureId) const { bool found = false; RoadInfo & ri = m_cache.Find(featureId, found); @@ -112,9 +225,9 @@ IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featu return ri; FeatureType ft; - Index::FeaturesLoaderGuard loader(m_index, m_mwmID); - loader.GetFeature(featureId, ft); - ASSERT_EQUAL(ft.GetFeatureType(), feature::GEOM_LINE, (featureId)); + Index::FeaturesLoaderGuard loader(m_index, featureId.m_mwmId); + loader.GetFeature(featureId.m_offset, ft); + ASSERT_EQUAL(ft.GetFeatureType(), feature::GEOM_LINE, ()); ft.ParseGeometry(FeatureType::BEST_GEOMETRY); @@ -122,10 +235,12 @@ IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featu ri.m_speedKMPH = GetSpeedKMPHFromFt(ft); ft.SwapPoints(ri.m_points); + LockFeatureMwm(featureId); + return ri; } -IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featureId, +IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(FeatureID const & featureId, FeatureType & ft, double speedKMPH) const { @@ -136,7 +251,7 @@ IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featu return ri; // ft must be set - ASSERT_EQUAL(featureId, ft.GetID().m_offset, ()); + ASSERT_EQUAL(featureId, ft.GetID(), ()); ft.ParseGeometry(FeatureType::BEST_GEOMETRY); @@ -144,7 +259,24 @@ IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featu ri.m_speedKMPH = speedKMPH; ft.SwapPoints(ri.m_points); + LockFeatureMwm(featureId); + return ri; } +void FeaturesRoadGraph::LockFeatureMwm(FeatureID const & featureId) const +{ + MwmSet::MwmId mwmId = featureId.m_mwmId; + ASSERT(mwmId.IsAlive(), ()); + + auto const itr = m_mwmLocks.find(mwmId); + if (itr != m_mwmLocks.end()) + return; + + MwmSet::MwmHandle mwmHandle = GetMwmHandleByMwmId(m_index, mwmId); + ASSERT(mwmHandle.IsAlive(), ()); + + m_mwmLocks.insert(make_pair(move(mwmId), move(mwmHandle))); +} + } // namespace routing diff --git a/routing/features_road_graph.hpp b/routing/features_road_graph.hpp index 6439f322f6..2e94b5871c 100644 --- a/routing/features_road_graph.hpp +++ b/routing/features_road_graph.hpp @@ -9,6 +9,7 @@ #include "base/cache.hpp" +#include "std/map.hpp" #include "std/unique_ptr.hpp" #include "std/vector.hpp" @@ -20,22 +21,54 @@ namespace routing class FeaturesRoadGraph : public IRoadGraph { +private: + class CrossCountryVehicleModel : public IVehicleModel + { + public: + CrossCountryVehicleModel(unique_ptr && vehicleModelFactory); + + // IVehicleModel overrides: + double GetSpeed(FeatureType const & f) const override; + double GetMaxSpeed() const override; + bool IsOneWay(FeatureType const & f) const override; + + void Clear(); + + private: + IVehicleModel * GetVehicleModel(FeatureID const & featureId) const; + + unique_ptr const m_vehicleModelFactory; + double const m_maxSpeedKMPH; + + mutable map> m_cache; + }; + + class RoadInfoCache + { + public: + RoadInfo & Find(FeatureID const & featureId, bool & found); + + void Clear(); + + private: + using TMwmFeatureCache = my::Cache; + map> m_cache; + }; + public: - FeaturesRoadGraph(IVehicleModel const & vehicleModel, Index const & index, MwmSet::MwmId const & mwmID); + FeaturesRoadGraph(Index & index, unique_ptr && vehicleModelFactory); static uint32_t GetStreetReadScale(); - inline MwmSet::MwmId const & GetMwmID() const { return m_mwmID; } - - double GetCacheMiss() const { return m_cache.GetCacheMiss(); } - -protected: // IRoadGraph overrides: - RoadInfo GetRoadInfo(uint32_t featureId) const override; - double GetSpeedKMPH(uint32_t featureId) const override; + RoadInfo GetRoadInfo(FeatureID const & featureId) const override; + double GetSpeedKMPH(FeatureID const & featureId) const override; double GetMaxSpeedKMPH() const override; void ForEachFeatureClosestToCross(m2::PointD const & cross, CrossEdgesLoader & edgesLoader) const override; + void FindClosestEdges(m2::PointD const & point, uint32_t count, + vector> & vicinities) const override; + void ClearState() override; private: friend class CrossFeaturesLoader; @@ -45,18 +78,19 @@ private: // Searches a feature RoadInfo in the cache, and if does not find then // loads feature from the index and takes speed for the feature from the vehicle model. - RoadInfo const & GetCachedRoadInfo(uint32_t featureId) const; + RoadInfo const & GetCachedRoadInfo(FeatureID const & featureId) const; // Searches a feature RoadInfo in the cache, and if does not find then takes passed feature and speed. // This version is used to prevent redundant feature loading when feature speed is known. - RoadInfo const & GetCachedRoadInfo(uint32_t featureId, + RoadInfo const & GetCachedRoadInfo(FeatureID const & featureId, FeatureType & ft, double speedKMPH) const; - Index const & m_index; - MwmSet::MwmId const m_mwmID; - IVehicleModel const & m_vehicleModel; - - mutable my::CacheWithStat m_cache; + void LockFeatureMwm(FeatureID const & featureId) const; + + Index & m_index; + mutable RoadInfoCache m_cache; + mutable CrossCountryVehicleModel m_vehicleModel; + mutable map m_mwmLocks; }; } // namespace routing diff --git a/routing/nearest_edge_finder.cpp b/routing/nearest_edge_finder.cpp index 8b7faa00ad..cf59419d9d 100644 --- a/routing/nearest_edge_finder.cpp +++ b/routing/nearest_edge_finder.cpp @@ -6,6 +6,8 @@ #include "base/assert.hpp" +#include "std/limits.hpp" + namespace routing { @@ -14,28 +16,26 @@ NearestEdgeFinder::Candidate::Candidate() m_segId(0), m_segStart(m2::PointD::Zero()), m_segEnd(m2::PointD::Zero()), - m_point(m2::PointD::Zero()), - m_fid(INVALID_FID) + m_point(m2::PointD::Zero()) { } -NearestEdgeFinder::NearestEdgeFinder(m2::PointD const & point, IRoadGraph & roadGraph) - : m_point(point), m_roadGraph(roadGraph) +NearestEdgeFinder::NearestEdgeFinder(m2::PointD const & point) + : m_point(point) { } -void NearestEdgeFinder::AddInformationSource(uint32_t featureId) +void NearestEdgeFinder::AddInformationSource(FeatureID const & featureId, IRoadGraph::RoadInfo const & roadInfo) { Candidate res; - IRoadGraph::RoadInfo info = m_roadGraph.GetRoadInfo(featureId); - size_t const count = info.m_points.size(); + size_t const count = roadInfo.m_points.size(); ASSERT_GREATER(count, 1, ()); for (size_t i = 1; i < count; ++i) { /// @todo Probably, we need to get exact projection distance in meters. m2::ProjectionToSection segProj; - segProj.SetBounds(info.m_points[i - 1], info.m_points[i]); + segProj.SetBounds(roadInfo.m_points[i - 1], roadInfo.m_points[i]); m2::PointD const pt = segProj(m_point); double const d = m_point.SquareLength(pt); @@ -44,13 +44,13 @@ void NearestEdgeFinder::AddInformationSource(uint32_t featureId) res.m_dist = d; res.m_fid = featureId; res.m_segId = i - 1; - res.m_segStart = info.m_points[i - 1]; - res.m_segEnd = info.m_points[i]; + res.m_segStart = roadInfo.m_points[i - 1]; + res.m_segEnd = roadInfo.m_points[i]; res.m_point = pt; } } - if (res.IsValid()) + if (res.m_fid.IsValid()) m_candidates.push_back(res); } diff --git a/routing/nearest_edge_finder.hpp b/routing/nearest_edge_finder.hpp index f415de6a93..6a9ad2a773 100644 --- a/routing/nearest_edge_finder.hpp +++ b/routing/nearest_edge_finder.hpp @@ -5,10 +5,10 @@ #include "geometry/point2d.hpp" +#include "indexer/feature_decl.hpp" #include "indexer/index.hpp" #include "indexer/mwm_set.hpp" -#include "std/limits.hpp" #include "std/unique_ptr.hpp" #include "std/utility.hpp" #include "std/vector.hpp" @@ -20,8 +20,6 @@ namespace routing /// Class returns pairs of outgoing edge and projection point on the edge class NearestEdgeFinder { - static constexpr uint32_t INVALID_FID = numeric_limits::max(); - struct Candidate { double m_dist; @@ -29,23 +27,20 @@ class NearestEdgeFinder m2::PointD m_segStart; m2::PointD m_segEnd; m2::PointD m_point; - uint32_t m_fid; + FeatureID m_fid; Candidate(); - - inline bool IsValid() const { return m_fid != INVALID_FID; } }; m2::PointD const m_point; - IRoadGraph & m_roadGraph; vector m_candidates; public: - NearestEdgeFinder(m2::PointD const & point, IRoadGraph & roadGraph); + NearestEdgeFinder(m2::PointD const & point); inline bool HasCandidates() const { return !m_candidates.empty(); } - void AddInformationSource(uint32_t featureId); + void AddInformationSource(FeatureID const & featureId, IRoadGraph::RoadInfo const & roadInfo); void MakeResult(vector> & res, size_t const maxCountFeatures); }; diff --git a/routing/road_graph.cpp b/routing/road_graph.cpp index a8c01dd522..0038426908 100644 --- a/routing/road_graph.cpp +++ b/routing/road_graph.cpp @@ -86,10 +86,10 @@ string DebugPrint(Junction const & r) Edge Edge::MakeFake(Junction const & startJunction, Junction const & endJunction) { - return Edge(kFakeFeatureId, true /* forward */, 0 /* segId */, startJunction, endJunction); + return Edge(FeatureID(), true /* forward */, 0 /* segId */, startJunction, endJunction); } -Edge::Edge(uint32_t featureId, bool forward, size_t segId, Junction const & startJunction, Junction const & endJunction) +Edge::Edge(FeatureID featureId, bool forward, uint32_t segId, Junction const & startJunction, Junction const & endJunction) : m_featureId(featureId), m_forward(forward), m_segId(segId), m_startJunction(startJunction), m_endJunction(endJunction) { ASSERT_LESS(segId, numeric_limits::max(), ()); @@ -134,7 +134,7 @@ bool Edge::operator<(Edge const & r) const string DebugPrint(Edge const & r) { ostringstream ss; - ss << "Edge{featureId: " << r.GetFeatureId() << ", isForward:" << r.IsForward() + ss << "Edge{featureId: " << DebugPrint(r.GetFeatureId()) << ", isForward:" << r.IsForward() << ", segId:" << r.m_segId << ", startJunction:" << DebugPrint(r.m_startJunction) << ", endJunction:" << DebugPrint(r.m_endJunction) << "}"; return ss.str(); @@ -163,7 +163,7 @@ IRoadGraph::CrossEdgesLoader::CrossEdgesLoader(m2::PointD const & cross, TEdgeVe { } -void IRoadGraph::CrossEdgesLoader::operator()(uint32_t featureId, RoadInfo const & roadInfo) +void IRoadGraph::CrossEdgesLoader::operator()(FeatureID const & featureId, RoadInfo const & roadInfo) { size_t const numPoints = roadInfo.m_points.size(); diff --git a/routing/road_graph.hpp b/routing/road_graph.hpp index 1de8844f71..efd55e661b 100644 --- a/routing/road_graph.hpp +++ b/routing/road_graph.hpp @@ -4,8 +4,9 @@ #include "base/string_utils.hpp" +#include "indexer/feature_data.hpp" + #include "std/initializer_list.hpp" -#include "std/limits.hpp" #include "std/map.hpp" #include "std/vector.hpp" @@ -38,22 +39,19 @@ private: /// The Edge class represents an edge description on a road network graph class Edge { -private: - static constexpr uint32_t kFakeFeatureId = numeric_limits::max(); - public: static Edge MakeFake(Junction const & startJunction, Junction const & endJunction); - Edge(uint32_t featureId, bool forward, size_t segId, Junction const & startJunction, Junction const & endJunction); + Edge(FeatureID featureId, bool forward, uint32_t segId, Junction const & startJunction, Junction const & endJunction); Edge(Edge const &) = default; Edge & operator=(Edge const &) = default; - inline uint32_t GetFeatureId() const { return m_featureId; } + inline FeatureID GetFeatureId() const { return m_featureId; } inline bool IsForward() const { return m_forward; } inline uint32_t GetSegId() const { return m_segId; } inline Junction const & GetStartJunction() const { return m_startJunction; } inline Junction const & GetEndJunction() const { return m_endJunction; } - inline bool IsFake() const { return m_featureId == kFakeFeatureId; } + inline bool IsFake() const { return !m_featureId.IsValid(); } Edge GetReverseEdge() const; @@ -65,8 +63,8 @@ public: private: friend string DebugPrint(Edge const & r); - // Feature on which position is defined. - uint32_t m_featureId; + // Feature for which edge is defined. + FeatureID m_featureId; // Is the feature along the road. bool m_forward; @@ -109,7 +107,7 @@ public: public: CrossEdgesLoader(m2::PointD const & cross, TEdgeVector & outgoingEdges); - void operator()(uint32_t featureId, RoadInfo const & roadInfo); + void operator()(FeatureID const & featureId, RoadInfo const & roadInfo); private: m2::PointD const m_cross; @@ -135,10 +133,10 @@ public: void AddFakeEdges(Junction const & junction, vector> const & vicinities); /// Returns RoadInfo for a road corresponding to featureId. - virtual RoadInfo GetRoadInfo(uint32_t featureId) const = 0; + virtual RoadInfo GetRoadInfo(FeatureID const & featureId) const = 0; /// Returns speed in KM/H for a road corresponding to featureId. - virtual double GetSpeedKMPH(uint32_t featureId) const = 0; + virtual double GetSpeedKMPH(FeatureID const & featureId) const = 0; /// Returns speed in KM/H for a road corresponding to edge. double GetSpeedKMPH(Edge const & edge) const; @@ -150,6 +148,15 @@ public: virtual void ForEachFeatureClosestToCross(m2::PointD const & cross, CrossEdgesLoader & edgesLoader) const = 0; + /// Finds the closest edges to the point. + /// @return Array of pairs of Edge and projection point on the Edge. If there is no the closest edges + /// then returns empty array. + virtual void FindClosestEdges(m2::PointD const & point, uint32_t count, + vector> & vicinities) const = 0; + + /// Clear all temporary buffers. + virtual void ClearState() {} + private: /// Finds all outgoing regular (non-fake) edges for junction. void GetRegularOutgoingEdges(Junction const & junction, TEdgeVector & edges) const; diff --git a/routing/road_graph_router.cpp b/routing/road_graph_router.cpp index d64d118bea..7843226de7 100644 --- a/routing/road_graph_router.cpp +++ b/routing/road_graph_router.cpp @@ -27,8 +27,6 @@ namespace // eliminates) this risk. size_t const MAX_ROAD_CANDIDATES = 1; -double const kMwmCrossingNodeEqualityRadiusMeters = 100.0; - IRouter::ResultCode Convert(IRoutingAlgorithm::Result value) { switch (value) @@ -40,88 +38,37 @@ IRouter::ResultCode Convert(IRoutingAlgorithm::Result value) ASSERT(false, ("Unexpected IRoutingAlgorithm::Result value:", value)); return IRouter::ResultCode::RouteNotFound; } - -string GetCountryForMwmFile(string const & mwmName) -{ - /// @todo Rework this function when storage will provide information about mwm's country - // We assume following schemes: - // Country or Country_Region - size_t const pos = mwmName.find('_'); - if (string::npos == pos) - return mwmName; - return mwmName.substr(0, pos); -} } // namespace RoadGraphRouter::~RoadGraphRouter() {} RoadGraphRouter::RoadGraphRouter(string const & name, Index & index, unique_ptr && vehicleModelFactory, - unique_ptr && algorithm, - TMwmFileByPointFn const & countryFileFn) - : m_name(name), - m_index(index), - m_vehicleModelFactory(move(vehicleModelFactory)), - m_algorithm(move(algorithm)), - m_countryFileFn(countryFileFn) + unique_ptr && algorithm) + : m_name(name) + , m_algorithm(move(algorithm)) + , m_roadGraph(make_unique(index, move(vehicleModelFactory))) { } -void RoadGraphRouter::GetClosestEdges(m2::PointD const & pt, vector> & edges) +void RoadGraphRouter::ClearState() { - NearestEdgeFinder finder(pt, *m_roadGraph.get()); - - auto const f = [&finder, this](FeatureType & ft) - { - if (ft.GetFeatureType() == feature::GEOM_LINE && m_vehicleModel->GetSpeed(ft) > 0.0) - finder.AddInformationSource(ft.GetID().m_offset); - }; - - m_index.ForEachInRect( - f, MercatorBounds::RectByCenterXYAndSizeInMeters(pt, kMwmCrossingNodeEqualityRadiusMeters), - FeaturesRoadGraph::GetStreetReadScale()); - - finder.MakeResult(edges, MAX_ROAD_CANDIDATES); -} - -bool RoadGraphRouter::IsMyMWM(MwmSet::MwmId const & mwmID) const -{ - return m_roadGraph && - dynamic_cast(m_roadGraph.get())->GetMwmID() == mwmID; + m_algorithm->Reset(); + m_roadGraph->ClearState(); } IRouter::ResultCode RoadGraphRouter::CalculateRoute(m2::PointD const & startPoint, m2::PointD const & /* startDirection */, m2::PointD const & finalPoint, Route & route) { - // Despite adding fake notes and calculating their vicinities on the fly - // we still need to check that startPoint and finalPoint are in the same MWM - // and probably reset the graph. So the checks stay here. - - string const mwmName = m_countryFileFn(finalPoint); - if (m_countryFileFn(startPoint) != mwmName) - return PointsInDifferentMWM; - - platform::CountryFile countryFile(mwmName); - MwmSet::MwmHandle const mwmHandle = m_index.GetMwmHandleByCountryFile(countryFile); - if (!mwmHandle.IsAlive()) - return RouteFileNotExist; - - MwmSet::MwmId const mwmID = mwmHandle.GetId(); - if (!IsMyMWM(mwmID)) - { - m_vehicleModel = m_vehicleModelFactory->GetVehicleModelForCountry(GetCountryForMwmFile(mwmName)); - m_roadGraph.reset(new FeaturesRoadGraph(*m_vehicleModel.get(), m_index, mwmID)); - } - vector> finalVicinity; - GetClosestEdges(finalPoint, finalVicinity); + m_roadGraph->FindClosestEdges(finalPoint, MAX_ROAD_CANDIDATES, finalVicinity); if (finalVicinity.empty()) return EndPointNotFound; vector> startVicinity; - GetClosestEdges(startPoint, startVicinity); + m_roadGraph->FindClosestEdges(startPoint, MAX_ROAD_CANDIDATES, startVicinity); if (startVicinity.empty()) return StartPointNotFound; @@ -149,22 +96,20 @@ IRouter::ResultCode RoadGraphRouter::CalculateRoute(m2::PointD const & startPoin } unique_ptr CreatePedestrianAStarRouter(Index & index, - TMwmFileByPointFn const & countryFileFn, TRoutingVisualizerFn const & visualizerFn) { unique_ptr vehicleModelFactory(new PedestrianModelFactory()); unique_ptr algorithm(new AStarRoutingAlgorithm(visualizerFn)); - unique_ptr router(new RoadGraphRouter("astar-pedestrian", index, move(vehicleModelFactory), move(algorithm), countryFileFn)); + unique_ptr router(new RoadGraphRouter("astar-pedestrian", index, move(vehicleModelFactory), move(algorithm))); return router; } -unique_ptr CreatePedestrianAStarBidirectionalRouter( - Index & index, TMwmFileByPointFn const & countryFileFn, - TRoutingVisualizerFn const & visualizerFn) +unique_ptr CreatePedestrianAStarBidirectionalRouter(Index & index, + TRoutingVisualizerFn const & visualizerFn) { unique_ptr vehicleModelFactory(new PedestrianModelFactory()); unique_ptr algorithm(new AStarBidirectionalRoutingAlgorithm(visualizerFn)); - unique_ptr router(new RoadGraphRouter("astar-bidirectional-pedestrian", index, move(vehicleModelFactory), move(algorithm), countryFileFn)); + unique_ptr router(new RoadGraphRouter("astar-bidirectional-pedestrian", index, move(vehicleModelFactory), move(algorithm))); return router; } diff --git a/routing/road_graph_router.hpp b/routing/road_graph_router.hpp index 8e4905ff13..bf94bc76a8 100644 --- a/routing/road_graph_router.hpp +++ b/routing/road_graph_router.hpp @@ -26,13 +26,12 @@ class RoadGraphRouter : public IRouter public: RoadGraphRouter(string const & name, Index & index, unique_ptr && vehicleModelFactory, - unique_ptr && algorithm, - TMwmFileByPointFn const & countryFileFn); + unique_ptr && algorithm); ~RoadGraphRouter() override; // IRouter overrides: string GetName() const override { return m_name; } - void ClearState() override { Reset(); } + void ClearState() override; ResultCode CalculateRoute(m2::PointD const & startPoint, m2::PointD const & startDirection, m2::PointD const & finalPoint, Route & route) override; @@ -42,26 +41,14 @@ public: bool IsCancelled() const override { return m_algorithm->IsCancelled(); } private: - /// @todo This method fits better in features_road_graph. - void GetClosestEdges(m2::PointD const & pt, vector> & edges); - - bool IsMyMWM(MwmSet::MwmId const & mwmID) const; - string const m_name; - Index & m_index; - unique_ptr const m_vehicleModelFactory; unique_ptr const m_algorithm; - TMwmFileByPointFn const m_countryFileFn; - - unique_ptr m_roadGraph; - shared_ptr m_vehicleModel; + unique_ptr const m_roadGraph; }; - + unique_ptr CreatePedestrianAStarRouter(Index & index, - TMwmFileByPointFn const & countryFileFn, TRoutingVisualizerFn const & visualizerFn); -unique_ptr CreatePedestrianAStarBidirectionalRouter( - Index & index, TMwmFileByPointFn const & countryFileFn, - TRoutingVisualizerFn const & visualizerFn); +unique_ptr CreatePedestrianAStarBidirectionalRouter(Index & index, + TRoutingVisualizerFn const & visualizerFn); } // namespace routing diff --git a/routing/routing_tests/nearest_edge_finder_tests.cpp b/routing/routing_tests/nearest_edge_finder_tests.cpp index 94c1f9b3bb..0a90d400bb 100644 --- a/routing/routing_tests/nearest_edge_finder_tests.cpp +++ b/routing/routing_tests/nearest_edge_finder_tests.cpp @@ -13,9 +13,12 @@ void TestNearestOnMock1(m2::PointD const & point, size_t const candidatesCount, unique_ptr graph(new RoadGraphMockSource()); InitRoadGraphMockSourceWithTest1(*graph); - NearestEdgeFinder finder(point, *graph.get()); + NearestEdgeFinder finder(point); for (size_t i = 0; i < graph->GetRoadCount(); ++i) - finder.AddInformationSource(i); + { + FeatureID const featureId = MakeTestFeatureID(i); + finder.AddInformationSource(featureId, graph->GetRoadInfo(featureId)); + } vector> result; TEST(finder.HasCandidates(), ()); @@ -28,7 +31,7 @@ UNIT_TEST(StarterPosAtBorder_Mock1Graph) { vector> const expected = { - make_pair(Edge(0, true /* forward */, 0, m2::PointD(0, 0), m2::PointD(5, 0)), m2::PointD(0, 0)) + make_pair(Edge(MakeTestFeatureID(0), true /* forward */, 0, m2::PointD(0, 0), m2::PointD(5, 0)), m2::PointD(0, 0)) }; TestNearestOnMock1(m2::PointD(0, 0), 1, expected); } @@ -37,7 +40,7 @@ UNIT_TEST(MiddleEdgeTest_Mock1Graph) { vector> const expected = { - make_pair(Edge(0, true /* forward */, 0, m2::PointD(0, 0), m2::PointD(5, 0)), m2::PointD(3, 0)) + make_pair(Edge(MakeTestFeatureID(0), true /* forward */, 0, m2::PointD(0, 0), m2::PointD(5, 0)), m2::PointD(3, 0)) }; TestNearestOnMock1(m2::PointD(3, 3), 1, expected); } @@ -46,8 +49,8 @@ UNIT_TEST(MiddleSegmentTest_Mock1Graph) { vector> const expected = { - make_pair(Edge(0, true /* forward */, 2, m2::PointD(10, 0), m2::PointD(15, 0)), m2::PointD(12.5, 0)), - make_pair(Edge(1, true /* forward */, 2, m2::PointD(10, 0), m2::PointD(10, 5)), m2::PointD(10, 2.5)) + make_pair(Edge(MakeTestFeatureID(0), true /* forward */, 2, m2::PointD(10, 0), m2::PointD(15, 0)), m2::PointD(12.5, 0)), + make_pair(Edge(MakeTestFeatureID(1), true /* forward */, 2, m2::PointD(10, 0), m2::PointD(10, 5)), m2::PointD(10, 2.5)) }; TestNearestOnMock1(m2::PointD(12.5, 2.5), 2, expected); } diff --git a/routing/routing_tests/road_graph_builder.cpp b/routing/routing_tests/road_graph_builder.cpp index 2873849b1e..8063c2c551 100644 --- a/routing/routing_tests/road_graph_builder.cpp +++ b/routing/routing_tests/road_graph_builder.cpp @@ -1,8 +1,12 @@ #include "road_graph_builder.hpp" +#include "base/macros.hpp" #include "base/logging.hpp" +#include "indexer/mwm_set.hpp" + #include "std/algorithm.hpp" +#include "std/shared_ptr.hpp" using namespace routing; @@ -11,6 +15,42 @@ namespace double const MAX_SPEED_KMPH = 5.0; +/// Class provides a valid instance of FeatureID for testing purposes +class TestValidFeatureIDProvider : private MwmSet +{ +public: + TestValidFeatureIDProvider() + { + UNUSED_VALUE(Register(platform::LocalCountryFile::MakeForTesting("0"))); + + vector> mwmsInfoList; + GetMwmsInfo(mwmsInfoList); + + m_mwmInfo = mwmsInfoList[0]; + } + + FeatureID MakeFeatureID(uint32_t offset) const + { + return FeatureID(MwmSet::MwmId(m_mwmInfo), offset); + } + +private: + // MwmSet overrides: + bool GetVersion(platform::LocalCountryFile const & localFile, MwmInfo & info) const override + { + info.m_maxScale = 1; + info.m_limitRect = m2::RectD(0, 0, 1, 1); + info.m_version.format = version::lastFormat; + return true; + } + TMwmValueBasePtr CreateValue(platform::LocalCountryFile const &) const override + { + return TMwmValueBasePtr(new MwmValueBase()); + } + + shared_ptr m_mwmInfo; +}; + } // namespace namespace routing_test @@ -22,16 +62,16 @@ void RoadGraphMockSource::AddRoad(RoadInfo && ri) m_roads.push_back(move(ri)); } -IRoadGraph::RoadInfo RoadGraphMockSource::GetRoadInfo(uint32_t featureId) const +IRoadGraph::RoadInfo RoadGraphMockSource::GetRoadInfo(FeatureID const & featureId) const { - CHECK_LESS(featureId, m_roads.size(), ("Invalid feature id.")); - return m_roads[featureId]; + CHECK_LESS(featureId.m_offset, m_roads.size(), ("Invalid feature id.")); + return m_roads[featureId.m_offset]; } -double RoadGraphMockSource::GetSpeedKMPH(uint32_t featureId) const +double RoadGraphMockSource::GetSpeedKMPH(FeatureID const & featureId) const { - CHECK_LESS(featureId, m_roads.size(), ("Invalid feature id.")); - return m_roads[featureId].m_speedKMPH; + CHECK_LESS(featureId.m_offset, m_roads.size(), ("Invalid feature id.")); + return m_roads[featureId.m_offset].m_speedKMPH; } double RoadGraphMockSource::GetMaxSpeedKMPH() const @@ -43,7 +83,21 @@ void RoadGraphMockSource::ForEachFeatureClosestToCross(m2::PointD const & /* cro CrossEdgesLoader & edgesLoader) const { for (size_t roadId = 0; roadId < m_roads.size(); ++roadId) - edgesLoader(roadId, m_roads[roadId]); + edgesLoader(MakeTestFeatureID(roadId), m_roads[roadId]); +} + +void RoadGraphMockSource::FindClosestEdges(m2::PointD const & point, uint32_t count, + vector> & vicinities) const +{ + UNUSED_VALUE(point); + UNUSED_VALUE(count); + UNUSED_VALUE(vicinities); +} + +FeatureID MakeTestFeatureID(uint32_t offset) +{ + static TestValidFeatureIDProvider instance; + return instance.MakeFeatureID(offset); } void InitRoadGraphMockSourceWithTest1(RoadGraphMockSource & src) diff --git a/routing/routing_tests/road_graph_builder.hpp b/routing/routing_tests/road_graph_builder.hpp index 9abf9a5368..33119f69e2 100644 --- a/routing/routing_tests/road_graph_builder.hpp +++ b/routing/routing_tests/road_graph_builder.hpp @@ -13,16 +13,20 @@ public: inline size_t GetRoadCount() const { return m_roads.size(); } // routing::IRoadGraph overrides: - RoadInfo GetRoadInfo(uint32_t featureId) const override; - double GetSpeedKMPH(uint32_t featureId) const override; + RoadInfo GetRoadInfo(FeatureID const & featureId) const override; + double GetSpeedKMPH(FeatureID const & featureId) const override; double GetMaxSpeedKMPH() const override; void ForEachFeatureClosestToCross(m2::PointD const & cross, CrossEdgesLoader & edgeLoader) const override; + void FindClosestEdges(m2::PointD const & point, uint32_t count, + vector> & vicinities) const override; private: vector m_roads; }; +FeatureID MakeTestFeatureID(uint32_t offset); + void InitRoadGraphMockSourceWithTest1(RoadGraphMockSource & graphMock); void InitRoadGraphMockSourceWithTest2(RoadGraphMockSource & graphMock); diff --git a/routing/routing_tests/road_graph_nearest_edges_test.cpp b/routing/routing_tests/road_graph_nearest_edges_test.cpp index b4e8d72ed4..e1f3424acf 100644 --- a/routing/routing_tests/road_graph_nearest_edges_test.cpp +++ b/routing/routing_tests/road_graph_nearest_edges_test.cpp @@ -57,20 +57,20 @@ UNIT_TEST(RoadGraph_NearestEdges) // Expected outgoing edges. IRoadGraph::TEdgeVector expectedOutgoing = { - Edge(0 /* first road */, false /* forward */, 1 /* segId */, m2::PointD(0, 0), m2::PointD(-1, 0)), - Edge(0 /* first road */, true /* forward */, 2 /* segId */, m2::PointD(0, 0), m2::PointD(1, 0)), - Edge(1 /* second road */, false /* forward */, 1 /* segId */, m2::PointD(0, 0), m2::PointD(0, -1)), - Edge(1 /* second road */, true /* forward */, 2 /* segId */, m2::PointD(0, 0), m2::PointD(0, 1)), + Edge(MakeTestFeatureID(0) /* first road */, false /* forward */, 1 /* segId */, m2::PointD(0, 0), m2::PointD(-1, 0)), + Edge(MakeTestFeatureID(0) /* first road */, true /* forward */, 2 /* segId */, m2::PointD(0, 0), m2::PointD(1, 0)), + Edge(MakeTestFeatureID(1) /* second road */, false /* forward */, 1 /* segId */, m2::PointD(0, 0), m2::PointD(0, -1)), + Edge(MakeTestFeatureID(1) /* second road */, true /* forward */, 2 /* segId */, m2::PointD(0, 0), m2::PointD(0, 1)), }; sort(expectedOutgoing.begin(), expectedOutgoing.end()); // Expected ingoing edges. IRoadGraph::TEdgeVector expectedIngoing = { - Edge(0 /* first road */, true /* forward */, 1 /* segId */, m2::PointD(-1, 0), m2::PointD(0, 0)), - Edge(0 /* first road */, false /* forward */, 2 /* segId */, m2::PointD(1, 0), m2::PointD(0, 0)), - Edge(1 /* second road */, true /* forward */, 1 /* segId */, m2::PointD(0, -1), m2::PointD(0, 0)), - Edge(1 /* second road */, false /* forward */, 2 /* segId */, m2::PointD(0, 1), m2::PointD(0, 0)), + Edge(MakeTestFeatureID(0) /* first road */, true /* forward */, 1 /* segId */, m2::PointD(-1, 0), m2::PointD(0, 0)), + Edge(MakeTestFeatureID(0) /* first road */, false /* forward */, 2 /* segId */, m2::PointD(1, 0), m2::PointD(0, 0)), + Edge(MakeTestFeatureID(1) /* second road */, true /* forward */, 1 /* segId */, m2::PointD(0, -1), m2::PointD(0, 0)), + Edge(MakeTestFeatureID(1) /* second road */, false /* forward */, 2 /* segId */, m2::PointD(0, 1), m2::PointD(0, 0)), }; sort(expectedIngoing.begin(), expectedIngoing.end());