Eclipse SUMO - Simulation of Urban MObility
NBAlgorithms_Railway.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2012-2022 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/****************************************************************************/
19// Algorithms for highway on-/off-ramps computation
20/****************************************************************************/
21#include <config.h>
22
23#include <cassert>
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 "NBVehicle.h"
39
40//#define DEBUG_SEQSTOREVERSE
41//#define DEBUG_DIRECTION_PRIORITY
42
43#define DEBUGNODEID "gneJ34"
44#define DEBUGNODEID2 "28842974"
45#define DEBUGEDGEID "22820560#0"
46#define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
47
48#define SHARP_THRESHOLD_SAMEDIR 100
49#define SHARP_THRESHOLD 80
50
51
52// ===========================================================================
53// method definitions
54// ===========================================================================
55// ---------------------------------------------------------------------------
56// Track methods
57// ---------------------------------------------------------------------------
58void
60 successors.push_back(track);
61 viaSuccessors.push_back(std::make_pair(track, nullptr));
63}
64
65
66const std::vector<NBRailwayTopologyAnalyzer::Track*>&
68 if ((minPermissions & svc) != 0) {
69 return successors;
70 } else {
71 if (svcSuccessors.count(svc) == 0) {
72 std::vector<Track*> succ;
73 for (Track* t : successors) {
74 if ((t->edge->getPermissions() & svc) != 0) {
75 succ.push_back(t);
76 }
77 }
78 svcSuccessors[svc] = succ;
79 }
80 return svcSuccessors[svc];
81 }
82}
83
84
85const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
87 if ((minPermissions & svc) != 0) {
88 return viaSuccessors;
89 } else {
90 if (svcViaSuccessors.count(svc) == 0) {
91 std::vector<std::pair<const Track*, const Track*> >& succ = svcViaSuccessors[svc];
92 for (const Track* const t : successors) {
93 if ((t->edge->getPermissions() & svc) != 0) {
94 succ.push_back(std::make_pair(t, nullptr));
95 }
96 }
97 }
98 return svcViaSuccessors[svc];
99 }
100}
101
102
103// ---------------------------------------------------------------------------
104// NBRailwayTopologyAnalyzer methods
105// ---------------------------------------------------------------------------
106void
108 getBrokenRailNodes(ec, true);
109}
110
111
112int
114 int addedBidi = 0;
115 addedBidi += extendBidiEdges(ec);
116 addedBidi += reverseEdges(ec, sc); // technically not bidi but new edges nevertheless
117 addedBidi += addBidiEdgesForBufferStops(ec);
118 addedBidi += addBidiEdgesBetweenSwitches(ec);
119 if (lc.getLines().size() > 0) {
120 addedBidi += addBidiEdgesForStops(ec, lc, sc);
121 }
122 if (OptionsCont::getOptions().getBool("railway.topology.repair.connect-straight")) {
123 addedBidi += addBidiEdgesForStraightConnectivity(ec, true);
124 addedBidi += addBidiEdgesForStraightConnectivity(ec, false);
125 addedBidi += extendBidiEdges(ec);
126 }
127 return addedBidi;
128}
129
130
131int
133 int numRailEdges = 0;
134 int numBidiEdges = 0;
135 int numNotCenterEdges = 0;
136 int numAddedBidiEdges = 0;
137 std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
138 std::vector<NBEdge*> edges;
139 if (inputfile == "") {
140 for (NBEdge* edge : ec.getAllEdges()) {
141 edges.push_back(edge);
142 }
143 } else {
144 std::set<std::string> edgeIDs;
145 NBHelpers::loadEdgesFromFile(inputfile, edgeIDs);
146 for (const std::string& edgeID : edgeIDs) {
147 NBEdge* edge = ec.retrieve(edgeID);
148 if (edge != nullptr) {
149 edges.push_back(edge);
150 }
151 }
152 }
153 for (NBEdge* edge : edges) {
154 if (hasRailway(edge->getPermissions())) {
155 numRailEdges++;
156 // rebuild connections if given from an earlier network
157 edge->invalidateConnections(true);
158 if (!edge->isBidiRail()) {
159 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
160 NBEdge* e2 = addBidiEdge(ec, edge, false);
161 if (e2 != nullptr) {
162 numAddedBidiEdges++;
163 }
164 } else {
165 numNotCenterEdges++;
166 }
167 } else {
168 numBidiEdges++;
169 }
170 }
171 }
172 WRITE_MESSAGE("Added " + toString(numAddedBidiEdges) + " bidi-edges to ensure that all tracks are usable in both directions.");
173 if (numNotCenterEdges) {
174 WRITE_WARNING("Ignore " + toString(numNotCenterEdges) + " edges because they have the wrong spreadType");
175 }
176 return numAddedBidiEdges;
177}
178
179
180NBEdge*
183 assert(!edge->isBidiRail());
184 const std::string id2 = (edge->getID()[0] == '-'
185 ? edge->getID().substr(1)
186 : "-" + edge->getID());
187 if (ec.wasIgnored(id2)) {
188 // we had it before so the warning is already there
189 return nullptr;
190 }
191 if (ec.retrieve(id2) == nullptr) {
192 NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
193 edge, edge->getGeometry().reverse());
194 ec.insert(e2);
195 if (ec.retrieve(id2) == nullptr) {
196 WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
197 return nullptr;
198 }
199 if (update) {
200 updateTurns(edge);
201 // reconnected added edges
202 for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
203 if (hasRailway(incoming->getPermissions())) {
204 incoming->invalidateConnections(true);
205 }
206 }
207 }
208 return e2;
209 } else {
210 WRITE_WARNINGF(TL("Could not add bidi-edge '%'."), id2);
211 return nullptr;
212 }
213}
214
215
216void
218 EdgeVector& inEdges, EdgeVector& outEdges) {
219 for (NBEdge* e : node->getIncomingEdges()) {
220 if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
221 inEdges.push_back(e);
222 }
223 }
224 for (NBEdge* e : node->getOutgoingEdges()) {
225 if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
226 outEdges.push_back(e);
227 }
228 }
229}
230
231
232std::set<NBNode*>
234 std::set<NBNode*> brokenNodes;
235 OutputDevice& device = OutputDevice::getDevice(verbose
236 ? OptionsCont::getOptions().getString("railway.topology.output")
237 : "/dev/null");
238
239 device.writeXMLHeader("railwayTopology", "");
240 std::set<NBNode*> railNodes = getRailNodes(ec, verbose);
241 std::map<std::pair<int, int>, std::set<NBNode*, ComparatorIdLess> > types;
242 std::set<NBEdge*, ComparatorIdLess> bidiEdges;
243 std::set<NBEdge*, ComparatorIdLess> bufferStops;
244 for (NBNode* node : railNodes) {
245 EdgeVector inEdges, outEdges;
246 getRailEdges(node, inEdges, outEdges);
247 types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
248 for (NBEdge* e : outEdges) {
249 if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
250 NBEdge* primary = e;
251 NBEdge* secondary = e->getTurnDestination(true);
252 if (e->getID()[0] == '-') {
253 std::swap(primary, secondary);
254 } else if (primary->getID()[0] != '-' && secondary->getID()[0] != '-' && secondary->getID() < primary->getID()) {
255 std::swap(primary, secondary);
256 }
257 if (bidiEdges.count(secondary) == 0) {
258 // avoid duplicate when both ids start with '-'
259 bidiEdges.insert(primary);
260 }
261 }
262 }
263 }
264
265 int numBrokenA = 0;
266 int numBrokenB = 0;
267 int numBrokenC = 0;
268 int numBrokenD = 0;
269 int numBufferStops = 0;
270 if (verbose && types.size() > 0) {
271 WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
272 }
273 device.openTag("legend");
274 device.openTag("error");
275 device.writeAttr(SUMO_ATTR_ID, "a");
276 device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
277 device.closeTag();
278 device.openTag("error");
279 device.writeAttr(SUMO_ATTR_ID, "b");
280 device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
281 device.closeTag();
282 device.openTag("error");
283 device.writeAttr(SUMO_ATTR_ID, "c");
284 device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
285 device.closeTag();
286 device.openTag("error");
287 device.writeAttr(SUMO_ATTR_ID, "d");
288 device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
289 device.closeTag();
290 device.closeTag();
291
292 for (auto it : types) {
293 int numBrokenType = 0;
294 device.openTag("railNodeType");
295 int in = it.first.first;
296 int out = it.first.second;
297 device.writeAttr("in", in);
298 device.writeAttr("out", out);
299 for (NBNode* n : it.second) {
300 device.openTag(SUMO_TAG_NODE);
301 device.writeAttr(SUMO_ATTR_ID, n->getID());
302 EdgeVector inRail, outRail;
303 getRailEdges(n, inRail, outRail);
304 // check if there is a mismatch between angle and edge direction
305 // (see above)
306
307 std::string broken = "";
308 if (in < 2 && hasStraightPair(n, outRail, outRail)) {
309 broken += "a";
310 numBrokenA++;
311 }
312 if (out < 2 && hasStraightPair(n, inRail, inRail)) {
313 broken += "b";
314 numBrokenB++;
315 }
316 if (out > 0) {
317 for (NBEdge* e : inRail) {
318 EdgeVector tmp;
319 tmp.push_back(e);
320 if (allSharp(n, tmp, outRail)) {
321 broken += "c";
322 numBrokenC++;
323 break;
324 }
325 }
326 }
327 if (in > 0) {
328 for (NBEdge* e : outRail) {
329 EdgeVector tmp;
330 tmp.push_back(e);
331 if (allSharp(n, inRail, tmp)) {
332 broken += "d";
333 numBrokenD++;
334 break;
335 }
336 }
337 }
338 // do not mark bidi nodes as broken
339 if (((in == 1 && out == 1) || (in == 2 && out == 2))
340 && allBidi(inRail) && allBidi(outRail)) {
341 broken = "";
342 }
343
344 if (broken.size() > 0) {
345 device.writeAttr("broken", broken);
346 brokenNodes.insert(n);
347 numBrokenType++;
348 }
349 if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
350 device.writeAttr("buffer_stop", "true");
351 numBufferStops++;
352 }
353 device.closeTag();
354 }
355 device.closeTag();
356 if (verbose) {
357 WRITE_MESSAGE(" " + toString(it.first.first) + "," + toString(it.first.second)
358 + " count: " + toString(it.second.size()) + " broken: " + toString(numBrokenType));
359 }
360
361 }
362 if (verbose) {
363 WRITE_MESSAGE("Found " + toString(brokenNodes.size()) + " broken railway nodes "
364 + "(A=" + toString(numBrokenA)
365 + " B=" + toString(numBrokenB)
366 + " C=" + toString(numBrokenC)
367 + " D=" + toString(numBrokenD)
368 + ")");
369 WRITE_MESSAGE("Found " + toString(numBufferStops) + " railway nodes marked as buffer_stop");
370 }
371
372 for (NBEdge* e : bidiEdges) {
373 device.openTag("bidiEdge");
374 device.writeAttr(SUMO_ATTR_ID, e->getID());
375 device.writeAttr("bidi", e->getTurnDestination(true)->getID());
376 device.closeTag();
377 }
378 if (verbose) {
379 WRITE_MESSAGE("Found " + toString(bidiEdges.size()) + " bidirectional rail edges");
380 }
381
382 device.close();
383 return brokenNodes;
384}
385
386
387std::set<NBNode*>
389 std::set<NBNode*> railNodes;
390 int numRailEdges = 0;
391 for (auto it = ec.begin(); it != ec.end(); it++) {
392 if (hasRailway(it->second->getPermissions())) {
393 numRailEdges++;
394 railNodes.insert(it->second->getFromNode());
395 railNodes.insert(it->second->getToNode());
396 }
397 }
398 int numRailSignals = 0;
399 for (const NBNode* const node : railNodes) {
400 if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
401 numRailSignals++;
402 }
403 }
404 if (verbose) {
405 WRITE_MESSAGE("Found " + toString(numRailEdges) + " railway edges and " + toString(railNodes.size()) + " railway nodes (" + toString(numRailSignals) + " signals).");
406 }
407 return railNodes;
408}
409
410
411bool
412NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
413 const double relAngle = NBHelpers::normRelAngle(e1->getAngleAtNode(node), e2->getAngleAtNode(node));
414 /*
415 std::cout << " isStraight n=" << node->getID()
416 << " e1=" << e1->getID()
417 << " e2=" << e2->getID()
418 << " a1=" << e1->getAngleAtNode(node)
419 << " a2=" << e2->getAngleAtNode(node)
420 << " rel=" << relAngle
421 << "\n";
422 */
423 if ((e1->getToNode() == node && e2->getFromNode() == node)
424 || (e1->getFromNode() == node && e2->getToNode() == node)) {
425 // edges go in the same direction
426 return fabs(relAngle) < SHARP_THRESHOLD;
427 } else {
428 // edges go in the opposite direction (both incoming or outgoing)
429 return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
430 }
431}
432
433
434bool
436 const EdgeVector& edges2) {
437#ifdef DEBUG_SEQSTOREVERSE
438 //if (node->getID() == DEBUGNODEID2) {
439 // std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
440 //}
441#endif
442 for (NBEdge* e1 : edges) {
443 for (NBEdge* e2 : edges2) {
444 //if (e1->getID() == "195411601#2" && e2->getID() == "93584120#3") {
445 // std::cout
446 // << " DEBUG normRelA=" << NBHelpers::normRelAngle(
447 // e1->getAngleAtNode(node),
448 // e2->getAngleAtNode(node))
449 // << "\n";
450 //}
451 if (e1 != e2 && isStraight(node, e1, e2)) {
452 return true;
453 }
454 }
455 }
456 return false;
457}
458
459
460bool
461NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
462 for (NBEdge* e : in) {
463 if (e != candOut && isStraight(node, e, candOut)) {
464 if (gDebugFlag1) {
465 std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
466 }
467 return false;
468 }
469 }
470 for (NBEdge* e : out) {
471 if (e != candOut && !isStraight(node, e, candOut)) {
472 if (gDebugFlag1) {
473 std::cout << " isSharp e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
474 }
475 return false;
476 }
477 }
478 return true;
479}
480
481
482bool
483NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
484 bool allBidi = true;
485 for (NBEdge* e1 : in) {
486 for (NBEdge* e2 : out) {
487 if (e1 != e2 && isStraight(node, e1, e2)) {
488 return false;
489 }
490 if (!e1->isBidiRail(true)) {
491 //std::cout << " allSharp node=" << node->getID() << " e1=" << e1->getID() << " is not bidi\n";
492 allBidi = false;
493 }
494 }
495 }
496 return !allBidi || countBidiAsSharp;
497}
498
499
500bool
502 for (NBEdge* e : edges) {
503 if (!e->isBidiRail()) {
504 return false;
505 }
506 }
507 return true;
508}
509
510
511int
513 int added = 0;
514 for (auto it = ec.begin(); it != ec.end(); it++) {
515 NBEdge* e = it->second;
516 if (e->isBidiRail()) {
517 added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
518 added += extendBidiEdges(ec, e->getToNode(), e);
519 }
520 }
521 if (added > 0) {
522 WRITE_MESSAGE("Added " + toString(added) + " bidi-edges as extension of existing bidi edges.");
523 }
524 return added;
525}
526
527
528int
530 assert(bidiIn->getToNode() == node);
531 NBEdge* bidiOut = bidiIn->getTurnDestination(true);
532 if (bidiOut == nullptr) {
533 WRITE_WARNING("Could not find bidi-edge for edge '" + bidiIn->getID() + "'");
534 return 0;
535 }
536 EdgeVector tmpBidiOut;
537 tmpBidiOut.push_back(bidiOut);
538 EdgeVector tmpBidiIn;
539 tmpBidiIn.push_back(bidiIn);
540 int added = 0;
541 EdgeVector inRail, outRail;
542 getRailEdges(node, inRail, outRail);
543 for (NBEdge* cand : outRail) {
544 //std::cout << " extendBidiEdges n=" << node->getID() << " bidiIn=" << bidiIn->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, bidiIn, cand) << " allSharp=" << allSharp(node, inRail, tmpBidiOut, true) << "\n";
545 if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
546 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
547 && allSharp(node, inRail, tmpBidiOut, true)) {
548 NBEdge* e2 = addBidiEdge(ec, cand);
549 if (e2 != nullptr) {
550 added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
551 }
552 }
553 }
554 for (NBEdge* cand : inRail) {
555 //std::cout << " extendBidiEdges n=" << node->getID() << " bidiOut=" << bidiOut->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, cand, bidiOut) << " allSharp=" << allSharp(node, outRail, tmpBidiIn, true) << "\n";
556 if (!cand->isBidiRail() && isStraight(node, cand, bidiOut)
557 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
558 && allSharp(node, outRail, tmpBidiIn, true)) {
559 NBEdge* e2 = addBidiEdge(ec, cand);
560 if (e2 != nullptr) {
561 added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
562 }
563 }
564 }
565 return added;
566}
567
568
569int
571 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
572 // find reversible edge sequences between broken nodes
573 std::vector<EdgeVector> seqsToReverse;
574 for (NBNode* n : brokenNodes) {
575 EdgeVector inRail, outRail;
576 getRailEdges(n, inRail, outRail);
577 for (NBEdge* start : outRail) {
578 EdgeVector tmp;
579 tmp.push_back(start);
580 // only reverse edges where the node would be unbroken afterwards
581 if (!allBroken(n, start, inRail, outRail)
582 || (inRail.size() == 1 && outRail.size() == 1)) {
583#ifdef DEBUG_SEQSTOREVERSE
584 if (n->getID() == DEBUGNODEID) {
585 std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
586 }
587#endif
588 continue;
589 }
590 //std::cout << " get sequences from " << start->getID() << "\n";
591 bool forward = true;
592 EdgeVector seq;
593 while (forward) {
594 seq.push_back(start);
595 //std::cout << " seq=" << toString(seq) << "\n";
596 NBNode* n2 = start->getToNode();
597 EdgeVector inRail2, outRail2;
598 getRailEdges(n2, inRail2, outRail2);
599 if (brokenNodes.count(n2) != 0) {
600 EdgeVector tmp2;
601 tmp2.push_back(start);
602 if (allBroken(n2, start, outRail2, inRail2)) {
603 seqsToReverse.push_back(seq);
604 } else {
605#ifdef DEBUG_SEQSTOREVERSE
606 if (n->getID() == DEBUGNODEID) {
607 std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
608 }
609#endif
610 }
611 forward = false;
612 } else {
613 if (outRail2.size() == 0) {
614 // stop at network border
615 forward = false;
616#ifdef DEBUG_SEQSTOREVERSE
617 if (n->getID() == DEBUGNODEID) {
618 std::cout << " abort at n2=" << n2->getID() << " (border)\n";
619 }
620#endif
621 } else if (outRail2.size() > 1 || inRail2.size() > 1) {
622 // stop at switch
623 forward = false;
624#ifdef DEBUG_SEQSTOREVERSE
625 if (n->getID() == DEBUGNODEID) {
626 std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
627 }
628#endif
629 } else {
630 start = outRail2.front();
631 }
632 }
633 }
634 }
635 }
636 // sort by sequence length
637 if (seqsToReverse.size() > 0) {
638 WRITE_MESSAGE("Found " + toString(seqsToReverse.size()) + " reversible edge sequences between broken rail nodes");
639 }
640 std::sort(seqsToReverse.begin(), seqsToReverse.end(),
641 [](const EdgeVector & a, const EdgeVector & b) {
642 return a.size() < b.size();
643 });
644 int numReversed = 0;
645 std::set<NBNode*> affectedEndpoints;
646 std::set<std::string> reversedIDs;
647 std::map<int, int> seqLengths;
648 for (EdgeVector& seq : seqsToReverse) {
649 NBNode* seqStart = seq.front()->getFromNode();
650 NBNode* seqEnd = seq.back()->getToNode();
651 // avoid reversing sequenes on both sides of a broken node
652 if (affectedEndpoints.count(seqStart) == 0
653 && affectedEndpoints.count(seqEnd) == 0) {
654 affectedEndpoints.insert(seqStart);
655 affectedEndpoints.insert(seqEnd);
656 //WRITE_MESSAGE(" reversed seq=" + toString(seq));
657 for (NBEdge* e : seq) {
658 e->reinitNodes(e->getToNode(), e->getFromNode());
659 e->setGeometry(e->getGeometry().reverse());
660 reversedIDs.insert(e->getID());
661 }
662 seqLengths[(int)seq.size()]++;
663 numReversed++;
664 }
665 }
666 if (numReversed > 0) {
667 WRITE_MESSAGE("Reversed " + toString(numReversed) + " sequences (count by length: " + joinToString(seqLengths, " ", ":") + ")");
668 for (auto& item : sc.getStops()) {
669 if (reversedIDs.count(item.second->getEdgeId())) {
670 item.second->findLaneAndComputeBusStopExtent(ec);
671 }
672 }
673 }
674 return numReversed;
675}
676
677
678int
680 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
681 std::set<NBNode*> railNodes = getRailNodes(ec);
682 // find buffer stops and ensure that thay are connect to the network in both directions
683 int numBufferStops = 0;
684 int numAddedBidiTotal = 0;
685 for (NBNode* node : railNodes) {
686 if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
687 if (node->getEdges().size() != 1) {
688 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
689 continue;
690 }
691 int numAddedBidi = 0;
692 numBufferStops++;
693 NBEdge* prev = nullptr;
694 NBEdge* prev2 = nullptr;
695 EdgeVector inRail, outRail;
696 getRailEdges(node, inRail, outRail);
697 bool addAway = true; // add new edges away from buffer stop
698 while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
699 NBEdge* e = nullptr;
700 if (prev == nullptr) {
701 assert(node->getEdges().size() == 1);
702 e = node->getEdges().front();
703 addAway = node == e->getToNode();
704 } else {
705 if (addAway) {
706 // XXX if node is broken we need to switch direction
707 assert(inRail.size() == 2);
708 e = inRail.front() == prev2 ? inRail.back() : inRail.front();
709 } else {
710 // XXX if node is broken we need to switch direction
711 assert(outRail.size() == 2);
712 e = outRail.front() == prev2 ? outRail.back() : outRail.front();
713 }
714 }
716 NBNode* e2From = nullptr;
717 NBNode* e2To = nullptr;
718 if (addAway) {
719 e2From = node;
720 e2To = e->getFromNode();
721 node = e2To;
722 } else {
723 e2From = e->getToNode();
724 e2To = node;
725 node = e2From;
726 }
727 NBEdge* e2 = addBidiEdge(ec, e);
728 if (e2 == nullptr) {
729 break;
730 }
731 prev = e;
732 prev2 = e2;
733 numAddedBidi++;
734 numAddedBidiTotal++;
735 inRail.clear();
736 outRail.clear();
737 getRailEdges(node, inRail, outRail);
738 }
739 //if (numAddedBidi > 0) {
740 // WRITE_MESSAGE(" added " + toString(numAddedBidi) + " edges between buffer stop junction '" + bufferStop->getID() + "' and junction '" + node->getID() + "'");
741 //}
742 }
743 }
744 if (numAddedBidiTotal > 0) {
745 WRITE_MESSAGE("Added " + toString(numAddedBidiTotal) + " edges to connect " + toString(numBufferStops) + " buffer stops in both directions.");
746 }
747 return numAddedBidiTotal;
748}
749
750NBEdge*
752 EdgeVector inRail, outRail;
753 getRailEdges(n, inRail, outRail);
754 if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
755 if (isStraight(n, inRail.front(), outRail.front())) {
756 return inRail.front();
757 } else if (isStraight(n, inRail.back(), outRail.front())) {
758 return inRail.back();
759 }
760 }
761 if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
762 if (isStraight(n, outRail.front(), inRail.front())) {
763 return outRail.front();
764 } else if (isStraight(n, outRail.back(), inRail.front())) {
765 return outRail.back();
766 }
767 }
768 return nullptr;
769}
770
771
772int
774 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
775 std::map<int, int> seqLengths;
776 int numAdded = 0;
777 int numSeqs = 0;
778 for (NBNode* n : brokenNodes) {
779 NBEdge* edge = isBidiSwitch(n);
780 if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
781 std::vector<NBNode*> nodeSeq;
782 EdgeVector edgeSeq;
783 NBNode* prev = n;
784 nodeSeq.push_back(prev);
785 edgeSeq.push_back(edge);
786 bool forward = true;
787 //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
788 // find a suitable end point for adding bidi edges
789 while (forward) {
790 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
791 EdgeVector allRail;
792 getRailEdges(next, allRail, allRail);
793 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
794 prev = next;
795 edge = allRail.front() == edge ? allRail.back() : allRail.front();
796 nodeSeq.push_back(prev);
797 edgeSeq.push_back(edge);
798 } else {
799 forward = false;
800 EdgeVector inRail2, outRail2;
801 getRailEdges(next, inRail2, outRail2);
802 if (isBidiSwitch(next) == edge) {
803 // suitable switch found as endpoint, add reverse edges
804 //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
805 // + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
806 for (NBEdge* e : edgeSeq) {
807 addBidiEdge(ec, e);
808 }
809 seqLengths[(int)edgeSeq.size()]++;
810 numSeqs++;
811 numAdded += (int)edgeSeq.size();
812 } else {
813 //std::cout << " sequence ended at junction " << next->getID()
814 // << " in=" << inRail2.size()
815 // << " out=" << outRail2.size()
816 // << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
817 // << "\n";
818 }
819
820 }
821 }
822
823 }
824 }
825 if (seqLengths.size() > 0) {
826 WRITE_MESSAGE("Added " + toString(numAdded) + " bidi-edges between " + toString(numSeqs) + " pairs of railway switches (count by length: " + joinToString(seqLengths, " ", ":") + ")");
827 }
828 return numAdded;
829}
830
831
832std::set<NBPTLine*>
834 std::set<NBPTLine*> result;
835 std::set<std::pair<NBPTStop*, NBPTStop*> > visited;
836 for (const auto& item : lc.getLines()) {
837 const std::vector<NBPTStop*>& stops = item.second->getStops();
838 if (stops.size() > 1) {
839 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
840 NBPTStop* fromStop = *it;
841 NBPTStop* toStop = *(it + 1);
842 visited.insert({fromStop, toStop});
843 }
844 }
845 }
846 for (const auto& item : lc.getLines()) {
847 const std::vector<NBPTStop*>& stops = item.second->getStops();
848 if (stops.size() > 1) {
849 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
850 NBPTStop* fromStop = *it;
851 NBPTStop* toStop = *(it + 1);
852 std::pair<NBPTStop*, NBPTStop*> reverseTrip({toStop, fromStop});
853 if (visited.count(reverseTrip)) {
854 result.insert(item.second);
855 break;
856 }
857 }
858 }
859 }
860 return result;
861}
862
863int
865 const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
866 // generate bidirectional routing graph
867 std::vector<Track*> tracks;
868 for (NBEdge* edge : ec.getAllEdges()) {
869 tracks.push_back(new Track(edge));
870 }
871 const int numEdges = (int)tracks.size();
872 for (NBEdge* edge : ec.getAllEdges()) {
873 tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse"));
874 }
875 // add special tracks for starting end ending in both directions
876 std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
877 for (NBEdge* edge : ec.getAllEdges()) {
878 if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
879 Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
880 tracks.push_back(start);
881 Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
882 tracks.push_back(end);
883 stopTracks[edge] = {start, end};
884 }
885 }
886 // set successors based on angle (connections are not yet built)
887 for (NBNode* node : getRailNodes(ec)) {
888 EdgeVector railEdges;
889 getRailEdges(node, railEdges, railEdges);
890 for (NBEdge* e1 : railEdges) {
891 for (NBEdge* e2 : railEdges) {
892 if (e1 != e2 && isStraight(node, e1, e2)) {
893 int i = e1->getNumericalID();
894 int i2 = e2->getNumericalID();
895 if (e1->getToNode() == node) {
896 if (e2->getFromNode() == node) {
897 // case 1) plain forward connection
898 tracks[i]->addSuccessor(tracks[i2]);
899 // reverse edge (numerical id incremented by numEdges)
900 tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
901 } else {
902 // case 2) both edges pointing towards each other
903 tracks[i]->addSuccessor(tracks[i2 + numEdges]);
904 tracks[i2]->addSuccessor(tracks[i + numEdges]);
905 }
906 } else {
907 if (e2->getFromNode() == node) {
908 // case 3) both edges pointing away from each other
909 tracks[i + numEdges]->addSuccessor(tracks[i2]);
910 tracks[i2 + numEdges]->addSuccessor(tracks[i]);
911 } else {
912 // already handled via case 1)
913 }
914 }
915
916 }
917 }
918 }
919 }
920 // define start and end successors
921 for (auto& item : stopTracks) {
922 const int index = item.first->getNumericalID();
923 // start
924 item.second.first->addSuccessor(tracks[index]);
925 item.second.first->addSuccessor(tracks[index + numEdges]);
926 // end
927 tracks[index]->addSuccessor(item.second.second);
928 tracks[index + numEdges]->addSuccessor(item.second.second);
929 }
930 // DEBUG
931 /*
932 for (Track* t : tracks) {
933 std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
934 for (Track* s : t->getSuccessors(SVC_IGNORING)) {
935 std::cout << " succ=" << s->getID() << "\n";
936 }
937 }
938 */
939
941 tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
942
943 int added = 0;
944 int numDisconnected = 0;
945 std::set<NBEdge*, ComparatorIdLess> addBidiStops;
946 std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
947 std::set<std::pair<std::string, std::string> > visited;
948
949 // the isConsistent heuristic may fail in some cases. If we observe that a
950 // specific sequence of stop ids in encoded in both directions, we take this
951 // as a reason to overrule the heuristic
952 std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
953
954 for (const auto& item : lc.getLines()) {
955 NBPTLine* line = item.second;
956 std::vector<std::pair<NBEdge*, std::string> > stops = line->getStopEdges(ec);
957 NBEdge* routeStart = line->getRouteStart(ec);
958 NBEdge* routeEnd = line->getRouteEnd(ec);
959 if (routeStart != nullptr) {
960 stops.insert(stops.begin(), {routeStart, routeStart->getID()});
961 }
962 if (routeEnd != nullptr) {
963 stops.push_back({routeEnd, routeEnd->getID()});
964 }
965 if (stops.size() < 2) {
966 continue;
967 }
968 std::vector<NBEdge*> stopEdges;
969 for (auto it : stops) {
970 stopEdges.push_back(it.first);
971 }
972 if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
973 WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
974 continue;
975 }
976 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
977 NBEdge* fromEdge = it->first;
978 NBEdge* toEdge = (it + 1)->first;
979 const std::string fromStop = it->second;
980 const std::string toStop = (it + 1)->second;
981 std::pair<std::string, std::string> trip(fromStop, toStop);
982 std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
983 //std::cout << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << "\n";
984 if (visited.count(trip) != 0) {
985 continue;
986 } else {
987 visited.insert(trip);
988 }
989 if (stopTracks.count(fromEdge) == 0
990 || stopTracks.count(toEdge) == 0) {
991 continue;
992 }
993 const bool needBidi = visited.count(reverseTrip) != 0;
994 NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
995 std::vector<const Track*> route;
996 router->compute(stopTracks[fromEdge].first, stopTracks[toEdge].second, &veh, 0, route);
997 //if (fromEdge->getID() == "356053025#1" && toEdge->getID() == "23256161") {
998 // std::cout << "DEBUG: route=" << toString(route) << "\n";
999 //}
1000 if (route.size() > 0) {
1001 assert(route.size() > 2);
1002 for (int i = 1; i < (int)route.size() - 1; ++i) {
1003 if (route[i]->getNumericalID() >= numEdges || needBidi) {
1004 NBEdge* edge = route[i]->edge;
1005 if (addBidiEdges.count(edge) == 0) {
1006 bool isStop = i == 1 || i == (int)route.size() - 2;
1007 if (!edge->isBidiRail(true)) {
1009 addBidiEdges.insert(edge);
1010 if (isStop) {
1011 addBidiStops.insert(edge);
1012 }
1013 } else {
1014 if (isStop) {
1015 WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
1016 }
1017 }
1018 } else if (isStop && needBidi) {
1019 NBPTStop* fs = sc.get(fromStop);
1020 NBPTStop* fromReverse = sc.getReverseStop(fs, ec);
1021 if (fromReverse) {
1022 sc.insert(fromReverse);
1023 fs->setBidiStop(fromReverse);
1024 }
1025 NBPTStop* ts = sc.get(toStop);
1026 NBPTStop* toReverse = sc.getReverseStop(ts, ec);
1027 if (toReverse) {
1028 sc.insert(toReverse);
1029 ts->setBidiStop(toReverse);
1030 }
1031 }
1032 }
1033 }
1034 }
1035 } else {
1036 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
1037 numDisconnected++;
1038 }
1039 }
1040 }
1041 for (NBEdge* edge : addBidiEdges) {
1042 if (!edge->isBidiRail()) {
1043 NBEdge* e2 = addBidiEdge(ec, edge);
1044 //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
1045 if (e2 != nullptr) {
1046 added++;
1047 if (!minimal) {
1048 added += extendBidiEdges(ec, edge->getToNode(), edge);
1049 added += extendBidiEdges(ec, edge->getFromNode(), e2);
1050 }
1051 }
1052 }
1053 }
1054
1055 if (addBidiEdges.size() > 0 || numDisconnected > 0) {
1056 WRITE_MESSAGE("Added " + toString(addBidiStops.size()) + " bidi-edges for public transport stops and a total of "
1057 + toString(added) + " bidi-edges to ensure connectivity of stops ("
1058 + toString(numDisconnected) + " stops remain disconnected)");
1059 }
1060
1061 // clean up
1062 for (Track* t : tracks) {
1063 delete t;
1064 }
1065 delete router;
1066 return (int)addBidiEdges.size();
1067}
1068
1069
1070int
1072 int added = 0;
1073 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
1074 for (const auto& e : ec) {
1075 if (!hasRailway(e.second->getPermissions())) {
1076 continue;
1077 }
1078 NBNode* const from = e.second->getFromNode();
1079 NBNode* const to = e.second->getToNode();
1080 if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
1081 continue;
1082 }
1083 if (e.second->isBidiRail()) {
1084 continue;
1085 }
1086 EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
1087 getRailEdges(from, inRailFrom, outRailFrom);
1088 getRailEdges(to, inRailTo, outRailTo);
1089 // check whether there is a straight edge pointing away from this one at the from-node
1090 // and there is no straight incoming edge at the from-node
1091 bool haveStraight = false;
1092 bool haveStraightReverse = false;
1093 if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
1094 for (const NBEdge* fromStraightCand : outRailFrom) {
1095 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1096 haveStraightReverse = true;
1097 //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
1098 break;
1099 }
1100 }
1101 if (haveStraightReverse) {
1102 for (const NBEdge* fromStraightCand : inRailFrom) {
1103 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1104 haveStraight = true;
1105 //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
1106 break;
1107 }
1108 }
1109 }
1110 }
1111 if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
1112 // check whether there is a straight edge pointing towards this one at the to-node
1113 // and there is no straight outoing edge at the to-node
1114 haveStraight = false;
1115 haveStraightReverse = false;
1116 for (const NBEdge* toStraightCand : inRailTo) {
1117 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1118 haveStraightReverse = true;
1119 //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
1120 break;
1121 }
1122 }
1123 if (haveStraightReverse) {
1124 for (const NBEdge* toStraightCand : outRailTo) {
1125 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1126 haveStraight = true;
1127 //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
1128 break;
1129 }
1130 }
1131 }
1132 }
1133 //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
1134 if (haveStraightReverse && !haveStraight) {
1135 NBEdge* e2 = addBidiEdge(ec, e.second);
1136 //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
1137 if (e2 != nullptr) {
1138 added++;
1139 added += extendBidiEdges(ec, to, e.second);
1140 added += extendBidiEdges(ec, from, e2);
1141 }
1142 }
1143 }
1144 if (added > 0) {
1145 if (geometryLike) {
1146 WRITE_MESSAGE("Added " + toString(added) + " bidi-edges to ensure connectivity of straight tracks at geometry-like nodes.");
1147 } else {
1148 WRITE_MESSAGE("Added " + toString(added) + " bidi-edges to ensure connectivity of straight tracks at switches.");
1149 }
1150 }
1151 return added;
1152}
1153
1154
1155void
1159}
1160
1161
1162double
1163NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1164 return NBEdge::getTravelTimeStatic(track->edge, veh, time);
1165}
1166
1167
1168void
1170 // if fromUniDir=true, assign priority value for each railway edge:
1171 // 4: edge is unidirectional
1172 // 3: edge is in main direction of bidirectional track
1173 // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
1174 // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
1175 // 0: edge is part of bidirectional track in reverse of main direction
1176 //
1177 // otherwise:
1178 // assign priority value for each railway edge with priority -1 (undefined):
1179 // x: edges with priority >= 0 keep their priority
1180 // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
1181 // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
1182 // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
1183 // x-4 : edge is reverse direction of an x-1 edge
1184
1185 std::set<NBEdge*, ComparatorIdLess> bidi;
1186 EdgeSet uni;
1187 for (NBEdge* edge : ec.getAllEdges()) {
1188 if (hasRailway(edge->getPermissions())) {
1189 if (fromUniDir) {
1190 if (!edge->isBidiRail()) {
1191 edge->setPriority(4);
1192 uni.insert(edge);
1193 } else {
1194 bidi.insert(edge);
1195 }
1196 } else {
1197 if (edge->getPriority() >= 0) {
1198 uni.insert(edge);
1199 } else {
1200 bidi.insert(edge);
1201 }
1202 }
1203 }
1204 }
1205
1206 if (uni.size() == 0) {
1207 if (bidi.size() != 0) {
1208 WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
1209 }
1210 return;
1211 }
1212 EdgeSet seen;
1213 EdgeSet check = uni;
1214 EdgeSet forward;
1215 while (!check.empty()) {
1216 NBEdge* edge = *check.begin();
1217 check.erase(edge);
1218 if (seen.count(edge) != 0) {
1219 continue;
1220 }
1221 seen.insert(edge);
1222 NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
1223 if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
1224 forward.insert(straightOut);
1225 check.insert(straightOut);
1226 }
1227 NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
1228 if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
1229 forward.insert(straightIn);
1230 check.insert(straightIn);
1231 }
1232#ifdef DEBUG_DIRECTION_PRIORITY
1233 std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
1234 << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
1235 << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
1236 << "\n";
1237#endif
1238 }
1239
1240 for (NBEdge* edge : bidi) {
1241 NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
1242 int prio;
1243 int bidiPrio;
1244 if (forward.count(edge) != 0) {
1245 if (forward.count(bidiEdge) == 0) {
1246 prio = 3;
1247 bidiPrio = 0;
1248 } else {
1249 // both forward
1250 prio = 2;
1251 bidiPrio = 2;
1252 }
1253 } else {
1254 if (forward.count(bidiEdge) != 0) {
1255 prio = 0;
1256 bidiPrio = 3;
1257 } else {
1258 // neither forward
1259 prio = 1;
1260 bidiPrio = 1;
1261 }
1262 }
1263 if (bidiEdge == nullptr) {
1264 WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
1265 }
1266 if (edge->getPriority() >= 0) {
1267 bidiPrio = 0;
1268 }
1269 if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
1270 prio = 0;
1271 }
1272 if (edge->getPriority() < 0) {
1273 edge->setPriority(prio);
1274 }
1275 if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
1276 bidiEdge->setPriority(bidiPrio);
1277 }
1278 }
1279 std::map<int, int> numPrios;
1280 for (NBEdge* edge : bidi) {
1281 numPrios[edge->getPriority()]++;
1282 }
1283 if (fromUniDir) {
1284 WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1285 } else {
1286 WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1287 }
1288}
1289
1290
1291/****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:266
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:267
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:265
#define TL(string)
Definition: MsgHandler.h:282
#define SHARP_THRESHOLD_SAMEDIR
#define DEBUGNODEID
#define SHARP_THRESHOLD
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:42
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SUMO_TAG_NODE
alternative definition for junction
@ SUMO_ATTR_ID
bool gDebugFlag1
global utility flags for debugging
Definition: StdDefs.cpp:33
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:282
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
Computes the shortest path through a network using the Dijkstra algorithm.
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:59
std::map< std::string, NBEdge * >::const_iterator begin() const
Returns the pointer to the begin of the stored edges.
Definition: NBEdgeCont.h:170
EdgeVector getAllEdges() const
return all edges
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:274
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:177
bool wasIgnored(std::string id) const
Returns whether the edge with the id was ignored during parsing.
Definition: NBEdgeCont.h:495
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
Definition: NBEdgeCont.cpp:177
The representation of a single edge during network building.
Definition: NBEdge.h:92
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4137
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:552
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:787
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:974
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition: NBEdge.cpp:762
NBEdge * getStraightContinuation(SVCPermissions permissions) const
return the straightest follower edge for the given permissions or nullptr (never returns turn-arounds...
Definition: NBEdge.cpp:4612
static double getTravelTimeStatic(const NBEdge *const edge, const NBVehicle *const, double)
Definition: NBEdge.h:1493
NBEdge * getStraightPredecessor(SVCPermissions permissions) const
return the straightest predecessor edge for the given permissions or nullptr (never returns turn-arou...
Definition: NBEdge.cpp:4627
const std::string & getID() const
Definition: NBEdge.h:1526
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition: NBEdge.cpp:968
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition: NBEdge.cpp:1483
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:545
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition: NBEdge.cpp:3767
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2083
void setPriority(int priority)
Sets the priority of the edge.
Definition: NBEdge.h:538
int getPriority() const
Returns the priority of the edge.
Definition: NBEdge.h:533
static double normRelAngle(double angle1, double angle2)
ensure that reverse relAngles (>=179.999) always count as turnarounds (-180)
Definition: NBHelpers.cpp:58
static void loadEdgesFromFile(const std::string &file, std::set< std::string > &into)
Add edge ids defined in file (either ID or edge:ID per line) into the given set.
Definition: NBHelpers.cpp:86
Represents a single node (junction) during network building.
Definition: NBNode.h:66
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:258
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:263
const std::map< std::string, NBPTLine * > & getLines() const
Definition: NBPTLineCont.h:42
bool isConsistent(const std::vector< NBEdge * > &stops) const
return whether the mentioned edges appear in that order in the route
Definition: NBPTLine.cpp:237
std::vector< std::pair< NBEdge *, std::string > > getStopEdges(const NBEdgeCont &ec) const
get stop edges and stop ids
Definition: NBPTLine.cpp:164
const std::string & getRef() const
get line reference (not unique)
Definition: NBPTLine.h:66
NBEdge * getRouteEnd(const NBEdgeCont &ec) const
return last valid edge of myRoute (if it doest not lie before the last stop)
Definition: NBPTLine.cpp:207
NBEdge * getRouteStart(const NBEdgeCont &ec) const
return first valid edge of myRoute (if it doest not lie after the first stop)
Definition: NBPTLine.cpp:177
bool insert(NBPTStop *ptStop, bool floating=false)
Inserts a node into the map.
NBPTStop * get(std::string id) const
Retrieve a previously inserted pt stop.
NBPTStop * getReverseStop(NBPTStop *pStop, const NBEdgeCont &ec)
const std::map< std::string, NBPTStop * > & getStops() const
Definition: NBPTStopCont.h:66
The representation of a single pt stop.
Definition: NBPTStop.h:46
void setBidiStop(NBPTStop *bidiStop)
Definition: NBPTStop.h:124
const std::vector< std::pair< const Track *, const Track * > > & getViaSuccessors(SUMOVehicleClass svc=SVC_IGNORING) const
const std::vector< Track * > & getSuccessors(SUMOVehicleClass svc=SVC_IGNORING) const
std::vector< std::pair< const Track *, const Track * > > viaSuccessors
static NBEdge * isBidiSwitch(const NBNode *n)
static int addBidiEdgesForStops(NBEdgeCont &ec, NBPTLineCont &lc, NBPTStopCont &sc)
add bidi-edges to connect successive public transport stops
static int repairTopology(NBEdgeCont &ec, NBPTStopCont &sc, NBPTLineCont &lc)
static void getRailEdges(const NBNode *node, EdgeVector &inEdges, EdgeVector &outEdges)
filter out rail edges among all edges of a the given node
static void extendDirectionPriority(NBEdgeCont &ec, bool fromUniDir)
static std::set< NBNode * > getRailNodes(NBEdgeCont &ec, bool verbose=false)
static void updateTurns(NBEdge *edge)
recompute turning directions for both nodes of the given edge
static bool isStraight(const NBNode *node, const NBEdge *e1, const NBEdge *e2)
static void analyzeTopology(NBEdgeCont &ec)
static std::set< NBPTLine * > findBidiCandidates(NBPTLineCont &lc)
identify lines that are likely to require bidirectional tracks
static int addBidiEdgesForStraightConnectivity(NBEdgeCont &ec, bool geometryLike)
add bidi-edges to connect straight tracks
static bool allSharp(const NBNode *node, const EdgeVector &in, const EdgeVector &out, bool countBidiAsSharp=false)
static bool allBroken(const NBNode *node, NBEdge *candOut, const EdgeVector &in, const EdgeVector &out)
static std::set< NBNode * > getBrokenRailNodes(NBEdgeCont &ec, bool verbose=false)
static int addBidiEdgesBetweenSwitches(NBEdgeCont &ec)
add bidi-edges to connect switches that are approached in both directions
static bool allBidi(const EdgeVector &edges)
static int makeAllBidi(NBEdgeCont &ec)
static double getTravelTimeStatic(const Track *const track, const NBVehicle *const veh, double time)
static bool hasRailway(SVCPermissions permissions)
filter for rail edges but do not return (legacy) all purpose edges
static int reverseEdges(NBEdgeCont &ec, NBPTStopCont &sc)
reverse edges sequences that are to broken nodes on both sides
static bool hasStraightPair(const NBNode *node, const EdgeVector &edges, const EdgeVector &edges2)
static int addBidiEdgesForBufferStops(NBEdgeCont &ec)
add bidi-edges to connect buffers stops in both directions
static NBEdge * addBidiEdge(NBEdgeCont &ec, NBEdge *edge, bool update=true)
add bidi-edge for the given edge
static int extendBidiEdges(NBEdgeCont &ec)
add further bidi-edges near existing bidi-edges
static void computeTurnDirectionsForNode(NBNode *node, bool warn)
Computes turnaround destinations for all incoming edges of the given nodes (if any)
A vehicle as used by router.
Definition: NBVehicle.h:42
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition: Named.h:67
const std::string & getID() const
Returns the id.
Definition: Named.h:74
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:59
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:251
void close()
Closes the device and removes it from the dictionary.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
PositionVector reverse() const
reverse position vector
virtual bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent=false)=0
Builds the route between the given edges using the minimum effort at the given time The definition of...
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884