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