| 1291 | } |
| 1292 | |
| 1293 | bool IndexRouter::PointsOnEdgesSnapping::FindBestEdges(m2::PointD const & checkpoint, m2::PointD const & direction, |
| 1294 | bool isOutgoing, double closestEdgesRadiusM, |
| 1295 | vector<Edge> & bestEdges, |
| 1296 | bool & bestSegmentIsAlmostCodirectional) |
| 1297 | { |
| 1298 | auto const rect = mercator::RectByCenterXYAndSizeInMeters(checkpoint, closestEdgesRadiusM); |
| 1299 | auto closestRoads = m_router.m_roadGraph.FindRoads(rect, [this](FeatureID const & fid) |
| 1300 | { |
| 1301 | auto const & info = fid.m_mwmId.GetInfo(); |
| 1302 | return m_router.m_numMwmIds->ContainsFile(info->GetLocalFile().GetCountryFile()); |
| 1303 | }); |
| 1304 | |
| 1305 | set<Segment> deadEnds[2]; |
| 1306 | // Removing all dead ends from |closestRoads|. Then some candidates will be taken from |closestRoads|. |
| 1307 | // It's necessary to remove all dead ends for all |closestRoads| before IsFencedOff(). |
| 1308 | // If to remove all fenced off by other features from |checkpoint| candidates at first, |
| 1309 | // only dead ends candidates may be left. And then the dead end candidates will be removed |
| 1310 | // as well as dead ends. It often happens near airports. |
| 1311 | EraseIfDeadEnd(checkpoint, closestRoads, deadEnds[0]); |
| 1312 | |
| 1313 | // Sorting from the closest features to the further ones. The idea is the closer |
| 1314 | // a feature to a |checkpoint| the more chances that it crosses the segment |
| 1315 | // |checkpoint|, projections of |checkpoint| on feature edges. It confirmed with benchmarks. |
| 1316 | sort(closestRoads.begin(), closestRoads.end(), [&checkpoint](RoadInfoT const & lhs, RoadInfoT const & rhs) |
| 1317 | { |
| 1318 | auto const & lj = lhs.m_roadInfo.m_junctions; |
| 1319 | auto const & rj = rhs.m_roadInfo.m_junctions; |
| 1320 | ASSERT(!lj.empty() && !rj.empty(), ()); |
| 1321 | |
| 1322 | return checkpoint.SquaredLength(lj[0].GetPoint()) < checkpoint.SquaredLength(rj[0].GetPoint()); |
| 1323 | }); |
| 1324 | |
| 1325 | // Note about necessity of removing dead ends twice. |
| 1326 | // At first, only real dead ends and roads which are not correct according to |worldGraph| |
| 1327 | // are removed in EraseIfDeadEnd() function. It's necessary to prepare correct road network |
| 1328 | // (|closestRoads|) which will be used in IsFencedOff() method later and |closestRoads| |
| 1329 | // should contain all roads independently of routing options to prevent crossing roads |
| 1330 | // which are switched off in RoutingOptions. |
| 1331 | // Then in |IsDeadEndCached(..., true /* useRoutingOptions */, ...)| below we ignore |
| 1332 | // candidates if it's a dead end taking into acount routing options. We ignore candidates as well |
| 1333 | // if they don't match RoutingOptions. |
| 1334 | auto const isGood = [&](EdgeProjectionT const & edgeProj) |
| 1335 | { |
| 1336 | auto const segment = GetSegmentByEdge(edgeProj.first); |
| 1337 | if (IsDeadEndCached(segment, isOutgoing, true /* useRoutingOptions */, m_graph, deadEnds[1]) && |
| 1338 | m_deadEnds[1].count(segment) == 0) |
| 1339 | { |
| 1340 | return false; |
| 1341 | } |
| 1342 | |
| 1343 | // Removing all candidates which are fenced off by the road graph (|closestRoads|) from |checkpoint|. |
| 1344 | return !IsFencedOff(checkpoint, edgeProj, closestRoads); |
| 1345 | }; |
| 1346 | |
| 1347 | // Getting closest edges from |closestRoads| if they are correct according to isGood() function. |
| 1348 | vector<EdgeProjectionT> candidates; |
| 1349 | RoadsToNearestEdges(checkpoint, closestRoads, isGood, candidates); |
| 1350 |
no test coverage detected