/src/gdal/gnm/gnmgraph.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | GNMGraph::GNMGraph() |
39 | 0 | { |
40 | 0 | } |
41 | | |
42 | | GNMGraph::~GNMGraph() |
43 | 0 | { |
44 | 0 | } |
45 | | |
46 | | void GNMGraph::AddVertex(GNMGFID nFID) |
47 | 0 | { |
48 | 0 | if (m_mstVertices.find(nFID) != m_mstVertices.end()) |
49 | 0 | return; |
50 | | |
51 | 0 | GNMStdVertex stVertex; |
52 | 0 | stVertex.bIsBlocked = false; |
53 | 0 | m_mstVertices[nFID] = std::move(stVertex); |
54 | 0 | } |
55 | | |
56 | | void GNMGraph::DeleteVertex(GNMGFID nFID) |
57 | 0 | { |
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 | 0 | { |
65 | 0 | if (it->second.nSrcVertexFID == nFID || |
66 | 0 | it->second.nTgtVertexFID == nFID) |
67 | 0 | aoIdsToErase.push_back(it->first); |
68 | 0 | } |
69 | 0 | for (size_t i = 0; i < aoIdsToErase.size(); i++) |
70 | 0 | m_mstEdges.erase(aoIdsToErase[i]); |
71 | 0 | } |
72 | | |
73 | | void GNMGraph::AddEdge(GNMGFID nConFID, GNMGFID nSrcFID, GNMGFID nTgtFID, |
74 | | bool bIsBidir, double dfCost, double dfInvCost) |
75 | 0 | { |
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 | 0 | std::map<GNMGFID, GNMStdEdge>::iterator it = m_mstEdges.find(nConFID); |
79 | 0 | if (it != m_mstEdges.end()) |
80 | 0 | { |
81 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "The edge already exist."); |
82 | 0 | return; |
83 | 0 | } |
84 | | |
85 | 0 | AddVertex(nSrcFID); |
86 | 0 | AddVertex(nTgtFID); |
87 | |
|
88 | 0 | auto itSrs = m_mstVertices.find(nSrcFID); |
89 | 0 | auto itTgt = m_mstVertices.find(nTgtFID); |
90 | 0 | if (itSrs == m_mstVertices.end() || itTgt == m_mstVertices.end()) |
91 | 0 | { |
92 | 0 | CPLAssert(false); |
93 | 0 | return; |
94 | 0 | } |
95 | | |
96 | | // Insert edge to the array of edges. |
97 | 0 | GNMStdEdge stEdge; |
98 | 0 | stEdge.nSrcVertexFID = nSrcFID; |
99 | 0 | stEdge.nTgtVertexFID = nTgtFID; |
100 | 0 | stEdge.bIsBidir = bIsBidir; |
101 | 0 | stEdge.dfDirCost = dfCost; |
102 | 0 | stEdge.dfInvCost = dfInvCost; |
103 | 0 | stEdge.bIsBlocked = false; |
104 | |
|
105 | 0 | m_mstEdges[nConFID] = stEdge; |
106 | |
|
107 | 0 | if (bIsBidir) |
108 | 0 | { |
109 | 0 | itSrs->second.anOutEdgeFIDs.push_back(nConFID); |
110 | 0 | itTgt->second.anOutEdgeFIDs.push_back(nConFID); |
111 | 0 | } |
112 | 0 | else |
113 | 0 | { |
114 | 0 | itSrs->second.anOutEdgeFIDs.push_back(nConFID); |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | void GNMGraph::DeleteEdge(GNMGFID nConFID) |
119 | 0 | { |
120 | 0 | m_mstEdges.erase(nConFID); |
121 | | |
122 | | // remove edge from all vertices anOutEdgeFIDs |
123 | 0 | for (auto &it : m_mstVertices) |
124 | 0 | { |
125 | 0 | 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 | 0 | } |
130 | 0 | } |
131 | | |
132 | | void GNMGraph::ChangeEdge(GNMGFID nFID, double dfCost, double dfInvCost) |
133 | 0 | { |
134 | 0 | std::map<GNMGFID, GNMStdEdge>::iterator it = m_mstEdges.find(nFID); |
135 | 0 | if (it != m_mstEdges.end()) |
136 | 0 | { |
137 | 0 | it->second.dfDirCost = dfCost; |
138 | 0 | it->second.dfInvCost = dfInvCost; |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | void GNMGraph::ChangeBlockState(GNMGFID nFID, bool bBlock) |
143 | 0 | { |
144 | | // check vertices |
145 | 0 | std::map<GNMGFID, GNMStdVertex>::iterator itv = m_mstVertices.find(nFID); |
146 | 0 | if (itv != m_mstVertices.end()) |
147 | 0 | { |
148 | 0 | itv->second.bIsBlocked = bBlock; |
149 | 0 | return; |
150 | 0 | } |
151 | | |
152 | | // check edges |
153 | 0 | std::map<GNMGFID, GNMStdEdge>::iterator ite = m_mstEdges.find(nFID); |
154 | 0 | if (ite != m_mstEdges.end()) |
155 | 0 | { |
156 | 0 | ite->second.bIsBlocked = bBlock; |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | bool GNMGraph::CheckVertexBlocked(GNMGFID nFID) const |
161 | 0 | { |
162 | 0 | std::map<GNMGFID, GNMStdVertex>::const_iterator it = |
163 | 0 | m_mstVertices.find(nFID); |
164 | 0 | if (it != m_mstVertices.end()) |
165 | 0 | return it->second.bIsBlocked; |
166 | 0 | return false; |
167 | 0 | } |
168 | | |
169 | | void GNMGraph::ChangeAllBlockState(bool bBlock) |
170 | 0 | { |
171 | 0 | for (std::map<GNMGFID, GNMStdVertex>::iterator itv = m_mstVertices.begin(); |
172 | 0 | itv != m_mstVertices.end(); ++itv) |
173 | 0 | { |
174 | 0 | itv->second.bIsBlocked = bBlock; |
175 | 0 | } |
176 | |
|
177 | 0 | for (std::map<GNMGFID, GNMStdEdge>::iterator ite = m_mstEdges.begin(); |
178 | 0 | ite != m_mstEdges.end(); ++ite) |
179 | 0 | { |
180 | 0 | ite->second.bIsBlocked = bBlock; |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | GNMPATH |
185 | | GNMGraph::DijkstraShortestPath(GNMGFID nStartFID, GNMGFID nEndFID, |
186 | | const std::map<GNMGFID, GNMStdEdge> &mstEdges) |
187 | 0 | { |
188 | 0 | std::map<GNMGFID, GNMGFID> mnShortestTree; |
189 | 0 | 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 | 0 | GNMPATH aoShortestPath; |
195 | 0 | GNMGFID nNextVertexId = nEndFID; |
196 | 0 | std::map<GNMGFID, GNMGFID>::iterator it; |
197 | 0 | EDGEVERTEXPAIR buf; |
198 | |
|
199 | 0 | while (true) |
200 | 0 | { |
201 | 0 | it = mnShortestTree.find(nNextVertexId); |
202 | |
|
203 | 0 | if (it == mnShortestTree.end()) |
204 | 0 | { |
205 | | // We haven't found the start vertex - there is no path between |
206 | | // to given vertices in a shortest-path tree. |
207 | 0 | break; |
208 | 0 | } |
209 | 0 | else if (it->first == nStartFID) |
210 | 0 | { |
211 | | // We've reached the start vertex and return an array. |
212 | 0 | aoShortestPath.push_back(std::make_pair(nNextVertexId, -1)); |
213 | | |
214 | | // Revert array because the first vertex is now the last in path. |
215 | 0 | int size = static_cast<int>(aoShortestPath.size()); |
216 | 0 | for (int i = 0; i < size / 2; ++i) |
217 | 0 | { |
218 | 0 | buf = aoShortestPath[i]; |
219 | 0 | aoShortestPath[i] = aoShortestPath[size - i - 1]; |
220 | 0 | aoShortestPath[size - i - 1] = buf; |
221 | 0 | } |
222 | 0 | return aoShortestPath; |
223 | 0 | } |
224 | 0 | else |
225 | 0 | { |
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 | 0 | 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 | 0 | nNextVertexId = GetOppositVertex(it->second, it->first); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | // return empty array |
238 | 0 | GNMPATH oRet; |
239 | 0 | return oRet; |
240 | 0 | } |
241 | | |
242 | | GNMPATH GNMGraph::DijkstraShortestPath(GNMGFID nStartFID, GNMGFID nEndFID) |
243 | 0 | { |
244 | 0 | return DijkstraShortestPath(nStartFID, nEndFID, m_mstEdges); |
245 | 0 | } |
246 | | |
247 | | std::vector<GNMPATH> GNMGraph::KShortestPaths(GNMGFID nStartFID, |
248 | | GNMGFID nEndFID, size_t nK) |
249 | 0 | { |
250 | | // Resulting array with paths. |
251 | | // A will be sorted by the path costs' descending. |
252 | 0 | std::vector<GNMPATH> A; |
253 | |
|
254 | 0 | 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 | 0 | 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 | 0 | GNMPATH aoFirstPath = DijkstraShortestPath(nStartFID, nEndFID); |
268 | 0 | if (aoFirstPath.empty()) |
269 | 0 | return A; // return empty array if there is no path between points. |
270 | | |
271 | 0 | A.push_back(std::move(aoFirstPath)); |
272 | |
|
273 | 0 | size_t i, k, l; |
274 | 0 | GNMPATH::iterator itAk, tempIt, itR; |
275 | 0 | std::vector<GNMPATH>::iterator itA; |
276 | 0 | std::map<GNMGFID, double>::iterator itDel; |
277 | 0 | GNMPATH aoRootPath, aoRootPathOther, aoSpurPath; |
278 | 0 | GNMGFID nSpurNode, nVertexToDel, nEdgeToDel; |
279 | 0 | double dfSumCost; |
280 | |
|
281 | 0 | std::map<GNMGFID, GNMStdEdge> mstEdges = m_mstEdges; |
282 | |
|
283 | 0 | for (k = 0; k < nK - 1; ++k) // -1 because we have already found one |
284 | 0 | { |
285 | 0 | std::map<GNMGFID, double> |
286 | 0 | mDeletedEdges; // for infinity costs assignment |
287 | 0 | itAk = A[k].begin(); |
288 | |
|
289 | 0 | for (i = 0; i < A[k].size() - 1; ++i) // avoid end node |
290 | 0 | { |
291 | | // Get the current node. |
292 | 0 | 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 | 0 | ++itAk; |
300 | |
|
301 | 0 | 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 | 0 | for (itA = A.begin(); itA != A.end(); ++itA) |
310 | 0 | { |
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 | 0 | if (i >= itA->size()) |
315 | 0 | continue; |
316 | | |
317 | | // + 1, because we will use std::vector::assign, which assigns |
318 | | // [..) range, not [..] |
319 | 0 | 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 | 0 | if ((aoRootPath == aoRootPathOther) && |
331 | 0 | (i < aoRootPathOther.size())) |
332 | 0 | { |
333 | 0 | tempIt = itA->begin() + i + 1; |
334 | 0 | mDeletedEdges.insert(std::make_pair( |
335 | 0 | tempIt->second, mstEdges[tempIt->second].dfDirCost)); |
336 | 0 | mstEdges[tempIt->second].dfDirCost = |
337 | 0 | std::numeric_limits<double>::infinity(); |
338 | 0 | } |
339 | 0 | } |
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 | 0 | for (itR = aoRootPath.begin(); itR != aoRootPath.end() - 1; ++itR) |
349 | 0 | { |
350 | 0 | nVertexToDel = itR->first; |
351 | 0 | for (l = 0; |
352 | 0 | l < m_mstVertices[nVertexToDel].anOutEdgeFIDs.size(); ++l) |
353 | 0 | { |
354 | 0 | nEdgeToDel = m_mstVertices[nVertexToDel].anOutEdgeFIDs[l]; |
355 | 0 | mDeletedEdges.insert(std::make_pair( |
356 | 0 | nEdgeToDel, mstEdges[nEdgeToDel].dfDirCost)); |
357 | 0 | mstEdges[nEdgeToDel].dfDirCost = |
358 | 0 | std::numeric_limits<double>::infinity(); |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | // Find the new best path in the modified graph. |
363 | 0 | 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 | 0 | for (itDel = mDeletedEdges.begin(); itDel != mDeletedEdges.end(); |
371 | 0 | ++itDel) |
372 | 0 | { |
373 | 0 | mstEdges[itDel->first].dfDirCost = itDel->second; |
374 | 0 | } |
375 | |
|
376 | 0 | 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 | 0 | if (!aoSpurPath.empty()) |
381 | 0 | { |
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 | 0 | aoRootPath.insert(aoRootPath.end(), aoSpurPath.begin() + 1, |
385 | 0 | aoSpurPath.end()); |
386 | | // Calculate the summary cost of the path. |
387 | | // TODO: get the summary cost from the Dejkstra method? |
388 | 0 | dfSumCost = 0.0; |
389 | 0 | for (itR = aoRootPath.begin(); itR != aoRootPath.end(); ++itR) |
390 | 0 | { |
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 | 0 | dfSumCost += mstEdges[itR->second].dfDirCost; |
396 | 0 | } |
397 | |
|
398 | 0 | B.insert(std::make_pair(dfSumCost, aoRootPath)); |
399 | 0 | } |
400 | 0 | } |
401 | |
|
402 | 0 | 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 | 0 | 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 | 0 | while (!B.empty() && B.begin()->second == A.back()) |
419 | 0 | { |
420 | 0 | B.erase(B.begin()); |
421 | 0 | } |
422 | 0 | } |
423 | |
|
424 | 0 | return A; |
425 | 0 | } |
426 | | |
427 | | GNMPATH GNMGraph::ConnectedComponents(const GNMVECTOR &anEmittersIDs) |
428 | 0 | { |
429 | 0 | GNMPATH anConnectedIDs; |
430 | |
|
431 | 0 | if (anEmittersIDs.empty()) |
432 | 0 | { |
433 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, "Emitters list is empty."); |
434 | 0 | return anConnectedIDs; |
435 | 0 | } |
436 | 0 | std::set<GNMGFID> anMarkedVertIDs; |
437 | |
|
438 | 0 | std::queue<GNMGFID> anStartQueue; |
439 | 0 | GNMVECTOR::const_iterator it; |
440 | 0 | for (it = anEmittersIDs.begin(); it != anEmittersIDs.end(); ++it) |
441 | 0 | { |
442 | 0 | anStartQueue.push(*it); |
443 | 0 | } |
444 | | |
445 | | // Begin the iterations of the Breadth-first search. |
446 | 0 | TraceTargets(anStartQueue, anMarkedVertIDs, anConnectedIDs); |
447 | |
|
448 | 0 | return anConnectedIDs; |
449 | 0 | } |
450 | | |
451 | | void GNMGraph::Clear() |
452 | 0 | { |
453 | 0 | m_mstVertices.clear(); |
454 | 0 | m_mstEdges.clear(); |
455 | 0 | } |
456 | | |
457 | | void GNMGraph::DijkstraShortestPathTree( |
458 | | GNMGFID nFID, const std::map<GNMGFID, GNMStdEdge> &mstEdges, |
459 | | std::map<GNMGFID, GNMGFID> &mnPathTree) |
460 | 0 | { |
461 | | // Initialize all vertices in graph with infinity mark. |
462 | 0 | double dfInfinity = std::numeric_limits<double>::infinity(); |
463 | |
|
464 | 0 | std::map<GNMGFID, double> mMarks; |
465 | 0 | std::map<GNMGFID, GNMStdVertex>::iterator itv; |
466 | 0 | for (itv = m_mstVertices.begin(); itv != m_mstVertices.end(); ++itv) |
467 | 0 | { |
468 | 0 | mMarks[itv->first] = dfInfinity; |
469 | 0 | } |
470 | |
|
471 | 0 | mMarks[nFID] = 0.0; |
472 | 0 | mnPathTree[nFID] = -1; |
473 | | |
474 | | // Initialize all vertices as unseen (there are no seen vertices). |
475 | 0 | 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 | 0 | std::multimap<double, GNMGFID> to_see; |
480 | 0 | std::multimap<double, GNMGFID>::iterator it; |
481 | 0 | to_see.insert(std::pair<double, GNMGFID>(0.0, nFID)); |
482 | 0 | LPGNMCONSTVECTOR panOutcomeEdgeId; |
483 | |
|
484 | 0 | size_t i; |
485 | 0 | GNMGFID nCurrentVertId, nCurrentEdgeId, nTargetVertId; |
486 | 0 | double dfCurrentEdgeCost, dfCurrentVertMark, dfNewVertexMark; |
487 | 0 | std::map<GNMGFID, GNMStdEdge>::const_iterator ite; |
488 | | |
489 | | // Continue iterations while there are some vertices to see. |
490 | 0 | while (!to_see.empty()) |
491 | 0 | { |
492 | | // We must see vertices with minimal costs at first. |
493 | | // In multimap the first cost is the minimal. |
494 | 0 | it = to_see.begin(); |
495 | |
|
496 | 0 | nCurrentVertId = it->second; |
497 | 0 | dfCurrentVertMark = it->first; |
498 | 0 | snSeen.insert(it->second); |
499 | 0 | to_see.erase(it); |
500 | | |
501 | | // For all neighbours for the current vertex. |
502 | 0 | panOutcomeEdgeId = GetOutEdges(nCurrentVertId); |
503 | 0 | if (nullptr == panOutcomeEdgeId) |
504 | 0 | continue; |
505 | | |
506 | 0 | for (i = 0; i < panOutcomeEdgeId->size(); ++i) |
507 | 0 | { |
508 | 0 | nCurrentEdgeId = panOutcomeEdgeId->operator[](i); |
509 | |
|
510 | 0 | ite = mstEdges.find(nCurrentEdgeId); |
511 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | dfNewVertexMark = dfCurrentVertMark + dfCurrentEdgeCost; |
525 | | |
526 | | // Update mark of the vertex if needed. |
527 | 0 | if (snSeen.find(nTargetVertId) == snSeen.end() && |
528 | 0 | dfNewVertexMark < mMarks[nTargetVertId] && |
529 | 0 | !CheckVertexBlocked(nTargetVertId)) |
530 | 0 | { |
531 | 0 | mMarks[nTargetVertId] = dfNewVertexMark; |
532 | 0 | mnPathTree[nTargetVertId] = nCurrentEdgeId; |
533 | | |
534 | | // The vertex with minimal cost will be inserted to the |
535 | | // beginning. |
536 | 0 | to_see.insert( |
537 | 0 | std::pair<double, GNMGFID>(dfNewVertexMark, nTargetVertId)); |
538 | 0 | } |
539 | 0 | } |
540 | 0 | } |
541 | 0 | } |
542 | | |
543 | | LPGNMCONSTVECTOR GNMGraph::GetOutEdges(GNMGFID nFID) const |
544 | 0 | { |
545 | 0 | std::map<GNMGFID, GNMStdVertex>::const_iterator it = |
546 | 0 | m_mstVertices.find(nFID); |
547 | 0 | if (it != m_mstVertices.end()) |
548 | 0 | return &it->second.anOutEdgeFIDs; |
549 | 0 | return nullptr; |
550 | 0 | } |
551 | | |
552 | | GNMGFID GNMGraph::GetOppositVertex(GNMGFID nEdgeFID, GNMGFID nVertexFID) const |
553 | 0 | { |
554 | 0 | std::map<GNMGFID, GNMStdEdge>::const_iterator it = |
555 | 0 | m_mstEdges.find(nEdgeFID); |
556 | 0 | if (it != m_mstEdges.end()) |
557 | 0 | { |
558 | 0 | if (nVertexFID == it->second.nSrcVertexFID) |
559 | 0 | { |
560 | 0 | return it->second.nTgtVertexFID; |
561 | 0 | } |
562 | 0 | else if (nVertexFID == it->second.nTgtVertexFID) |
563 | 0 | { |
564 | 0 | return it->second.nSrcVertexFID; |
565 | 0 | } |
566 | 0 | } |
567 | 0 | return -1; |
568 | 0 | } |
569 | | |
570 | | void GNMGraph::TraceTargets(std::queue<GNMGFID> &vertexQueue, |
571 | | std::set<GNMGFID> &markedVertIds, |
572 | | GNMPATH &connectedIds) |
573 | 0 | { |
574 | 0 | std::queue<GNMGFID> neighbours_queue; |
575 | | |
576 | | // See all given vertices except thouse that have been already seen. |
577 | 0 | while (!vertexQueue.empty()) |
578 | 0 | { |
579 | 0 | GNMGFID nCurVertID = vertexQueue.front(); |
580 | | |
581 | | // There may be duplicate unmarked vertices in a current queue. Check |
582 | | // it. |
583 | 0 | if (markedVertIds.find(nCurVertID) == markedVertIds.end()) |
584 | 0 | { |
585 | 0 | 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 | 0 | LPGNMCONSTVECTOR panOutcomeEdgeIDs = GetOutEdges(nCurVertID); |
591 | 0 | if (nullptr != panOutcomeEdgeIDs) |
592 | 0 | { |
593 | 0 | for (const GNMGFID nCurEdgeID : *panOutcomeEdgeIDs) |
594 | 0 | { |
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 | 0 | connectedIds.push_back( |
599 | 0 | 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 | 0 | 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 | 0 | nTargetVertID = GetOppositVertex(nCurEdgeID, nCurVertID); |
618 | | |
619 | | // Avoid marked or blocked vertices. |
620 | 0 | if ((markedVertIds.find(nTargetVertID) == |
621 | 0 | markedVertIds.end()) && |
622 | 0 | (!CheckVertexBlocked(nTargetVertID))) |
623 | 0 | neighbours_queue.push(nTargetVertID); |
624 | 0 | } |
625 | 0 | } |
626 | 0 | } |
627 | |
|
628 | 0 | vertexQueue.pop(); |
629 | 0 | } |
630 | |
|
631 | 0 | if (!neighbours_queue.empty()) |
632 | 0 | TraceTargets(neighbours_queue, markedVertIds, connectedIds); |
633 | 0 | } |
634 | | |
635 | | //! @endcond |