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