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