LCOV - code coverage report
Current view: top level - gnm - gnmgraph.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 186 247 75.3 %
Date: 2024-11-21 22:18:42 Functions: 13 20 65.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL/OGR Geography Network support (Geographic Network Model)
       4             :  * Purpose:  GNM graph implementation.
       5             :  * Authors:  Mikhail Gusev (gusevmihs at gmail dot com)
       6             :  *           Dmitry Baryshnikov, polimax@mail.ru
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2014, Mikhail Gusev
      10             :  * Copyright (c) 2014-2015, NextGIS <info@nextgis.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "gnmgraph.h"
      32             : #include "gnm_priv.h"
      33             : #include <algorithm>
      34             : #include <limits>
      35             : #include <set>
      36             : 
      37             : //! @cond Doxygen_Suppress
      38          16 : GNMGraph::GNMGraph()
      39             : {
      40          16 : }
      41             : 
      42          16 : GNMGraph::~GNMGraph()
      43             : {
      44          16 : }
      45             : 
      46         462 : void GNMGraph::AddVertex(GNMGFID nFID)
      47             : {
      48         462 :     if (m_mstVertices.find(nFID) != m_mstVertices.end())
      49         245 :         return;
      50             : 
      51         434 :     GNMStdVertex stVertex;
      52         217 :     stVertex.bIsBlocked = false;
      53         217 :     m_mstVertices[nFID] = std::move(stVertex);
      54             : }
      55             : 
      56           0 : void GNMGraph::DeleteVertex(GNMGFID nFID)
      57             : {
      58           0 :     m_mstVertices.erase(nFID);
      59             : 
      60             :     // remove all edges with this vertex
      61           0 :     std::vector<GNMGFID> aoIdsToErase;
      62           0 :     for (std::map<GNMGFID, GNMStdEdge>::iterator it = m_mstEdges.begin();
      63           0 :          it != m_mstEdges.end(); ++it)
      64             :     {
      65           0 :         if (it->second.nSrcVertexFID == nFID ||
      66           0 :             it->second.nTgtVertexFID == nFID)
      67           0 :             aoIdsToErase.push_back(it->first);
      68             :     }
      69           0 :     for (size_t i = 0; i < aoIdsToErase.size(); i++)
      70           0 :         m_mstEdges.erase(aoIdsToErase[i]);
      71           0 : }
      72             : 
      73         231 : void GNMGraph::AddEdge(GNMGFID nConFID, GNMGFID nSrcFID, GNMGFID nTgtFID,
      74             :                        bool bIsBidir, double dfCost, double dfInvCost)
      75             : {
      76             :     // We do not add edge if an edge with the same id already exist
      77             :     // because each edge must have only one source and one target vertex.
      78         231 :     std::map<GNMGFID, GNMStdEdge>::iterator it = m_mstEdges.find(nConFID);
      79         231 :     if (it != m_mstEdges.end())
      80             :     {
      81           0 :         CPLError(CE_Failure, CPLE_AppDefined, "The edge already exist.");
      82           0 :         return;
      83             :     }
      84             : 
      85         231 :     AddVertex(nSrcFID);
      86         231 :     AddVertex(nTgtFID);
      87             : 
      88         231 :     auto itSrs = m_mstVertices.find(nSrcFID);
      89         231 :     auto itTgt = m_mstVertices.find(nTgtFID);
      90         231 :     if (itSrs == m_mstVertices.end() || itTgt == m_mstVertices.end())
      91             :     {
      92           0 :         CPLAssert(false);
      93             :         return;
      94             :     }
      95             : 
      96             :     // Insert edge to the array of edges.
      97             :     GNMStdEdge stEdge;
      98         231 :     stEdge.nSrcVertexFID = nSrcFID;
      99         231 :     stEdge.nTgtVertexFID = nTgtFID;
     100         231 :     stEdge.bIsBidir = bIsBidir;
     101         231 :     stEdge.dfDirCost = dfCost;
     102         231 :     stEdge.dfInvCost = dfInvCost;
     103         231 :     stEdge.bIsBlocked = false;
     104             : 
     105         231 :     m_mstEdges[nConFID] = stEdge;
     106             : 
     107         231 :     if (bIsBidir)
     108             :     {
     109         231 :         itSrs->second.anOutEdgeFIDs.push_back(nConFID);
     110         231 :         itTgt->second.anOutEdgeFIDs.push_back(nConFID);
     111             :     }
     112             :     else
     113             :     {
     114           0 :         itSrs->second.anOutEdgeFIDs.push_back(nConFID);
     115             :     }
     116             : }
     117             : 
     118           0 : void GNMGraph::DeleteEdge(GNMGFID nConFID)
     119             : {
     120           0 :     m_mstEdges.erase(nConFID);
     121             : 
     122             :     // remove edge from all vertices anOutEdgeFIDs
     123           0 :     for (auto &it : m_mstVertices)
     124             :     {
     125             :         it.second.anOutEdgeFIDs.erase(
     126           0 :             std::remove(it.second.anOutEdgeFIDs.begin(),
     127           0 :                         it.second.anOutEdgeFIDs.end(), nConFID),
     128           0 :             it.second.anOutEdgeFIDs.end());
     129             :     }
     130           0 : }
     131             : 
     132           0 : void GNMGraph::ChangeEdge(GNMGFID nFID, double dfCost, double dfInvCost)
     133             : {
     134           0 :     std::map<GNMGFID, GNMStdEdge>::iterator it = m_mstEdges.find(nFID);
     135           0 :     if (it != m_mstEdges.end())
     136             :     {
     137           0 :         it->second.dfDirCost = dfCost;
     138           0 :         it->second.dfInvCost = dfInvCost;
     139             :     }
     140           0 : }
     141             : 
     142           0 : void GNMGraph::ChangeBlockState(GNMGFID nFID, bool bBlock)
     143             : {
     144             :     // check vertices
     145           0 :     std::map<GNMGFID, GNMStdVertex>::iterator itv = m_mstVertices.find(nFID);
     146           0 :     if (itv != m_mstVertices.end())
     147             :     {
     148           0 :         itv->second.bIsBlocked = bBlock;
     149           0 :         return;
     150             :     }
     151             : 
     152             :     // check edges
     153           0 :     std::map<GNMGFID, GNMStdEdge>::iterator ite = m_mstEdges.find(nFID);
     154           0 :     if (ite != m_mstEdges.end())
     155             :     {
     156           0 :         ite->second.bIsBlocked = bBlock;
     157             :     }
     158             : }
     159             : 
     160         337 : bool GNMGraph::CheckVertexBlocked(GNMGFID nFID) const
     161             : {
     162             :     std::map<GNMGFID, GNMStdVertex>::const_iterator it =
     163         337 :         m_mstVertices.find(nFID);
     164         337 :     if (it != m_mstVertices.end())
     165         337 :         return it->second.bIsBlocked;
     166           0 :     return false;
     167             : }
     168             : 
     169           0 : void GNMGraph::ChangeAllBlockState(bool bBlock)
     170             : {
     171           0 :     for (std::map<GNMGFID, GNMStdVertex>::iterator itv = m_mstVertices.begin();
     172           0 :          itv != m_mstVertices.end(); ++itv)
     173             :     {
     174           0 :         itv->second.bIsBlocked = bBlock;
     175             :     }
     176             : 
     177           0 :     for (std::map<GNMGFID, GNMStdEdge>::iterator ite = m_mstEdges.begin();
     178           0 :          ite != m_mstEdges.end(); ++ite)
     179             :     {
     180           0 :         ite->second.bIsBlocked = bBlock;
     181             :     }
     182           0 : }
     183             : 
     184             : GNMPATH
     185          42 : GNMGraph::DijkstraShortestPath(GNMGFID nStartFID, GNMGFID nEndFID,
     186             :                                const std::map<GNMGFID, GNMStdEdge> &mstEdges)
     187             : {
     188          84 :     std::map<GNMGFID, GNMGFID> mnShortestTree;
     189          42 :     DijkstraShortestPathTree(nStartFID, mstEdges, mnShortestTree);
     190             : 
     191             :     // We search for a path in the resulting tree, starting from end point to
     192             :     // start point.
     193             : 
     194          84 :     GNMPATH aoShortestPath;
     195          42 :     GNMGFID nNextVertexId = nEndFID;
     196          42 :     std::map<GNMGFID, GNMGFID>::iterator it;
     197          42 :     EDGEVERTEXPAIR buf;
     198             : 
     199             :     while (true)
     200             :     {
     201         116 :         it = mnShortestTree.find(nNextVertexId);
     202             : 
     203         116 :         if (it == mnShortestTree.end())
     204             :         {
     205             :             // We haven't found the start vertex - there is no path between
     206             :             // to given vertices in a shortest-path tree.
     207          32 :             break;
     208             :         }
     209          84 :         else if (it->first == nStartFID)
     210             :         {
     211             :             // We've reached the start vertex and return an array.
     212          10 :             aoShortestPath.push_back(std::make_pair(nNextVertexId, -1));
     213             : 
     214             :             // Revert array because the first vertex is now the last in path.
     215          10 :             int size = static_cast<int>(aoShortestPath.size());
     216          52 :             for (int i = 0; i < size / 2; ++i)
     217             :             {
     218          42 :                 buf = aoShortestPath[i];
     219          42 :                 aoShortestPath[i] = aoShortestPath[size - i - 1];
     220          42 :                 aoShortestPath[size - i - 1] = buf;
     221             :             }
     222          10 :             return aoShortestPath;
     223             :         }
     224             :         else
     225             :         {
     226             :             // There is only one edge which leads to this vertex, because we
     227             :             // analyse a tree. We add this edge with its target vertex into
     228             :             // final array.
     229          74 :             aoShortestPath.push_back(std::make_pair(nNextVertexId, it->second));
     230             : 
     231             :             // An edge has only two vertices, so we get the opposite one to the
     232             :             // current vertex in order to continue search backwards.
     233          74 :             nNextVertexId = GetOppositVertex(it->second, it->first);
     234             :         }
     235          74 :     }
     236             : 
     237             :     // return empty array
     238          64 :     GNMPATH oRet;
     239          32 :     return oRet;
     240             : }
     241             : 
     242           4 : GNMPATH GNMGraph::DijkstraShortestPath(GNMGFID nStartFID, GNMGFID nEndFID)
     243             : {
     244           4 :     return DijkstraShortestPath(nStartFID, nEndFID, m_mstEdges);
     245             : }
     246             : 
     247           2 : std::vector<GNMPATH> GNMGraph::KShortestPaths(GNMGFID nStartFID,
     248             :                                               GNMGFID nEndFID, size_t nK)
     249             : {
     250             :     // Resulting array with paths.
     251             :     // A will be sorted by the path costs' descending.
     252           2 :     std::vector<GNMPATH> A;
     253             : 
     254           2 :     if (nK == 0)
     255           0 :         return A;  // return empty array if K is incorrect.
     256             : 
     257             :     // Temporary array for storing paths-candidates.
     258             :     // B will be automatically sorted by the cost descending order. We
     259             :     // need multimap because there can be physically different paths but
     260             :     // with the same costs.
     261           4 :     std::multimap<double, GNMPATH> B;
     262             : 
     263             :     // Firstly get the very shortest path.
     264             :     // Note, that it is important to obtain the path from DijkstraShortestPath()
     265             :     // as vector, rather than the map, because we need the correct order of the
     266             :     // path segments in the Yen's algorithm iterations.
     267           4 :     GNMPATH aoFirstPath = DijkstraShortestPath(nStartFID, nEndFID);
     268           2 :     if (aoFirstPath.empty())
     269           0 :         return A;  // return empty array if there is no path between points.
     270             : 
     271           2 :     A.push_back(aoFirstPath);
     272             : 
     273             :     size_t i, k, l;
     274           2 :     GNMPATH::iterator itAk, tempIt, itR;
     275           2 :     std::vector<GNMPATH>::iterator itA;
     276           2 :     std::map<GNMGFID, double>::iterator itDel;
     277           4 :     GNMPATH aoRootPath, aoRootPathOther, aoSpurPath;
     278             :     GNMGFID nSpurNode, nVertexToDel, nEdgeToDel;
     279             :     double dfSumCost;
     280             : 
     281           4 :     std::map<GNMGFID, GNMStdEdge> mstEdges = m_mstEdges;
     282             : 
     283           6 :     for (k = 0; k < nK - 1; ++k)  // -1 because we have already found one
     284             :     {
     285             :         std::map<GNMGFID, double>
     286           4 :             mDeletedEdges;  // for infinity costs assignment
     287           4 :         itAk = A[k].begin();
     288             : 
     289          42 :         for (i = 0; i < A[k].size() - 1; ++i)  // avoid end node
     290             :         {
     291             :             // Get the current node.
     292          38 :             nSpurNode = A[k][i].first;
     293             : 
     294             :             // Get the root path from the 0 to the current node.
     295             : 
     296             :             // Equivalent to A[k][i]
     297             :             // because we will use std::vector::assign, which assigns [..)
     298             :             // range, not [..]
     299          38 :             ++itAk;
     300             : 
     301          38 :             aoRootPath.assign(A[k].begin(), itAk);
     302             : 
     303             :             // Remove old incidence edges of all other best paths.
     304             :             // i.e. if the spur vertex can be reached in already found best
     305             :             // paths we must remove the following edge after the end of root
     306             :             // path from the graph in order not to take in account these already
     307             :             // seen best paths.
     308             :             // i.e. it ensures that the spur path will be different.
     309          96 :             for (itA = A.begin(); itA != A.end(); ++itA)
     310             :             {
     311             :                 // check if the number of node exceed the number of last node in
     312             :                 // the path array (i.e. if one of the A paths has less amount of
     313             :                 // segments than the current candidate path)
     314          58 :                 if (i >= itA->size())
     315           0 :                     continue;
     316             : 
     317             :                 // + 1, because we will use std::vector::assign, which assigns
     318             :                 // [..) range, not [..]
     319          58 :                 aoRootPathOther.assign(itA->begin(), itA->begin() + i + 1);
     320             : 
     321             :                 // Get the edge which follows the spur node for current path
     322             :                 // and delete it.
     323             :                 //
     324             :                 // NOTE: we do not delete edges due to performance reasons,
     325             :                 // because the deletion of edge and all its GFIDs in vertex
     326             :                 // records is slower than the infinity cost assignment.
     327             : 
     328             :                 // also check if node number exceed the number of the last node
     329             :                 // in root array.
     330         100 :                 if ((aoRootPath == aoRootPathOther) &&
     331          42 :                     (i < aoRootPathOther.size()))
     332             :                 {
     333          42 :                     tempIt = itA->begin() + i + 1;
     334           0 :                     mDeletedEdges.insert(std::make_pair(
     335          42 :                         tempIt->second, mstEdges[tempIt->second].dfDirCost));
     336          42 :                     mstEdges[tempIt->second].dfDirCost =
     337          42 :                         std::numeric_limits<double>::infinity();
     338             :                 }
     339             :             }
     340             : 
     341             :             // Remove root path nodes from the graph. If we do not delete them
     342             :             // the path will be found backwards and some parts of the path will
     343             :             // duplicate the parts of old paths.
     344             :             // Note: we "delete" all the incidence to the root nodes edges, so
     345             :             // to restore them in a common way.
     346             : 
     347             :             // end()-1, because we should not remove the spur node
     348         200 :             for (itR = aoRootPath.begin(); itR != aoRootPath.end() - 1; ++itR)
     349             :             {
     350         162 :                 nVertexToDel = itR->first;
     351         162 :                 for (l = 0;
     352         544 :                      l < m_mstVertices[nVertexToDel].anOutEdgeFIDs.size(); ++l)
     353             :                 {
     354         382 :                     nEdgeToDel = m_mstVertices[nVertexToDel].anOutEdgeFIDs[l];
     355           0 :                     mDeletedEdges.insert(std::make_pair(
     356         382 :                         nEdgeToDel, mstEdges[nEdgeToDel].dfDirCost));
     357         382 :                     mstEdges[nEdgeToDel].dfDirCost =
     358         382 :                         std::numeric_limits<double>::infinity();
     359             :                 }
     360             :             }
     361             : 
     362             :             // Find the new best path in the modified graph.
     363          38 :             aoSpurPath = DijkstraShortestPath(nSpurNode, nEndFID, mstEdges);
     364             : 
     365             :             // Firstly, restore deleted edges in order to calculate the summary
     366             :             // cost of the path correctly later, because the costs will be
     367             :             // gathered from the initial graph.
     368             :             // We must do it here, after each edge removing, because the later
     369             :             // Dijkstra searches must consider these edges.
     370         332 :             for (itDel = mDeletedEdges.begin(); itDel != mDeletedEdges.end();
     371         294 :                  ++itDel)
     372             :             {
     373         294 :                 mstEdges[itDel->first].dfDirCost = itDel->second;
     374             :             }
     375             : 
     376          38 :             mDeletedEdges.clear();
     377             : 
     378             :             // If the part of a new best path has been found we form a full one
     379             :             // and add it to the candidates array.
     380          38 :             if (!aoSpurPath.empty())
     381             :             {
     382             :                 // + 1 so not to consider the first node in the found path,
     383             :                 // which is already the last node in the root path
     384          12 :                 aoRootPath.insert(aoRootPath.end(), aoSpurPath.begin() + 1,
     385          18 :                                   aoSpurPath.end());
     386             :                 // Calculate the summary cost of the path.
     387             :                 // TODO: get the summary cost from the Dejkstra method?
     388           6 :                 dfSumCost = 0.0;
     389          74 :                 for (itR = aoRootPath.begin(); itR != aoRootPath.end(); ++itR)
     390             :                 {
     391             :                     // TODO: check: Note, that here the current cost can not be
     392             :                     // infinity, because every time we assign infinity costs for
     393             :                     // edges of old paths, we anyway have the alternative edges
     394             :                     // with non-infinity costs.
     395          68 :                     dfSumCost += mstEdges[itR->second].dfDirCost;
     396             :                 }
     397             : 
     398           6 :                 B.insert(std::make_pair(dfSumCost, aoRootPath));
     399             :             }
     400             :         }
     401             : 
     402           4 :         if (B.empty())
     403           0 :             break;
     404             : 
     405             :         // The best path is the first, because the map is sorted accordingly.
     406             :         // Note, that here we won't clear the path candidates array and select
     407             :         // the best path from all of the rest paths, even from those which were
     408             :         // found on previous iterations. That's why we need k iterations at all.
     409             :         // Note, that if there were two paths with the same costs and it is the
     410             :         // LAST iteration the first occurred path will be added, rather than
     411             :         // random.
     412           4 :         A.push_back(B.begin()->second);
     413             : 
     414             :         // Sometimes B contains fully duplicate paths. Such duplicates have been
     415             :         // formed during the search of alternative for almost the same paths
     416             :         // which were already in A.
     417             :         // We allowed to add them into B so here we must delete all duplicates.
     418           8 :         while (!B.empty() && B.begin()->second == A.back())
     419             :         {
     420           4 :             B.erase(B.begin());
     421             :         }
     422             :     }
     423             : 
     424           2 :     return A;
     425             : }
     426             : 
     427           1 : GNMPATH GNMGraph::ConnectedComponents(const GNMVECTOR &anEmittersIDs)
     428             : {
     429           1 :     GNMPATH anConnectedIDs;
     430             : 
     431           1 :     if (anEmittersIDs.empty())
     432             :     {
     433           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Emitters list is empty.");
     434           0 :         return anConnectedIDs;
     435             :     }
     436           2 :     std::set<GNMGFID> anMarkedVertIDs;
     437             : 
     438           2 :     std::queue<GNMGFID> anStartQueue;
     439           1 :     GNMVECTOR::const_iterator it;
     440           3 :     for (it = anEmittersIDs.begin(); it != anEmittersIDs.end(); ++it)
     441             :     {
     442           2 :         anStartQueue.push(*it);
     443             :     }
     444             : 
     445             :     // Begin the iterations of the Breadth-first search.
     446           1 :     TraceTargets(anStartQueue, anMarkedVertIDs, anConnectedIDs);
     447             : 
     448           1 :     return anConnectedIDs;
     449             : }
     450             : 
     451           0 : void GNMGraph::Clear()
     452             : {
     453           0 :     m_mstVertices.clear();
     454           0 :     m_mstEdges.clear();
     455           0 : }
     456             : 
     457          42 : void GNMGraph::DijkstraShortestPathTree(
     458             :     GNMGFID nFID, const std::map<GNMGFID, GNMStdEdge> &mstEdges,
     459             :     std::map<GNMGFID, GNMGFID> &mnPathTree)
     460             : {
     461             :     // Initialize all vertices in graph with infinity mark.
     462          42 :     double dfInfinity = std::numeric_limits<double>::infinity();
     463             : 
     464          84 :     std::map<GNMGFID, double> mMarks;
     465          42 :     std::map<GNMGFID, GNMStdVertex>::iterator itv;
     466        1344 :     for (itv = m_mstVertices.begin(); itv != m_mstVertices.end(); ++itv)
     467             :     {
     468        1302 :         mMarks[itv->first] = dfInfinity;
     469             :     }
     470             : 
     471          42 :     mMarks[nFID] = 0.0;
     472          42 :     mnPathTree[nFID] = -1;
     473             : 
     474             :     // Initialize all vertices as unseen (there are no seen vertices).
     475          84 :     std::set<GNMGFID> snSeen;
     476             : 
     477             :     // We use multimap to maintain the ascending order of costs and because
     478             :     // there can be different vertices with the equal cost.
     479          84 :     std::multimap<double, GNMGFID> to_see;
     480          42 :     std::multimap<double, GNMGFID>::iterator it;
     481          42 :     to_see.insert(std::pair<double, GNMGFID>(0.0, nFID));
     482             :     LPGNMCONSTVECTOR panOutcomeEdgeId;
     483             : 
     484             :     size_t i;
     485             :     GNMGFID nCurrentVertId, nCurrentEdgeId, nTargetVertId;
     486             :     double dfCurrentEdgeCost, dfCurrentVertMark, dfNewVertexMark;
     487          42 :     std::map<GNMGFID, GNMStdEdge>::const_iterator ite;
     488             : 
     489             :     // Continue iterations while there are some vertices to see.
     490         388 :     while (!to_see.empty())
     491             :     {
     492             :         // We must see vertices with minimal costs at first.
     493             :         // In multimap the first cost is the minimal.
     494         346 :         it = to_see.begin();
     495             : 
     496         346 :         nCurrentVertId = it->second;
     497         346 :         dfCurrentVertMark = it->first;
     498         346 :         snSeen.insert(it->second);
     499         346 :         to_see.erase(it);
     500             : 
     501             :         // For all neighbours for the current vertex.
     502         346 :         panOutcomeEdgeId = GetOutEdges(nCurrentVertId);
     503         346 :         if (nullptr == panOutcomeEdgeId)
     504           0 :             continue;
     505             : 
     506        1078 :         for (i = 0; i < panOutcomeEdgeId->size(); ++i)
     507             :         {
     508         732 :             nCurrentEdgeId = panOutcomeEdgeId->operator[](i);
     509             : 
     510         732 :             ite = mstEdges.find(nCurrentEdgeId);
     511         732 :             if (ite == mstEdges.end() || ite->second.bIsBlocked)
     512           0 :                 continue;
     513             : 
     514             :             // We go in any edge from source to target so we take only
     515             :             // direct cost (even if an edge is bi-directed).
     516         732 :             dfCurrentEdgeCost = ite->second.dfDirCost;
     517             : 
     518             :             // While we see outcome edges of current vertex id we definitely
     519             :             // know that target vertex id will be target for current edge id.
     520         732 :             nTargetVertId = GetOppositVertex(nCurrentEdgeId, nCurrentVertId);
     521             : 
     522             :             // Calculate a new mark assuming the full path cost (mark of the
     523             :             // current vertex) to this vertex.
     524         732 :             dfNewVertexMark = dfCurrentVertMark + dfCurrentEdgeCost;
     525             : 
     526             :             // Update mark of the vertex if needed.
     527         732 :             if (snSeen.find(nTargetVertId) == snSeen.end() &&
     528        1036 :                 dfNewVertexMark < mMarks[nTargetVertId] &&
     529         304 :                 !CheckVertexBlocked(nTargetVertId))
     530             :             {
     531         304 :                 mMarks[nTargetVertId] = dfNewVertexMark;
     532         304 :                 mnPathTree[nTargetVertId] = nCurrentEdgeId;
     533             : 
     534             :                 // The vertex with minimal cost will be inserted to the
     535             :                 // beginning.
     536             :                 to_see.insert(
     537         304 :                     std::pair<double, GNMGFID>(dfNewVertexMark, nTargetVertId));
     538             :             }
     539             :         }
     540             :     }
     541          42 : }
     542             : 
     543         377 : LPGNMCONSTVECTOR GNMGraph::GetOutEdges(GNMGFID nFID) const
     544             : {
     545             :     std::map<GNMGFID, GNMStdVertex>::const_iterator it =
     546         377 :         m_mstVertices.find(nFID);
     547         377 :     if (it != m_mstVertices.end())
     548         377 :         return &it->second.anOutEdgeFIDs;
     549           0 :     return nullptr;
     550             : }
     551             : 
     552         872 : GNMGFID GNMGraph::GetOppositVertex(GNMGFID nEdgeFID, GNMGFID nVertexFID) const
     553             : {
     554             :     std::map<GNMGFID, GNMStdEdge>::const_iterator it =
     555         872 :         m_mstEdges.find(nEdgeFID);
     556         872 :     if (it != m_mstEdges.end())
     557             :     {
     558         872 :         if (nVertexFID == it->second.nSrcVertexFID)
     559             :         {
     560         399 :             return it->second.nTgtVertexFID;
     561             :         }
     562         473 :         else if (nVertexFID == it->second.nTgtVertexFID)
     563             :         {
     564         473 :             return it->second.nSrcVertexFID;
     565             :         }
     566             :     }
     567           0 :     return -1;
     568             : }
     569             : 
     570           8 : void GNMGraph::TraceTargets(std::queue<GNMGFID> &vertexQueue,
     571             :                             std::set<GNMGFID> &markedVertIds,
     572             :                             GNMPATH &connectedIds)
     573             : {
     574          16 :     std::queue<GNMGFID> neighbours_queue;
     575             : 
     576             :     // See all given vertices except thouse that have been already seen.
     577          43 :     while (!vertexQueue.empty())
     578             :     {
     579          35 :         GNMGFID nCurVertID = vertexQueue.front();
     580             : 
     581             :         // There may be duplicate unmarked vertices in a current queue. Check
     582             :         // it.
     583          35 :         if (markedVertIds.find(nCurVertID) == markedVertIds.end())
     584             :         {
     585          31 :             markedVertIds.insert(nCurVertID);
     586             : 
     587             :             // See all outcome edges, add them to connected and than see the
     588             :             // target vertex of each edge. Add it to the queue, which will be
     589             :             // recursively seen the same way on the next iteration.
     590          31 :             LPGNMCONSTVECTOR panOutcomeEdgeIDs = GetOutEdges(nCurVertID);
     591          31 :             if (nullptr != panOutcomeEdgeIDs)
     592             :             {
     593          97 :                 for (const GNMGFID nCurEdgeID : *panOutcomeEdgeIDs)
     594             :                 {
     595             :                     // ISSUE: think about to return a sequence of vertices and
     596             :                     // edges (which is more universal), as now we are going to
     597             :                     // return only sequence of edges.
     598          66 :                     connectedIds.push_back(
     599          66 :                         std::make_pair(nCurVertID, nCurEdgeID));
     600             : 
     601             :                     // Get the only target vertex of this edge. If edge is
     602             :                     // bidirected get not that vertex that with nCurVertID.
     603             :                     GNMGFID nTargetVertID;
     604             :                     /*
     605             :                     std::vector<GNMGFID> target_vert_ids =
     606             :                     _getTgtVert(cur_edge_id); std::vector<GNMGFID>::iterator
     607             :                     itt; for (itt = target_vert_ids.begin(); itt !=
     608             :                     target_vert_ids.end(); ++itt)
     609             :                     {
     610             :                         if ((*itt) != cur_vert_id)
     611             :                         {
     612             :                             target_vert_id = *itt;
     613             :                             break;
     614             :                         }
     615             :                     }
     616             :                     */
     617          66 :                     nTargetVertID = GetOppositVertex(nCurEdgeID, nCurVertID);
     618             : 
     619             :                     // Avoid marked or blocked vertices.
     620          66 :                     if ((markedVertIds.find(nTargetVertID) ==
     621         165 :                          markedVertIds.end()) &&
     622          33 :                         (!CheckVertexBlocked(nTargetVertID)))
     623          33 :                         neighbours_queue.push(nTargetVertID);
     624             :                 }
     625             :             }
     626             :         }
     627             : 
     628          35 :         vertexQueue.pop();
     629             :     }
     630             : 
     631           8 :     if (!neighbours_queue.empty())
     632           7 :         TraceTargets(neighbours_queue, markedVertIds, connectedIds);
     633           8 : }
     634             : 
     635             : //! @endcond

Generated by: LCOV version 1.14