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