Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NBAlgorithms_Railway.cpp
15 : /// @author Jakob Erdmann
16 : /// @author Melanie Weber
17 : /// @date 29. March 2018
18 : ///
19 : // Algorithms for highway on-/off-ramps computation
20 : /****************************************************************************/
21 : #include <config.h>
22 :
23 : #include <cassert>
24 : #include <utils/options/OptionsCont.h>
25 : #include <utils/common/MsgHandler.h>
26 : #include <utils/common/ToString.h>
27 : #include <utils/common/StringUtils.h>
28 : #include <utils/iodevices/OutputDevice.h>
29 : #include <utils/iodevices/OutputDevice_String.h>
30 : #include <utils/router/DijkstraRouter.h>
31 : #include "NBNetBuilder.h"
32 : #include "NBAlgorithms.h"
33 : #include "NBNodeCont.h"
34 : #include "NBEdgeCont.h"
35 : #include "NBNode.h"
36 : #include "NBEdge.h"
37 : #include "NBPTStop.h"
38 : #include "NBVehicle.h"
39 : #include "NBAlgorithms_Railway.h"
40 :
41 : //#define DEBUG_SEQSTOREVERSE
42 : //#define DEBUG_DIRECTION_PRIORITY
43 :
44 : #define DEBUGNODEID "gneJ34"
45 : #define DEBUGNODEID2 "28842974"
46 : #define DEBUGEDGEID "22820560#0"
47 : #define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
48 :
49 : #define SHARP_THRESHOLD_SAMEDIR 100
50 : #define SHARP_THRESHOLD 80
51 :
52 :
53 : // ===========================================================================
54 : // method definitions
55 : // ===========================================================================
56 : // ---------------------------------------------------------------------------
57 : // Track methods
58 : // ---------------------------------------------------------------------------
59 : void
60 29592 : NBRailwayTopologyAnalyzer::Track::addSuccessor(Track* track) {
61 29592 : successors.push_back(track);
62 29592 : viaSuccessors.push_back(std::make_pair(track, nullptr));
63 29592 : minPermissions &= track->edge->getPermissions();
64 29592 : }
65 :
66 :
67 : const std::vector<NBRailwayTopologyAnalyzer::Track*>&
68 0 : NBRailwayTopologyAnalyzer::Track::getSuccessors(SUMOVehicleClass svc) const {
69 0 : if ((minPermissions & svc) != 0) {
70 0 : return successors;
71 : } else {
72 : if (svcSuccessors.count(svc) == 0) {
73 : std::vector<Track*> succ;
74 0 : for (Track* t : successors) {
75 0 : if ((t->edge->getPermissions() & svc) != 0) {
76 0 : succ.push_back(t);
77 : }
78 : }
79 0 : svcSuccessors[svc] = succ;
80 0 : }
81 0 : return svcSuccessors[svc];
82 : }
83 : }
84 :
85 :
86 : const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
87 6566 : NBRailwayTopologyAnalyzer::Track::getViaSuccessors(SUMOVehicleClass svc, bool /*ignoreTransientPermissions*/) const {
88 6566 : if ((minPermissions & svc) != 0) {
89 6566 : return viaSuccessors;
90 : } else {
91 : if (svcViaSuccessors.count(svc) == 0) {
92 0 : std::vector<std::pair<const Track*, const Track*> >& succ = svcViaSuccessors[svc];
93 0 : for (const Track* const t : successors) {
94 0 : if ((t->edge->getPermissions() & svc) != 0) {
95 0 : succ.push_back(std::make_pair(t, nullptr));
96 : }
97 : }
98 : }
99 0 : return svcViaSuccessors[svc];
100 : }
101 : }
102 :
103 :
104 : // ---------------------------------------------------------------------------
105 : // NBRailwayTopologyAnalyzer methods
106 : // ---------------------------------------------------------------------------
107 : void
108 29 : NBRailwayTopologyAnalyzer::analyzeTopology(NBEdgeCont& ec) {
109 29 : getBrokenRailNodes(ec, true);
110 29 : }
111 :
112 :
113 : int
114 39 : NBRailwayTopologyAnalyzer::repairTopology(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
115 39 : const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
116 : int addedBidi = 0;
117 39 : if (!minimal) {
118 35 : addedBidi += extendBidiEdges(ec);
119 35 : addedBidi += reverseEdges(ec, sc, lc); // technically not bidi but new edges nevertheless
120 35 : addedBidi += addBidiEdgesForBufferStops(ec);
121 35 : addedBidi += addBidiEdgesBetweenSwitches(ec);
122 : }
123 39 : if (lc.getLines().size() > 0) {
124 31 : addedBidi += addBidiEdgesForStops(ec, lc, sc, minimal);
125 : }
126 78 : if (OptionsCont::getOptions().getBool("railway.topology.repair.connect-straight")) {
127 3 : addedBidi += addBidiEdgesForStraightConnectivity(ec, true);
128 3 : addedBidi += addBidiEdgesForStraightConnectivity(ec, false);
129 3 : addedBidi += extendBidiEdges(ec);
130 : }
131 39 : return addedBidi;
132 : }
133 :
134 :
135 : int
136 7 : NBRailwayTopologyAnalyzer::makeAllBidi(NBEdgeCont& ec) {
137 7 : int numNotCenterEdges = 0;
138 7 : int numAddedBidiEdges = 0;
139 14 : std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
140 : std::vector<NBEdge*> edges;
141 7 : if (inputfile == "") {
142 81 : for (NBEdge* edge : ec.getAllEdges()) {
143 75 : edges.push_back(edge);
144 6 : }
145 : } else {
146 : std::set<std::string> edgeIDs;
147 1 : NBHelpers::loadEdgesFromFile(inputfile, edgeIDs);
148 26 : for (const std::string& edgeID : edgeIDs) {
149 25 : NBEdge* edge = ec.retrieve(edgeID);
150 25 : if (edge != nullptr) {
151 11 : edges.push_back(edge);
152 : }
153 : }
154 : }
155 93 : for (NBEdge* edge : edges) {
156 86 : if (hasRailway(edge->getPermissions())) {
157 : // rebuild connections if given from an earlier network
158 86 : edge->invalidateConnections(true);
159 86 : if (!edge->isBidiRail()) {
160 82 : if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
161 82 : NBEdge* e2 = addBidiEdge(ec, edge, false);
162 82 : if (e2 != nullptr) {
163 82 : numAddedBidiEdges++;
164 : }
165 : } else {
166 0 : numNotCenterEdges++;
167 : }
168 : }
169 : }
170 : }
171 14 : WRITE_MESSAGEF(TL("Added % bidi-edges to ensure that all tracks are usable in both directions."), toString(numAddedBidiEdges));
172 7 : if (numNotCenterEdges) {
173 0 : WRITE_WARNINGF(TL("Ignore % edges because they have the wrong spreadType"), toString(numNotCenterEdges));
174 : }
175 7 : return numAddedBidiEdges;
176 7 : }
177 :
178 :
179 : NBEdge*
180 1111 : NBRailwayTopologyAnalyzer::addBidiEdge(NBEdgeCont& ec, NBEdge* edge, bool update) {
181 : assert(edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER);
182 : assert(!edge->isBidiRail());
183 1111 : const std::string id2 = (edge->getID()[0] == '-'
184 1111 : ? edge->getID().substr(1)
185 1111 : : "-" + edge->getID());
186 1111 : if (ec.wasIgnored(id2)) {
187 : // we had it before so the warning is already there
188 : return nullptr;
189 : }
190 1108 : if (ec.retrieve(id2) == nullptr) {
191 : NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
192 1108 : edge, edge->getGeometry().reverse());
193 2216 : if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
194 714 : e2->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
195 : }
196 1108 : ec.insert(e2);
197 1108 : if (ec.retrieve(id2) == nullptr) {
198 3 : WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
199 1 : return nullptr;
200 : }
201 1107 : if (update) {
202 1025 : updateTurns(edge);
203 : // reconnected added edges
204 2931 : for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
205 1906 : if (hasRailway(incoming->getPermissions())) {
206 1735 : incoming->invalidateConnections(true);
207 : }
208 : }
209 : }
210 1107 : return e2;
211 : } else {
212 0 : WRITE_WARNINGF(TL("Could not add bidi-edge '%'."), id2);
213 0 : return nullptr;
214 : }
215 : }
216 :
217 :
218 : void
219 19354 : NBRailwayTopologyAnalyzer::getRailEdges(const NBNode* node,
220 : EdgeVector& inEdges, EdgeVector& outEdges) {
221 52730 : for (NBEdge* e : node->getIncomingEdges()) {
222 33376 : if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
223 31520 : inEdges.push_back(e);
224 : }
225 : }
226 52467 : for (NBEdge* e : node->getOutgoingEdges()) {
227 33113 : if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
228 31260 : outEdges.push_back(e);
229 : }
230 : }
231 19354 : }
232 :
233 :
234 : std::set<NBNode*>
235 140 : NBRailwayTopologyAnalyzer::getBrokenRailNodes(NBEdgeCont& ec, bool verbose) {
236 : std::set<NBNode*> brokenNodes;
237 140 : OutputDevice& device = OutputDevice::getDevice(verbose
238 309 : ? OptionsCont::getOptions().getString("railway.topology.output")
239 : : "/dev/null");
240 :
241 280 : device.writeXMLHeader("railwayTopology", "");
242 140 : std::set<NBNode*> railNodes = getRailNodes(ec, verbose);
243 : std::map<std::pair<int, int>, std::set<NBNode*, ComparatorIdLess> > types;
244 : std::set<NBEdge*, ComparatorIdLess> bidiEdges;
245 : std::set<NBEdge*, ComparatorIdLess> bufferStops;
246 6123 : for (NBNode* node : railNodes) {
247 : EdgeVector inEdges, outEdges;
248 5983 : getRailEdges(node, inEdges, outEdges);
249 11966 : types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
250 15249 : for (NBEdge* e : outEdges) {
251 11483 : if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
252 4527 : NBEdge* primary = e;
253 4527 : NBEdge* secondary = e->getTurnDestination(true);
254 4527 : if (e->getID()[0] == '-') {
255 : std::swap(primary, secondary);
256 3378 : } else if (primary->getID()[0] != '-' && secondary->getID()[0] != '-' && secondary->getID() < primary->getID()) {
257 : std::swap(primary, secondary);
258 : }
259 : if (bidiEdges.count(secondary) == 0) {
260 : // avoid duplicate when both ids start with '-'
261 : bidiEdges.insert(primary);
262 : }
263 : }
264 : }
265 5983 : }
266 :
267 140 : int numBrokenA = 0;
268 140 : int numBrokenB = 0;
269 140 : int numBrokenC = 0;
270 140 : int numBrokenD = 0;
271 140 : int numBufferStops = 0;
272 140 : if (verbose && types.size() > 0) {
273 58 : WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
274 : }
275 140 : device.openTag("legend");
276 140 : device.openTag("error");
277 140 : device.writeAttr(SUMO_ATTR_ID, "a");
278 140 : device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
279 140 : device.closeTag();
280 140 : device.openTag("error");
281 140 : device.writeAttr(SUMO_ATTR_ID, "b");
282 140 : device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
283 140 : device.closeTag();
284 140 : device.openTag("error");
285 140 : device.writeAttr(SUMO_ATTR_ID, "c");
286 140 : device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
287 140 : device.closeTag();
288 140 : device.openTag("error");
289 140 : device.writeAttr(SUMO_ATTR_ID, "d");
290 140 : device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
291 140 : device.closeTag();
292 280 : device.closeTag();
293 :
294 779 : for (auto it : types) {
295 639 : int numBrokenType = 0;
296 639 : device.openTag("railNodeType");
297 639 : int in = it.first.first;
298 639 : int out = it.first.second;
299 639 : device.writeAttr("in", in);
300 1278 : device.writeAttr("out", out);
301 6622 : for (NBNode* n : it.second) {
302 5983 : device.openTag(SUMO_TAG_NODE);
303 5983 : device.writeAttr(SUMO_ATTR_ID, n->getID());
304 : EdgeVector inRail, outRail;
305 5983 : getRailEdges(n, inRail, outRail);
306 : // check if there is a mismatch between angle and edge direction
307 : // (see above)
308 :
309 5983 : std::string broken = "";
310 5983 : if (in < 2 && hasStraightPair(n, outRail, outRail)) {
311 : broken += "a";
312 106 : numBrokenA++;
313 : }
314 5983 : if (out < 2 && hasStraightPair(n, inRail, inRail)) {
315 : broken += "b";
316 149 : numBrokenB++;
317 : }
318 5983 : if (out > 0) {
319 14409 : for (NBEdge* e : inRail) {
320 : EdgeVector tmp;
321 8844 : tmp.push_back(e);
322 8844 : if (allSharp(n, tmp, outRail)) {
323 : broken += "c";
324 90 : numBrokenC++;
325 : break;
326 : }
327 8844 : }
328 : }
329 5983 : if (in > 0) {
330 14361 : for (NBEdge* e : outRail) {
331 : EdgeVector tmp;
332 8815 : tmp.push_back(e);
333 8815 : if (allSharp(n, inRail, tmp)) {
334 : broken += "d";
335 71 : numBrokenD++;
336 : break;
337 : }
338 8815 : }
339 : }
340 : // do not mark bidi nodes as broken
341 5983 : if (((in == 1 && out == 1) || (in == 2 && out == 2))
342 10870 : && allBidi(inRail) && allBidi(outRail)) {
343 : broken = "";
344 : }
345 :
346 5983 : if (broken.size() > 0) {
347 514 : device.writeAttr("broken", broken);
348 : brokenNodes.insert(n);
349 257 : numBrokenType++;
350 : }
351 11966 : if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
352 39 : device.writeAttr("buffer_stop", "true");
353 39 : numBufferStops++;
354 : }
355 11966 : device.closeTag();
356 5983 : }
357 639 : device.closeTag();
358 639 : if (verbose) {
359 625 : WRITE_MESSAGE(" " + toString(it.first.first) + "," + toString(it.first.second)
360 : + " count: " + toString(it.second.size()) + " broken: " + toString(numBrokenType));
361 : }
362 :
363 : }
364 140 : if (verbose) {
365 203 : WRITE_MESSAGE("Found " + toString(brokenNodes.size()) + " broken railway nodes "
366 : + "(A=" + toString(numBrokenA)
367 : + " B=" + toString(numBrokenB)
368 : + " C=" + toString(numBrokenC)
369 : + " D=" + toString(numBrokenD)
370 : + ")");
371 58 : WRITE_MESSAGEF(TL("Found % railway nodes marked as buffer_stop"), toString(numBufferStops));
372 : }
373 :
374 3512 : for (NBEdge* e : bidiEdges) {
375 3372 : device.openTag("bidiEdge");
376 3372 : device.writeAttr(SUMO_ATTR_ID, e->getID());
377 3372 : device.writeAttr("bidi", e->getTurnDestination(true)->getID());
378 6744 : device.closeTag();
379 : }
380 140 : if (verbose) {
381 58 : WRITE_MESSAGEF(TL("Found % bidirectional rail edges"), toString(bidiEdges.size()));
382 : }
383 :
384 140 : device.close();
385 140 : return brokenNodes;
386 : }
387 :
388 :
389 : std::set<NBNode*>
390 206 : NBRailwayTopologyAnalyzer::getRailNodes(NBEdgeCont& ec, bool verbose) {
391 : std::set<NBNode*> railNodes;
392 206 : int numRailEdges = 0;
393 43919 : for (auto it = ec.begin(); it != ec.end(); it++) {
394 43713 : if (hasRailway(it->second->getPermissions())) {
395 15540 : numRailEdges++;
396 15540 : railNodes.insert(it->second->getFromNode());
397 15540 : railNodes.insert(it->second->getToNode());
398 : }
399 : }
400 206 : int numRailSignals = 0;
401 10245 : for (const NBNode* const node : railNodes) {
402 10039 : if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
403 2512 : numRailSignals++;
404 : }
405 : }
406 206 : if (verbose) {
407 58 : WRITE_MESSAGEF(TL("Found % railway edges and % railway nodes (% signals)."), toString(numRailEdges), toString(railNodes.size()), toString(numRailSignals));
408 : }
409 206 : return railNodes;
410 : }
411 :
412 :
413 : bool
414 44038 : NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
415 44038 : const double relAngle = NBHelpers::normRelAngle(e1->getAngleAtNode(node), e2->getAngleAtNode(node));
416 : /*
417 : std::cout << " isStraight n=" << node->getID()
418 : << " e1=" << e1->getID()
419 : << " e2=" << e2->getID()
420 : << " a1=" << e1->getAngleAtNode(node)
421 : << " a2=" << e2->getAngleAtNode(node)
422 : << " rel=" << relAngle
423 : << "\n";
424 : */
425 34790 : if ((e1->getToNode() == node && e2->getFromNode() == node)
426 46891 : || (e1->getFromNode() == node && e2->getToNode() == node)) {
427 : // edges go in the same direction
428 38337 : return fabs(relAngle) < SHARP_THRESHOLD;
429 : } else {
430 : // edges go in the opposite direction (both incoming or outgoing)
431 5701 : return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
432 : }
433 : }
434 :
435 :
436 : bool
437 4831 : NBRailwayTopologyAnalyzer::hasStraightPair(const NBNode* node, const EdgeVector& edges,
438 : const EdgeVector& edges2) {
439 : #ifdef DEBUG_SEQSTOREVERSE
440 : //if (node->getID() == DEBUGNODEID2) {
441 : // std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
442 : //}
443 : #endif
444 8980 : for (NBEdge* e1 : edges) {
445 9209 : for (NBEdge* e2 : edges2) {
446 : //if (e1->getID() == "195411601#2" && e2->getID() == "93584120#3") {
447 : // std::cout
448 : // << " DEBUG normRelA=" << NBHelpers::normRelAngle(
449 : // e1->getAngleAtNode(node),
450 : // e2->getAngleAtNode(node))
451 : // << "\n";
452 : //}
453 5060 : if (e1 != e2 && isStraight(node, e1, e2)) {
454 : return true;
455 : }
456 : }
457 : }
458 : return false;
459 : }
460 :
461 :
462 : bool
463 83 : NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
464 112 : for (NBEdge* e : in) {
465 70 : if (e != candOut && isStraight(node, e, candOut)) {
466 41 : if (gDebugFlag1) {
467 0 : std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
468 : }
469 : return false;
470 : }
471 : }
472 120 : for (NBEdge* e : out) {
473 84 : if (e != candOut && !isStraight(node, e, candOut)) {
474 6 : if (gDebugFlag1) {
475 0 : std::cout << " isSharp e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
476 : }
477 : return false;
478 : }
479 : }
480 : return true;
481 : }
482 :
483 :
484 : bool
485 18714 : NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
486 : bool allBidi = true;
487 23760 : for (NBEdge* e1 : in) {
488 30680 : for (NBEdge* e2 : out) {
489 25634 : if (e1 != e2 && isStraight(node, e1, e2)) {
490 : return false;
491 : }
492 8484 : if (!e1->isBidiRail(true)) {
493 : //std::cout << " allSharp node=" << node->getID() << " e1=" << e1->getID() << " is not bidi\n";
494 : allBidi = false;
495 : }
496 : }
497 : }
498 1564 : return !allBidi || countBidiAsSharp;
499 : }
500 :
501 :
502 : bool
503 8234 : NBRailwayTopologyAnalyzer::allBidi(const EdgeVector& edges) {
504 21296 : for (NBEdge* e : edges) {
505 14602 : if (!e->isBidiRail()) {
506 : return false;
507 : }
508 : }
509 : return true;
510 : }
511 :
512 :
513 : int
514 38 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec) {
515 38 : int added = 0;
516 8314 : for (auto it = ec.begin(); it != ec.end(); it++) {
517 8276 : NBEdge* e = it->second;
518 8276 : if (e->isBidiRail()) {
519 1853 : added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
520 1853 : added += extendBidiEdges(ec, e->getToNode(), e);
521 : }
522 : }
523 38 : if (added > 0) {
524 24 : WRITE_MESSAGEF(TL("Added % bidi-edges as extension of existing bidi edges."), toString(added));
525 : }
526 38 : return added;
527 : }
528 :
529 :
530 : int
531 4733 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec, NBNode* node, NBEdge* bidiIn) {
532 : assert(bidiIn->getToNode() == node);
533 4733 : NBEdge* bidiOut = bidiIn->getTurnDestination(true);
534 4733 : if (bidiOut == nullptr) {
535 0 : WRITE_WARNINGF(TL("Could not find bidi-edge for edge '%'"), bidiIn->getID());
536 0 : return 0;
537 : }
538 : EdgeVector tmpBidiOut;
539 4733 : tmpBidiOut.push_back(bidiOut);
540 : EdgeVector tmpBidiIn;
541 4733 : tmpBidiIn.push_back(bidiIn);
542 : int added = 0;
543 : EdgeVector inRail, outRail;
544 4733 : getRailEdges(node, inRail, outRail);
545 13600 : for (NBEdge* cand : outRail) {
546 : //std::cout << " extendBidiEdges n=" << node->getID() << " bidiIn=" << bidiIn->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, bidiIn, cand) << " allSharp=" << allSharp(node, inRail, tmpBidiOut, true) << "\n";
547 9340 : if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
548 435 : && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
549 9294 : && allSharp(node, inRail, tmpBidiOut, true)) {
550 387 : NBEdge* e2 = addBidiEdge(ec, cand);
551 387 : if (e2 != nullptr) {
552 387 : added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
553 : }
554 : }
555 : }
556 13794 : for (NBEdge* cand : inRail) {
557 : //std::cout << " extendBidiEdges n=" << node->getID() << " bidiOut=" << bidiOut->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, cand, bidiOut) << " allSharp=" << allSharp(node, outRail, tmpBidiIn, true) << "\n";
558 9728 : if (!cand->isBidiRail() && isStraight(node, cand, bidiOut)
559 637 : && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
560 9689 : && allSharp(node, outRail, tmpBidiIn, true)) {
561 584 : NBEdge* e2 = addBidiEdge(ec, cand);
562 584 : if (e2 != nullptr) {
563 582 : added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
564 : }
565 : }
566 : }
567 : return added;
568 4733 : }
569 :
570 :
571 : int
572 35 : NBRailwayTopologyAnalyzer::reverseEdges(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
573 35 : std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
574 : // find reversible edge sequences between broken nodes
575 : std::vector<EdgeVector> seqsToReverse;
576 : EdgeSet lineEdges;
577 174 : for (auto item : lc.getLines()) {
578 : const EdgeVector& route = item.second->getEdges();
579 : lineEdges.insert(route.begin(), route.end());
580 : }
581 112 : for (NBNode* n : brokenNodes) {
582 : EdgeVector inRail, outRail;
583 77 : getRailEdges(n, inRail, outRail);
584 175 : for (NBEdge* start : outRail) {
585 24 : if (lineEdges.count(start)) {
586 : // ptline edges should never be reversed. They are fixed by a different algorithm require their original ids and orientation
587 68 : continue;
588 : }
589 : EdgeVector tmp;
590 74 : tmp.push_back(start);
591 : // only reverse edges where the node would be unbroken afterwards
592 74 : if (!allBroken(n, start, inRail, outRail)
593 74 : || (inRail.size() == 1 && outRail.size() == 1)) {
594 : #ifdef DEBUG_SEQSTOREVERSE
595 : if (n->getID() == DEBUGNODEID) {
596 : std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
597 : }
598 : #endif
599 : continue;
600 : }
601 : //std::cout << " get sequences from " << start->getID() << "\n";
602 : bool forward = true;
603 : EdgeVector seq;
604 74 : while (forward) {
605 44 : seq.push_back(start);
606 : //std::cout << " seq=" << toString(seq) << "\n";
607 44 : NBNode* n2 = start->getToNode();
608 : EdgeVector inRail2, outRail2;
609 44 : getRailEdges(n2, inRail2, outRail2);
610 : if (brokenNodes.count(n2) != 0) {
611 : EdgeVector tmp2;
612 9 : tmp2.push_back(start);
613 9 : if (allBroken(n2, start, outRail2, inRail2)) {
614 6 : seqsToReverse.push_back(seq);
615 : } else {
616 : #ifdef DEBUG_SEQSTOREVERSE
617 : if (n->getID() == DEBUGNODEID) {
618 : std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
619 : }
620 : #endif
621 : }
622 : forward = false;
623 9 : } else {
624 35 : if (outRail2.size() == 0) {
625 : // stop at network border
626 : forward = false;
627 : #ifdef DEBUG_SEQSTOREVERSE
628 : if (n->getID() == DEBUGNODEID) {
629 : std::cout << " abort at n2=" << n2->getID() << " (border)\n";
630 : }
631 : #endif
632 19 : } else if (outRail2.size() > 1 || inRail2.size() > 1) {
633 : // stop at switch
634 : forward = false;
635 : #ifdef DEBUG_SEQSTOREVERSE
636 : if (n->getID() == DEBUGNODEID) {
637 : std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
638 : }
639 : #endif
640 : } else {
641 14 : start = outRail2.front();
642 : }
643 : }
644 44 : }
645 74 : }
646 77 : }
647 : // sort by sequence length
648 35 : if (seqsToReverse.size() > 0) {
649 8 : WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
650 : }
651 35 : std::sort(seqsToReverse.begin(), seqsToReverse.end(),
652 : [](const EdgeVector & a, const EdgeVector & b) {
653 : return a.size() < b.size();
654 : });
655 35 : int numReversed = 0;
656 : std::set<NBNode*> affectedEndpoints;
657 : std::set<std::string> reversedIDs;
658 : std::map<int, int> seqLengths;
659 41 : for (EdgeVector& seq : seqsToReverse) {
660 6 : NBNode* seqStart = seq.front()->getFromNode();
661 6 : NBNode* seqEnd = seq.back()->getToNode();
662 : // avoid reversing sequences on both sides of a broken node
663 : if (affectedEndpoints.count(seqStart) == 0
664 : && affectedEndpoints.count(seqEnd) == 0) {
665 : affectedEndpoints.insert(seqStart);
666 : affectedEndpoints.insert(seqEnd);
667 : //WRITE_MESSAGE(" reversed seq=" + toString(seq));
668 15 : for (NBEdge* e : seq) {
669 9 : e->reinitNodes(e->getToNode(), e->getFromNode());
670 9 : e->setGeometry(e->getGeometry().reverse());
671 9 : reversedIDs.insert(e->getID());
672 18 : if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
673 0 : e->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
674 : }
675 : }
676 6 : seqLengths[(int)seq.size()]++;
677 6 : numReversed++;
678 : }
679 : }
680 35 : if (numReversed > 0) {
681 8 : WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
682 137 : for (auto& item : sc.getStops()) {
683 133 : if (reversedIDs.count(item.second->getEdgeId())) {
684 0 : item.second->findLaneAndComputeBusStopExtent(ec);
685 : }
686 : }
687 : }
688 35 : return numReversed;
689 35 : }
690 :
691 :
692 : int
693 35 : NBRailwayTopologyAnalyzer::addBidiEdgesForBufferStops(NBEdgeCont& ec) {
694 35 : std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
695 35 : std::set<NBNode*> railNodes = getRailNodes(ec);
696 : // find buffer stops and ensure that they are connect to the network in both directions
697 35 : int numBufferStops = 0;
698 35 : int numAddedBidiTotal = 0;
699 1872 : for (NBNode* node : railNodes) {
700 3674 : if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
701 10 : if (node->getEdges().size() != 1) {
702 3 : WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
703 1 : continue;
704 : }
705 : // int numAddedBidi = 0;
706 9 : numBufferStops++;
707 : NBEdge* prev = nullptr;
708 : NBEdge* prev2 = nullptr;
709 : EdgeVector inRail, outRail;
710 9 : getRailEdges(node, inRail, outRail);
711 : bool addAway = true; // add new edges away from buffer stop
712 22 : while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
713 : NBEdge* e = nullptr;
714 13 : if (prev == nullptr) {
715 : assert(node->getEdges().size() == 1);
716 9 : e = node->getEdges().front();
717 9 : addAway = node == e->getToNode();
718 : } else {
719 4 : if (addAway) {
720 : // XXX if node is broken we need to switch direction
721 : assert(inRail.size() == 2);
722 3 : e = inRail.front() == prev2 ? inRail.back() : inRail.front();
723 : } else {
724 : // XXX if node is broken we need to switch direction
725 : assert(outRail.size() == 2);
726 1 : e = outRail.front() == prev2 ? outRail.back() : outRail.front();
727 : }
728 : }
729 13 : e->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
730 : NBNode* e2From = nullptr;
731 : NBNode* e2To = nullptr;
732 13 : if (addAway) {
733 : e2From = node;
734 : e2To = e->getFromNode();
735 : node = e2To;
736 : } else {
737 : e2From = e->getToNode();
738 : e2To = node;
739 : node = e2From;
740 : }
741 13 : NBEdge* e2 = addBidiEdge(ec, e);
742 13 : if (e2 == nullptr) {
743 : break;
744 : }
745 : prev = e;
746 : prev2 = e2;
747 : // numAddedBidi++;
748 13 : numAddedBidiTotal++;
749 : inRail.clear();
750 : outRail.clear();
751 13 : getRailEdges(node, inRail, outRail);
752 : }
753 : //if (numAddedBidi > 0) {
754 : // WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
755 : //}
756 9 : }
757 : }
758 35 : if (numAddedBidiTotal > 0) {
759 10 : WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
760 : }
761 35 : return numAddedBidiTotal;
762 : }
763 :
764 : NBEdge*
765 106 : NBRailwayTopologyAnalyzer::isBidiSwitch(const NBNode* n) {
766 : EdgeVector inRail, outRail;
767 106 : getRailEdges(n, inRail, outRail);
768 106 : if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
769 28 : if (isStraight(n, inRail.front(), outRail.front())) {
770 23 : return inRail.front();
771 5 : } else if (isStraight(n, inRail.back(), outRail.front())) {
772 5 : return inRail.back();
773 : }
774 : }
775 78 : if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
776 19 : if (isStraight(n, outRail.front(), inRail.front())) {
777 10 : return outRail.front();
778 9 : } else if (isStraight(n, outRail.back(), inRail.front())) {
779 9 : return outRail.back();
780 : }
781 : }
782 : return nullptr;
783 106 : }
784 :
785 :
786 : int
787 35 : NBRailwayTopologyAnalyzer::addBidiEdgesBetweenSwitches(NBEdgeCont& ec) {
788 35 : std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
789 : std::map<int, int> seqLengths;
790 35 : int numAdded = 0;
791 35 : int numSeqs = 0;
792 101 : for (NBNode* n : brokenNodes) {
793 66 : NBEdge* edge = isBidiSwitch(n);
794 66 : if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
795 : std::vector<NBNode*> nodeSeq;
796 : EdgeVector edgeSeq;
797 40 : NBNode* prev = n;
798 40 : nodeSeq.push_back(prev);
799 40 : edgeSeq.push_back(edge);
800 : bool forward = true;
801 : //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
802 : // find a suitable end point for adding bidi edges
803 129 : while (forward) {
804 89 : NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
805 : EdgeVector allRail;
806 89 : getRailEdges(next, allRail, allRail);
807 89 : if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
808 49 : prev = next;
809 49 : edge = allRail.front() == edge ? allRail.back() : allRail.front();
810 49 : nodeSeq.push_back(prev);
811 49 : edgeSeq.push_back(edge);
812 : } else {
813 : forward = false;
814 : EdgeVector inRail2, outRail2;
815 40 : getRailEdges(next, inRail2, outRail2);
816 40 : if (isBidiSwitch(next) == edge) {
817 : // suitable switch found as endpoint, add reverse edges
818 : //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
819 : // + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
820 10 : for (NBEdge* e : edgeSeq) {
821 6 : addBidiEdge(ec, e);
822 : }
823 4 : seqLengths[(int)edgeSeq.size()]++;
824 4 : numSeqs++;
825 4 : numAdded += (int)edgeSeq.size();
826 : } else {
827 : //std::cout << " sequence ended at junction " << next->getID()
828 : // << " in=" << inRail2.size()
829 : // << " out=" << outRail2.size()
830 : // << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
831 : // << "\n";
832 : }
833 :
834 40 : }
835 89 : }
836 :
837 40 : }
838 : }
839 35 : if (seqLengths.size() > 0) {
840 4 : WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
841 : }
842 70 : return numAdded;
843 : }
844 :
845 :
846 : std::set<NBPTLine*>
847 31 : NBRailwayTopologyAnalyzer::findBidiCandidates(NBPTLineCont& lc) {
848 : std::set<NBPTLine*> result;
849 : std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
850 195 : for (const auto& item : lc.getLines()) {
851 164 : const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
852 164 : if (stops.size() > 1) {
853 659 : for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
854 : std::shared_ptr<NBPTStop> fromStop = *it;
855 : std::shared_ptr<NBPTStop> toStop = *(it + 1);
856 517 : visited.insert({fromStop, toStop});
857 : }
858 : }
859 : }
860 195 : for (const auto& item : lc.getLines()) {
861 164 : const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
862 164 : if (stops.size() > 1) {
863 581 : for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
864 : std::shared_ptr<NBPTStop> fromStop = *it;
865 : std::shared_ptr<NBPTStop> toStop = *(it + 1);
866 : std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
867 : if (visited.count(reverseTrip)) {
868 14 : result.insert(item.second);
869 : break;
870 : }
871 453 : }
872 : }
873 : }
874 31 : return result;
875 : }
876 :
877 : int
878 31 : NBRailwayTopologyAnalyzer::addBidiEdgesForStops(NBEdgeCont& ec, NBPTLineCont& lc, NBPTStopCont& sc, bool minimal) {
879 62 : const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
880 : // generate bidirectional routing graph
881 : std::vector<Track*> tracks;
882 9066 : for (NBEdge* edge : ec.getAllEdges()) {
883 18070 : tracks.push_back(new Track(edge));
884 31 : }
885 31 : const int numEdges = (int)tracks.size();
886 9066 : for (NBEdge* edge : ec.getAllEdges()) {
887 18070 : tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
888 31 : }
889 : // add special tracks for starting end ending in both directions
890 : std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
891 9066 : for (NBEdge* edge : ec.getAllEdges()) {
892 9035 : if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
893 3384 : Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
894 3384 : tracks.push_back(start);
895 3384 : Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
896 3384 : tracks.push_back(end);
897 3384 : stopTracks[edge] = {start, end};
898 : }
899 31 : }
900 : // set successors based on angle (connections are not yet built)
901 2250 : for (NBNode* node : getRailNodes(ec)) {
902 : EdgeVector railEdges;
903 2219 : getRailEdges(node, railEdges, railEdges);
904 8986 : for (NBEdge* e1 : railEdges) {
905 29834 : for (NBEdge* e2 : railEdges) {
906 23067 : if (e1 != e2 && isStraight(node, e1, e2)) {
907 11332 : int i = e1->getNumericalID();
908 11332 : int i2 = e2->getNumericalID();
909 11332 : if (e1->getToNode() == node) {
910 5676 : if (e2->getFromNode() == node) {
911 : // case 1) plain forward connection
912 3304 : tracks[i]->addSuccessor(tracks[i2]);
913 : // reverse edge (numerical id incremented by numEdges)
914 3304 : tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
915 : } else {
916 : // case 2) both edges pointing towards each other
917 2372 : tracks[i]->addSuccessor(tracks[i2 + numEdges]);
918 2372 : tracks[i2]->addSuccessor(tracks[i + numEdges]);
919 : }
920 : } else {
921 5656 : if (e2->getFromNode() == node) {
922 : // case 3) both edges pointing away from each other
923 2352 : tracks[i + numEdges]->addSuccessor(tracks[i2]);
924 2352 : tracks[i2 + numEdges]->addSuccessor(tracks[i]);
925 : } else {
926 : // already handled via case 1)
927 : }
928 : }
929 :
930 : }
931 : }
932 : }
933 2219 : }
934 : // define start and end successors
935 3415 : for (auto& item : stopTracks) {
936 3384 : const int index = item.first->getNumericalID();
937 : // start
938 3384 : item.second.first->addSuccessor(tracks[index]);
939 3384 : item.second.first->addSuccessor(tracks[index + numEdges]);
940 : // end
941 3384 : tracks[index]->addSuccessor(item.second.second);
942 3384 : tracks[index + numEdges]->addSuccessor(item.second.second);
943 : }
944 : // DEBUG
945 : /*
946 : for (Track* t : tracks) {
947 : std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
948 : for (Track* s : t->getSuccessors(SVC_IGNORING)) {
949 : std::cout << " succ=" << s->getID() << "\n";
950 : }
951 : }
952 : */
953 :
954 : SUMOAbstractRouter<Track, NBVehicle>* const router = new DijkstraRouter<Track, NBVehicle>(
955 31 : tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
956 :
957 31 : int added = 0;
958 31 : int numDisconnected = 0;
959 : std::set<std::shared_ptr<NBPTStop>> addBidiStops;
960 : std::set<NBEdge*, ComparatorIdLess> addBidiStopEdges;
961 : std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
962 : std::set<std::pair<std::string, std::string> > visited;
963 :
964 : // the isConsistent heuristic may fail in some cases. If we observe that a
965 : // specific sequence of stop ids in encoded in both directions, we take this
966 : // as a reason to overrule the heuristic
967 31 : std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
968 :
969 195 : for (const auto& item : lc.getLines()) {
970 164 : NBPTLine* line = item.second;
971 164 : std::vector<NBPTLine::PTStopInfo> stops = line->getStopEdges(ec);
972 : std::vector<NBEdge*> stopEdges;
973 844 : for (auto it : stops) {
974 680 : stopEdges.push_back(it.edge);
975 : }
976 164 : NBEdge* routeStart = line->getRouteStart(ec);
977 164 : NBEdge* routeEnd = line->getRouteEnd(ec);
978 164 : if (routeStart != nullptr && (stopEdges.empty() || routeStart != stopEdges.front())) {
979 160 : stops.insert(stops.begin(), NBPTLine::PTStopInfo(routeStart, routeStart->getID(), 0, false));
980 : }
981 164 : if (routeEnd != nullptr && (stopEdges.empty() || routeEnd != stopEdges.back())) {
982 174 : stops.push_back(NBPTLine::PTStopInfo(routeEnd, routeEnd->getID(), routeEnd->getLength(), false));
983 : }
984 164 : if (stops.size() < 2) {
985 7 : continue;
986 : }
987 314 : if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
988 27 : WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
989 9 : continue;
990 : }
991 796 : for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
992 648 : NBEdge* fromEdge = it->edge;
993 648 : NBEdge* toEdge = (it + 1)->edge;
994 : const std::string fromStop = it->stopID;
995 : const std::string toStop = (it + 1)->stopID;
996 648 : const double fromPos = it->pos;
997 648 : const double toPos = (it + 1)->pos;
998 648 : bool fromRevised = it->revised;
999 648 : bool toRevised = (it + 1)->revised;
1000 648 : std::pair<std::string, std::string> trip(fromStop, toStop);
1001 648 : std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
1002 : //if (line->getLineID() == "147471") {
1003 : // std::cout << " line=" << line->getLineID() << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << " fromStop=" << fromStop << " toStop=" << toStop << " fromRevised=" << fromRevised << " toRevised=" << toRevised << "\n";
1004 : //}
1005 222 : if (visited.count(trip) != 0) {
1006 222 : continue;
1007 : } else {
1008 : visited.insert(trip);
1009 : }
1010 98 : if (stopTracks.count(fromEdge) == 0
1011 : || stopTracks.count(toEdge) == 0) {
1012 98 : continue;
1013 : }
1014 328 : bool needBidi = visited.count(reverseTrip) != 0;
1015 328 : NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
1016 : std::vector<const Track*> route;
1017 328 : Track* from = fromRevised ? tracks[fromEdge->getNumericalID()] : stopTracks[fromEdge].first;
1018 328 : Track* to = toRevised ? tracks[toEdge->getNumericalID()] : stopTracks[toEdge].second;
1019 328 : int iStart = fromRevised ? 0 : 1;
1020 328 : int iDeltaEnd = toRevised ? 0 : 1;
1021 328 : if (fromEdge == toEdge && fromPos > toPos) {
1022 : // must use reverse edge
1023 18 : route.push_back(tracks[fromEdge->getNumericalID() + numEdges]);
1024 : iStart = 0;
1025 : iDeltaEnd = 0;
1026 : needBidi = true;
1027 : } else {
1028 310 : router->compute(from, to, &veh, 0, route);
1029 : }
1030 : //if (line->getLineID() == "147471") {
1031 : // std::cout << "DEBUG: route=" << toString(route) << "\n";
1032 : //}
1033 328 : if (route.size() > 0) {
1034 : assert((int)route.size() > iStart + iDeltaEnd);
1035 2665 : for (int i = iStart; i < (int)route.size() - iDeltaEnd; ++i) {
1036 2337 : const bool isBidi = route[i]->getNumericalID() >= numEdges;
1037 2337 : bool isStop = i == iStart || i == (int)route.size() - 1 - iDeltaEnd;
1038 2337 : if (isBidi || needBidi) {
1039 129 : NBEdge* edge = route[i]->edge;
1040 : if (addBidiEdges.count(edge) == 0) {
1041 105 : if (!edge->isBidiRail(true)) {
1042 79 : if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
1043 : addBidiEdges.insert(edge);
1044 79 : if (isStop) {
1045 : addBidiStopEdges.insert(edge);
1046 : }
1047 : } else {
1048 0 : if (isStop) {
1049 0 : WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
1050 : }
1051 : }
1052 : }
1053 : }
1054 : }
1055 86 : if (isStop && isBidi) {
1056 146 : std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
1057 73 : if (fs) {
1058 : addBidiStops.insert(fs);
1059 : }
1060 146 : std::shared_ptr<NBPTStop> ts = sc.get(toStop);
1061 73 : if (ts) {
1062 : addBidiStops.insert(ts);
1063 : }
1064 : }
1065 : }
1066 : } else {
1067 0 : WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
1068 0 : numDisconnected++;
1069 : }
1070 328 : }
1071 164 : }
1072 110 : for (NBEdge* edge : addBidiEdges) {
1073 79 : if (!edge->isBidiRail()) {
1074 33 : NBEdge* e2 = addBidiEdge(ec, edge);
1075 : //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
1076 33 : if (e2 != nullptr) {
1077 33 : added++;
1078 33 : if (!minimal) {
1079 23 : added += extendBidiEdges(ec, edge->getToNode(), edge);
1080 23 : added += extendBidiEdges(ec, edge->getFromNode(), e2);
1081 : }
1082 : }
1083 : }
1084 : }
1085 110 : for (std::shared_ptr<NBPTStop> stop : addBidiStops) {
1086 158 : std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(stop, ec);
1087 79 : if (fromReverse) {
1088 140 : sc.insert(fromReverse);
1089 70 : stop->setBidiStop(fromReverse);
1090 : }
1091 : }
1092 31 : if (addBidiEdges.size() > 0 || numDisconnected > 0) {
1093 95 : WRITE_MESSAGE("Added " + toString(addBidiStopEdges.size()) + " bidi-edges for public transport stops and a total of "
1094 : + toString(added) + " bidi-edges to ensure connectivity of stops ("
1095 : + toString(numDisconnected) + " stops remain disconnected)");
1096 : }
1097 :
1098 : // clean up
1099 24869 : for (Track* t : tracks) {
1100 24838 : delete t;
1101 : }
1102 31 : delete router;
1103 62 : return (int)addBidiEdges.size();
1104 31 : }
1105 :
1106 :
1107 : int
1108 6 : NBRailwayTopologyAnalyzer::addBidiEdgesForStraightConnectivity(NBEdgeCont& ec, bool geometryLike) {
1109 6 : int added = 0;
1110 6 : std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
1111 107 : for (const auto& e : ec) {
1112 101 : if (!hasRailway(e.second->getPermissions())) {
1113 72 : continue;
1114 : }
1115 101 : NBNode* const from = e.second->getFromNode();
1116 : NBNode* const to = e.second->getToNode();
1117 59 : if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
1118 59 : continue;
1119 : }
1120 42 : if (e.second->isBidiRail()) {
1121 13 : continue;
1122 : }
1123 : EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
1124 29 : getRailEdges(from, inRailFrom, outRailFrom);
1125 29 : getRailEdges(to, inRailTo, outRailTo);
1126 : // check whether there is a straight edge pointing away from this one at the from-node
1127 : // and there is no straight incoming edge at the from-node
1128 : bool haveStraight = false;
1129 : bool haveStraightReverse = false;
1130 29 : if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
1131 25 : for (const NBEdge* fromStraightCand : outRailFrom) {
1132 17 : if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1133 : haveStraightReverse = true;
1134 : //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
1135 : break;
1136 : }
1137 : }
1138 12 : if (haveStraightReverse) {
1139 5 : for (const NBEdge* fromStraightCand : inRailFrom) {
1140 4 : if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1141 : haveStraight = true;
1142 : //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
1143 : break;
1144 : }
1145 : }
1146 : }
1147 : }
1148 29 : if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
1149 : // check whether there is a straight edge pointing towards this one at the to-node
1150 : // and there is no straight outgoing edge at the to-node
1151 : haveStraight = false;
1152 : haveStraightReverse = false;
1153 26 : for (const NBEdge* toStraightCand : inRailTo) {
1154 22 : if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1155 : haveStraightReverse = true;
1156 : //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
1157 : break;
1158 : }
1159 : }
1160 13 : if (haveStraightReverse) {
1161 13 : for (const NBEdge* toStraightCand : outRailTo) {
1162 8 : if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1163 : haveStraight = true;
1164 : //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
1165 : break;
1166 : }
1167 : }
1168 : }
1169 : }
1170 : //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
1171 29 : if (haveStraightReverse && !haveStraight) {
1172 6 : NBEdge* e2 = addBidiEdge(ec, e.second);
1173 : //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
1174 6 : if (e2 != nullptr) {
1175 6 : added++;
1176 6 : added += extendBidiEdges(ec, to, e.second);
1177 6 : added += extendBidiEdges(ec, from, e2);
1178 : }
1179 : }
1180 29 : }
1181 6 : if (added > 0) {
1182 4 : if (geometryLike) {
1183 4 : WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
1184 : } else {
1185 4 : WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
1186 : }
1187 : }
1188 6 : return added;
1189 : }
1190 :
1191 :
1192 : void
1193 1025 : NBRailwayTopologyAnalyzer::updateTurns(NBEdge* edge) {
1194 1025 : NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getFromNode(), false);
1195 1025 : NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getToNode(), false);
1196 1025 : }
1197 :
1198 :
1199 : double
1200 6566 : NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1201 6566 : return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
1202 : }
1203 :
1204 :
1205 : void
1206 4 : NBRailwayTopologyAnalyzer::extendDirectionPriority(NBEdgeCont& ec, bool fromUniDir) {
1207 : // if fromUniDir=true, assign priority value for each railway edge:
1208 : // 4: edge is unidirectional
1209 : // 3: edge is in main direction of bidirectional track
1210 : // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
1211 : // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
1212 : // 0: edge is part of bidirectional track in reverse of main direction
1213 : //
1214 : // otherwise:
1215 : // assign priority value for each railway edge with priority -1 (undefined):
1216 : // x: edges with priority >= 0 keep their priority
1217 : // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
1218 : // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
1219 : // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
1220 : // x-4 : edge is reverse direction of an x-1 edge
1221 :
1222 : std::set<NBEdge*, ComparatorIdLess> bidi;
1223 : EdgeSet uni;
1224 174 : for (NBEdge* edge : ec.getAllEdges()) {
1225 170 : if (hasRailway(edge->getPermissions())) {
1226 170 : if (fromUniDir) {
1227 46 : if (!edge->isBidiRail()) {
1228 : edge->setPriority(4);
1229 28 : edge->setRoutingType("4");
1230 : uni.insert(edge);
1231 : } else {
1232 : bidi.insert(edge);
1233 : }
1234 : } else {
1235 124 : if (edge->getPriority() >= 0) {
1236 : uni.insert(edge);
1237 : } else {
1238 : bidi.insert(edge);
1239 : }
1240 : }
1241 : }
1242 4 : }
1243 :
1244 4 : if (uni.size() == 0) {
1245 0 : if (bidi.size() != 0) {
1246 0 : WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
1247 : }
1248 : return;
1249 : }
1250 : EdgeSet seen;
1251 : EdgeSet check = uni;
1252 : EdgeSet forward;
1253 120 : while (!check.empty()) {
1254 116 : NBEdge* edge = *check.begin();
1255 : check.erase(edge);
1256 48 : if (seen.count(edge) != 0) {
1257 48 : continue;
1258 : }
1259 : seen.insert(edge);
1260 68 : NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
1261 68 : if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
1262 : forward.insert(straightOut);
1263 : check.insert(straightOut);
1264 : }
1265 68 : NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
1266 68 : if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
1267 : forward.insert(straightIn);
1268 : check.insert(straightIn);
1269 : }
1270 : #ifdef DEBUG_DIRECTION_PRIORITY
1271 : std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
1272 : << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
1273 : << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
1274 : << "\n";
1275 : #endif
1276 : }
1277 :
1278 130 : for (NBEdge* edge : bidi) {
1279 126 : NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
1280 : int prio;
1281 : int bidiPrio;
1282 : if (forward.count(edge) != 0) {
1283 : if (forward.count(bidiEdge) == 0) {
1284 12 : prio = 3;
1285 12 : bidiPrio = 0;
1286 : } else {
1287 : // both forward
1288 12 : prio = 2;
1289 12 : bidiPrio = 2;
1290 : }
1291 : } else {
1292 : if (forward.count(bidiEdge) != 0) {
1293 25 : prio = 0;
1294 25 : bidiPrio = 3;
1295 : } else {
1296 : // neither forward
1297 77 : prio = 1;
1298 77 : bidiPrio = 1;
1299 : }
1300 : }
1301 126 : if (bidiEdge == nullptr) {
1302 6 : WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
1303 : }
1304 126 : if (edge->getPriority() >= 0) {
1305 55 : bidiPrio = 0;
1306 : }
1307 126 : if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
1308 69 : prio = 0;
1309 : }
1310 126 : if (edge->getPriority() < 0) {
1311 71 : edge->setPriority(prio);
1312 142 : edge->setRoutingType(toString(prio));
1313 : }
1314 126 : if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
1315 55 : bidiEdge->setPriority(bidiPrio);
1316 110 : bidiEdge->setRoutingType(toString(bidiPrio));
1317 : }
1318 : }
1319 : std::map<int, int> numPrios;
1320 130 : for (NBEdge* edge : bidi) {
1321 126 : numPrios[edge->getPriority()]++;
1322 : }
1323 4 : if (fromUniDir) {
1324 3 : WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1325 : } else {
1326 9 : WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1327 : }
1328 : }
1329 :
1330 : // ---------------------------------------------------------------------------
1331 : // NBRailwaySignalGuesser methods
1332 : // ---------------------------------------------------------------------------
1333 :
1334 : int
1335 1841 : NBRailwaySignalGuesser::guessRailSignals(NBEdgeCont& ec, NBPTStopCont& sc) {
1336 1841 : const OptionsCont& oc = OptionsCont::getOptions();
1337 : int addedSignals = 0;
1338 3682 : if (oc.exists("railway.signal.guess.by-stops")) {
1339 3506 : if (oc.getBool("railway.signal.guess.by-stops")) {
1340 1 : const double minLength = oc.getFloat("osm.stop-output.length.train");
1341 1 : addedSignals += guessByStops(ec, sc, minLength);
1342 : }
1343 : }
1344 1841 : return addedSignals;
1345 : }
1346 :
1347 :
1348 : bool
1349 6 : NBRailwaySignalGuesser::canBeSignal(const NBNode* node) {
1350 6 : return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
1351 : }
1352 :
1353 : int
1354 1 : NBRailwaySignalGuesser::guessByStops(NBEdgeCont& ec, NBPTStopCont& sc, double minLength) {
1355 : int addedSignals = 0;
1356 5 : for (auto& item : sc.getStops()) {
1357 4 : const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
1358 4 : if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
1359 : NBNode* to = stopEdge->getToNode();
1360 4 : if (canBeSignal(to)) {
1361 2 : to->reinit(to->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1362 2 : addedSignals++;
1363 : }
1364 : NBNode* from = stopEdge->getFromNode();
1365 4 : if (stopEdge->getLoadedLength() >= minLength) {
1366 : /// XXX should split edge if it is too long
1367 2 : if (canBeSignal(from)) {
1368 0 : from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1369 0 : addedSignals++;
1370 : }
1371 : } else {
1372 2 : double searchDist = minLength - stopEdge->getLoadedLength();
1373 4 : while (searchDist > 0 && from->geometryLike()) {
1374 3 : for (const NBEdge* in : from->getIncomingEdges()) {
1375 3 : if (in->getFromNode() != stopEdge->getToNode()) {
1376 : // found edge that isn't a bidi predecessor
1377 : stopEdge = in;
1378 : break;
1379 : }
1380 : }
1381 2 : if (stopEdge->getFromNode() == from) {
1382 : // bidi edge without predecessor
1383 : break;
1384 : } else {
1385 : from = stopEdge->getFromNode();
1386 : }
1387 2 : searchDist -= stopEdge->getLoadedLength();
1388 : }
1389 2 : if (searchDist <= 0 && canBeSignal(from)) {
1390 0 : from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1391 0 : addedSignals++;
1392 : }
1393 : }
1394 : }
1395 : }
1396 1 : WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
1397 1 : return addedSignals;
1398 : }
1399 :
1400 :
1401 : int
1402 0 : NBRailwayGeometryHelper::straigthenCorrdidor(NBEdgeCont& ec, double maxAngle) {
1403 : int moved = 0;
1404 : int numCorridors = 0;
1405 0 : std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
1406 : std::set<NBNode*> railGeomNodes;
1407 0 : for (NBNode* n : railNodes) {
1408 0 : if (n->geometryLike()) {
1409 : railGeomNodes.insert(n);
1410 : }
1411 : }
1412 : std::set<NBNode*, ComparatorIdLess> kinkNodes;;
1413 0 : for (NBNode* n : railGeomNodes) {
1414 0 : NBEdge* in = n->getIncomingEdges().front();
1415 0 : NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1416 0 : const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
1417 0 : if (maxAngle > 0 && relAngle > maxAngle) {
1418 : kinkNodes.insert(n);
1419 : }
1420 : }
1421 0 : while (!kinkNodes.empty()) {
1422 : std::vector<NBNode*> corridor;
1423 : std::vector<NBEdge*> corridorEdges;
1424 0 : Boundary corridorBox;
1425 : double length = 0;
1426 0 : NBNode* n = *kinkNodes.begin();
1427 : kinkNodes.erase(kinkNodes.begin());
1428 : // go downstream and upstream, add kinkNodes until a "long" enough
1429 : // non-kink stretch is found
1430 0 : NBEdge* in = n->getIncomingEdges().front();
1431 0 : NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1432 : NBEdge* const centerIn = in;
1433 : NBEdge* const centerOut = out;
1434 0 : NBNode* up = in->getFromNode();
1435 0 : NBNode* down = out->getToNode();
1436 0 : corridor.push_back(up);
1437 0 : corridor.push_back(n);
1438 0 : corridor.push_back(down);
1439 0 : corridorBox.add(up->getPosition());
1440 0 : corridorBox.add(down->getPosition());
1441 0 : corridorEdges.push_back(in);
1442 0 : corridorEdges.push_back(out);
1443 0 : length += in->getLoadedLength();
1444 0 : length += out->getLoadedLength();
1445 : Position cBeg, cEnd, delta;
1446 : while (kinkNodes.count(up) != 0) {
1447 : NBEdge* const out2 = in;
1448 0 : NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1449 0 : length += in2->getLoadedLength();
1450 0 : up = in2->getFromNode();
1451 0 : corridor.insert(corridor.begin(), up);
1452 0 : corridorEdges.insert(corridorEdges.begin(), in2);
1453 : kinkNodes.erase(up);
1454 0 : corridorBox.add(up->getPosition());
1455 : }
1456 0 : cBeg = up->getPosition();
1457 0 : cEnd = down->getPosition();
1458 : delta = cEnd - cBeg;
1459 0 : while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
1460 : NBEdge* const out2 = in;
1461 0 : NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1462 0 : length += in2->getLoadedLength();
1463 0 : up = in2->getFromNode();
1464 0 : corridor.insert(corridor.begin(), up);
1465 0 : corridorEdges.insert(corridorEdges.begin(), in2);
1466 : kinkNodes.erase(up);
1467 0 : corridorBox.add(up->getPosition());
1468 0 : cBeg = up->getPosition();
1469 0 : cEnd = down->getPosition();
1470 : delta = cEnd - cBeg;
1471 : }
1472 : in = centerIn;
1473 : out = centerOut;
1474 : while (kinkNodes.count(down) != 0) {
1475 : NBEdge* const in2 = out;
1476 0 : NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1477 0 : down = out2->getToNode();
1478 0 : length += out2->getLoadedLength();
1479 0 : corridor.push_back(down);
1480 0 : corridorEdges.push_back(out2);
1481 : kinkNodes.erase(down);
1482 0 : corridorBox.add(down->getPosition());
1483 : }
1484 0 : cBeg = up->getPosition();
1485 0 : cEnd = down->getPosition();
1486 : delta = cEnd - cBeg;
1487 0 : while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
1488 : NBEdge* const in2 = out;
1489 0 : NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1490 0 : down = out2->getToNode();
1491 0 : length += out2->getLoadedLength();
1492 0 : corridor.push_back(down);
1493 0 : corridorEdges.push_back(out2);
1494 : kinkNodes.erase(down);
1495 0 : corridorBox.add(down->getPosition());
1496 0 : cBeg = up->getPosition();
1497 0 : cEnd = down->getPosition();
1498 : delta = cEnd - cBeg;
1499 : }
1500 : // straighten all edges in corridor (corridorEdges doesn't include bidi)
1501 0 : std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
1502 0 : for (NBNode* n2 : corridorNodes) {
1503 0 : for (NBEdge* e : n2->getEdges()) {
1504 : if (corridorNodes.count(e->getFromNode()) != 0
1505 : && corridorNodes.count(e->getToNode()) != 0) {
1506 0 : PositionVector simpleGeom;
1507 0 : simpleGeom.push_back(e->getFromNode()->getPosition());
1508 0 : simpleGeom.push_back(e->getToNode()->getPosition());
1509 0 : e->setGeometry(simpleGeom);
1510 0 : }
1511 : }
1512 : }
1513 0 : if (delta.length2D() > 0) {
1514 : double currLength = 0;
1515 0 : for (int i = 1; i < (int)corridor.size() - 1; i++) {
1516 0 : currLength += corridorEdges[i - 1]->getLoadedLength();
1517 0 : const Position newPos = cBeg + delta * (currLength / length);
1518 0 : NBNode* const n2 = corridor[i];
1519 0 : n2->reinit(newPos, n2->getType());
1520 0 : for (NBEdge* e : n2->getEdges()) {
1521 0 : e->resetEndpointAtNode(n2);
1522 : }
1523 0 : moved += 1;
1524 : }
1525 0 : numCorridors += 1;
1526 : } else {
1527 0 : WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
1528 : }
1529 0 : }
1530 : //std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
1531 :
1532 0 : WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
1533 0 : return moved;
1534 : }
1535 :
1536 : /****************************************************************************/
|