diff --git a/routing/features_road_graph.cpp b/routing/features_road_graph.cpp index e0a1fd3383..9bef97fbe9 100644 --- a/routing/features_road_graph.cpp +++ b/routing/features_road_graph.cpp @@ -212,10 +212,10 @@ vector FeaturesRoadGraph::FindRoads( // DataSource::ForEachInRect() gives not ony features inside |rect| but some other features // which lie close to the rect. Removes all the features which don't cross |rect|. auto const & roadInfo = GetCachedRoadInfo(featureId, ft, kInvalidSpeedKMPH); - if (!PolylineInRect(roadInfo.m_junctions, rect)) + if (!RectCoversPolyline(roadInfo.m_junctions, rect)) return; - roads.emplace_back(featureId, roadInfo, true /* m_isRoadAccordingToModel */); + roads.emplace_back(featureId, roadInfo); }; m_dataSource.ForEachInRect(f, rect, GetStreetReadScale()); diff --git a/routing/index_router.cpp b/routing/index_router.cpp index e234878f85..66c554f41b 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -881,10 +881,10 @@ bool IndexRouter::IsFencedOff(m2::PointD const & point, pair con void IndexRouter::RoadsToNearestEdges(m2::PointD const & point, vector const & roads, uint32_t count, - IsEdgeProjGood const & IsGood, + IsEdgeProjGood const & isGood, vector> & edgeProj) const { - NearestEdgeFinder finder(point, IsGood); + NearestEdgeFinder finder(point, isGood); for (auto const & r : roads) finder.AddInformationSource(r.m_featureId, r.m_roadInfo.m_junctions, r.m_roadInfo.m_bidirectional); @@ -978,9 +978,10 @@ bool IndexRouter::FindBestEdges(m2::PointD const & point, auto closestRoads = m_roadGraph.FindRoads(rect, isGoodFeature); // Removing all dead ends from |closestRoads|. Then some candidates will be taken from |closestRoads|. - // It's necessary to call for all |closestRoads| before IsFencedOff(). If to remove all fenced off - // by other features from |point| candidates at first, only dead ends candidates may be left. - // And then the dead end candidates will be removed as well as dead ends. + // It's necessary to remove all dead ends for all |closestRoads| before IsFencedOff(). + // If to remove all fenced off by other features from |point| candidates at first, + // only dead ends candidates may be left. And then the dead end candidates will be removed + // as well as dead ends. It often happens near airports. EraseIfDeadEnd(worldGraph, closestRoads); // Sorting from the closest features to the further ones. The idea is the closer @@ -994,22 +995,29 @@ bool IndexRouter::FindBestEdges(m2::PointD const & point, point.SquaredLength(rhs.m_roadInfo.m_junctions[0].GetPoint()); }); + // Note about necessity of removing dead ends twice. + // At first, only real dead ends and roads which are not correct according to |worldGraph| + // are removed in EraseIfDeadEnd() function. It's necessary to prepare correct road network + // (|closestRoads|) which will be used in IsFencedOff() method later and |closestRoads| + // should contain all roads independently of routing options to prevent crossing roads + // which are switched off in RoutingOptions. + // Then in |IsDeadEndCached(..., true /* useRoutingOptions */, ...)| below we ignore + // candidates if it's a dead end taking into acount routing options. We ignore candidates as well + // if they don't match RoutingOptions. set deadEnds; - auto const IsGood = [&, this](pair const & edgeProj){ + auto const isGood = [&, this](pair const & edgeProj){ auto const segment = GetSegmentByEdge(edgeProj.first); if (IsDeadEndCached(segment, isOutgoing, true /* useRoutingOptions */, worldGraph, deadEnds)) return false; - // Removing all candidates which are fenced off by the road graph from |point|. - // It's better to perform this step after |candidates| are found: - // * by performance reasons - // * the closest edge(s) is not separated from |point| by other edges. + // Removing all candidates which are fenced off by the road graph (|closestRoads|) from |point|. return !IsFencedOff(point, edgeProj, closestRoads); }; - // Getting |kMaxRoadCandidates| closest edges from |closestRoads|. + // Getting |kMaxRoadCandidates| closest edges from |closestRoads| if they are correct + // according to isGood() function. vector> candidates; - RoadsToNearestEdges(point, closestRoads, kMaxRoadCandidates, IsGood, candidates); + RoadsToNearestEdges(point, closestRoads, kMaxRoadCandidates, isGood, candidates); if (candidates.empty()) return false; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 167d13c95d..6eadf8a93b 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -106,7 +106,8 @@ private: std::unique_ptr MakeWorldGraph(); - /// \brief Removes all roads from |roads| which goes to dead ends. + /// \brief Removes all roads from |roads| which goes to dead ends and all road which + /// is not good according to |worldGraph|. For car routing it's roads with hwtag nocar. void EraseIfDeadEnd(WorldGraph & worldGraph, std::vector & roads) const; diff --git a/routing/nearest_edge_finder.hpp b/routing/nearest_edge_finder.hpp index b8e8a2e6dd..9f503099fa 100644 --- a/routing/nearest_edge_finder.hpp +++ b/routing/nearest_edge_finder.hpp @@ -25,6 +25,17 @@ using IsEdgeProjGood = std::function const&)>; /// Class returns pairs of outgoing edge and projection point on the edge class NearestEdgeFinder { +public: + NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood); + + inline bool HasCandidates() const { return !m_candidates.empty(); } + + void AddInformationSource(FeatureID const & featureId, IRoadGraph::JunctionVec const & junctions, + bool bidirectional); + + void MakeResult(std::vector> & res, size_t maxCountFeatures); + +private: struct Candidate { static auto constexpr kInvalidSegmentId = std::numeric_limits::max(); @@ -38,26 +49,15 @@ class NearestEdgeFinder bool m_bidirectional = true; }; - m2::PointD const m_point; - std::vector m_candidates; - IsEdgeProjGood m_isEdgeProjGood; - -public: - explicit NearestEdgeFinder(m2::PointD const & point, IsEdgeProjGood const & isEdgeProjGood); - - inline bool HasCandidates() const { return !m_candidates.empty(); } - - void AddInformationSource(FeatureID const & featureId, IRoadGraph::JunctionVec const & junctions, - bool bidirectional); - - void MakeResult(std::vector> & res, size_t maxCountFeatures); - -private: void AddResIf(FeatureID const & featureId, bool forward, uint32_t segId, Junction const & startJunction, Junction const & endJunction, Junction const & projPoint, size_t maxCountFeatures, std::vector> & res) const; void CandidateToResult(Candidate const & candidate, size_t maxCountFeatures, std::vector> & res) const; + + m2::PointD const m_point; + std::vector m_candidates; + IsEdgeProjGood m_isEdgeProjGood; }; } // namespace routing diff --git a/routing/road_graph.hpp b/routing/road_graph.hpp index 5b979cb32f..8f3cc1d68c 100644 --- a/routing/road_graph.hpp +++ b/routing/road_graph.hpp @@ -200,17 +200,14 @@ public: struct FullRoadInfo { - FullRoadInfo(FeatureID const & featureId, RoadInfo const & roadInfo, - bool isRoadAccordingToModel) + FullRoadInfo(FeatureID const & featureId, RoadInfo const & roadInfo) : m_featureId(featureId) , m_roadInfo(roadInfo) - , m_isRoadAccordingToModel(isRoadAccordingToModel) { } FeatureID m_featureId; RoadInfo m_roadInfo; - bool m_isRoadAccordingToModel; }; /// This class is responsible for loading edges in a cross. diff --git a/routing/routing_helpers.cpp b/routing/routing_helpers.cpp index 0efc28a2db..1dd03fb8a1 100644 --- a/routing/routing_helpers.cpp +++ b/routing/routing_helpers.cpp @@ -4,7 +4,6 @@ #include "traffic/traffic_info.hpp" -#include "geometry/parametrized_segment.hpp" #include "geometry/point2d.hpp" #include "base/stl_helpers.hpp" @@ -164,7 +163,23 @@ Segment ConvertEdgeToSegment(NumMwmIds const & numMwmIds, Edge const & edge) return Segment(numMwmId, edge.GetFeatureId().m_index, edge.GetSegId(), edge.IsForward()); } -bool PolylineInRect(IRoadGraph::JunctionVec const & junctions, m2::RectD const & rect) +bool SegmentCrossesRect(m2::Segment2D const & segment, m2::RectD const & rect) +{ + double constexpr kEps = 1e-6; + bool isSideIntersected = false; + rect.ForEachSide([&segment, &isSideIntersected](m2::PointD const & a, m2::PointD const & b) { + if (isSideIntersected) + return; + + m2::Segment2D const rectSide(a, b); + isSideIntersected = + m2::Intersect(segment, rectSide, kEps).m_type != m2::IntersectionResult::Type::Zero; + }); + + return isSideIntersected; +} + +bool RectCoversPolyline(IRoadGraph::JunctionVec const & junctions, m2::RectD const & rect) { if (junctions.empty()) return false; @@ -172,14 +187,21 @@ bool PolylineInRect(IRoadGraph::JunctionVec const & junctions, m2::RectD const & if (junctions.size() == 1) return rect.IsPointInside(junctions.front().GetPoint()); - auto const & center = rect.Center(); - for (size_t i = 1; i < junctions.size(); ++i) + for (auto const & j : junctions) { - m2::ParametrizedSegment segProj(junctions[i - 1].GetPoint(), junctions[i].GetPoint()); - m2::PointD const & proj = segProj.ClosestPointTo(center); - if (rect.IsPointInside(proj)) + if (rect.IsPointInside(j.GetPoint())) return true; } + + // No point of polyline |junctions| lays inside |rect| but may be segments of the polyline + // cross |rect| borders. + for (size_t i = 1; i < junctions.size(); ++i) + { + m2::Segment2D const polylineSegment(junctions[i - 1].GetPoint(), junctions[i].GetPoint()); + if (SegmentCrossesRect(polylineSegment, rect)) + return true; + } + return false; } } // namespace routing diff --git a/routing/routing_helpers.hpp b/routing/routing_helpers.hpp index 409f406418..d72d3eaecc 100644 --- a/routing/routing_helpers.hpp +++ b/routing/routing_helpers.hpp @@ -13,6 +13,7 @@ #include "routing_common/pedestrian_model.hpp" #include "geometry/rect2d.hpp" +#include "geometry/segment2d.hpp" #include "base/cancellable.hpp" @@ -53,8 +54,11 @@ void ReconstructRoute(IDirectionsEngine & engine, IndexRoadGraph const & graph, /// \returns Segment() if mwm of |edge| is not alive. Segment ConvertEdgeToSegment(NumMwmIds const & numMwmIds, Edge const & edge); -// @returns true if any part of polyline |junctions| lay in |rect| and false otherwise. -bool PolylineInRect(IRoadGraph::JunctionVec const & junctions, m2::RectD const & rect); +/// \returns true if |segment| crosses any side of |rect|. +bool SegmentCrossesRect(m2::Segment2D const & segment, m2::RectD const & rect); + +// \returns true if any part of polyline |junctions| lay in |rect| and false otherwise. +bool RectCoversPolyline(IRoadGraph::JunctionVec const & junctions, m2::RectD const & rect); /// \brief Checks is edge connected with world graph. Function does BFS while it finds some number /// of edges, diff --git a/routing/routing_tests/routing_helpers_tests.cpp b/routing/routing_tests/routing_helpers_tests.cpp index da2892f6ba..16597911ad 100644 --- a/routing/routing_tests/routing_helpers_tests.cpp +++ b/routing/routing_tests/routing_helpers_tests.cpp @@ -69,25 +69,25 @@ UNIT_TEST(FillSegmentInfoTest) UNIT_TEST(PolylineInRectTest) { // Empty polyline. - TEST(!PolylineInRect({}, m2::RectD()), ()); - TEST(!PolylineInRect({}, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(!RectCoversPolyline({}, m2::RectD()), ()); + TEST(!RectCoversPolyline({}, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); // One point polyline outside the rect. { auto const junctions = IRoadGraph::JunctionVec({{m2::PointD(3.0, 3.0), 0 /* altitude */}}); - TEST(!PolylineInRect(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(!RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); } // One point polyline inside the rect. { auto const junctions = IRoadGraph::JunctionVec({{m2::PointD(1.0, 1.0), 0 /* altitude */}}); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); } // One point polyline on the rect border. { auto const junctions = IRoadGraph::JunctionVec({{m2::PointD(0.0, 0.0), 0 /* altitude */}}); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); } // Two point polyline touching the rect border. @@ -96,16 +96,24 @@ UNIT_TEST(PolylineInRectTest) {m2::PointD(-1.0, -1.0), 0 /* altitude */}, {m2::PointD(0.0, 0.0), 0 /* altitude */}, }); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); } - // Crossing rect. + // Crossing rect by a segment but no polyline points inside the rect. { auto const junctions = IRoadGraph::JunctionVec({ {m2::PointD(-1.0, -1.0), 0 /* altitude */}, {m2::PointD(5.0, 5.0), 0 /* altitude */}, }); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 2.0, 2.0)), ()); + } + + { + auto const junctions = IRoadGraph::JunctionVec({ + {m2::PointD(0.0, 1.0), 0 /* altitude */}, + {m2::PointD(100.0, 2.0), 0 /* altitude */}, + }); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 100.0, 1.0)), ()); } // Crossing a rect very close to a corner. @@ -114,7 +122,7 @@ UNIT_TEST(PolylineInRectTest) {m2::PointD(-1.0, 0.0), 0 /* altitude */}, {m2::PointD(1.0, 1.9), 0 /* altitude */}, }); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 1.0, 1.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 1.0, 1.0)), ()); } // Three point polyline crossing the rect. @@ -124,7 +132,7 @@ UNIT_TEST(PolylineInRectTest) {m2::PointD(1.0, 0.01), 0 /* altitude */}, {m2::PointD(2.0, -1.0), 0 /* altitude */}, }); - TEST(PolylineInRect(junctions, m2::RectD(0.0, 0.0, 1.0, 1.0)), ()); + TEST(RectCoversPolyline(junctions, m2::RectD(0.0, 0.0, 1.0, 1.0)), ()); } } } // namespace routing_test diff --git a/routing/transit_world_graph.cpp b/routing/transit_world_graph.cpp index 30022b1df6..b582bc5266 100644 --- a/routing/transit_world_graph.cpp +++ b/routing/transit_world_graph.cpp @@ -202,7 +202,7 @@ void TransitWorldGraph::AddRealEdges(Segment const & segment, bool isOutgoing, bool useRoutingOptions, vector & edges) { auto & indexGraph = GetIndexGraph(segment.GetMwmId()); - indexGraph.GetEdgeList(segment, isOutgoing, true /* useRoutingOptions */, edges); + indexGraph.GetEdgeList(segment, isOutgoing, useRoutingOptions, edges); GetTwins(segment, isOutgoing, useRoutingOptions, edges); }