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 e->reinitNodes(e->getToNode(), e->getFromNode());
670 e->setGeometry(e->getGeometry().reverse());
671 reversedIDs.insert(e->getID());
672 if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
674 }
675 }
676 seqLengths[(int)seq.size()]++;
677 numReversed++;
678 }
679 }
680 if (numReversed > 0) {
681 WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
682 for (auto& item : sc.getStops()) {
683 if (reversedIDs.count(item.second->getEdgeId())) {
684 item.second->findLaneAndComputeBusStopExtent(ec);
685 }
686 }
687 }
688 return numReversed;
689}
690
691
692int
694 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
695 std::set<NBNode*> railNodes = getRailNodes(ec);
696 // find buffer stops and ensure that they are connect to the network in both directions
697 int numBufferStops = 0;
698 int numAddedBidiTotal = 0;
699 for (NBNode* node : railNodes) {
700 if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
701 if (node->getEdges().size() != 1) {
702 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
703 continue;
704 }
705 // int numAddedBidi = 0;
706 numBufferStops++;
707 NBEdge* prev = nullptr;
708 NBEdge* prev2 = nullptr;
709 EdgeVector inRail, outRail;
710 getRailEdges(node, inRail, outRail);
711 bool addAway = true; // add new edges away from buffer stop
712 while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
713 NBEdge* e = nullptr;
714 if (prev == nullptr) {
715 assert(node->getEdges().size() == 1);
716 e = node->getEdges().front();
717 addAway = node == e->getToNode();
718 } else {
719 if (addAway) {
720 // XXX if node is broken we need to switch direction
721 assert(inRail.size() == 2);
722 e = inRail.front() == prev2 ? inRail.back() : inRail.front();
723 } else {
724 // XXX if node is broken we need to switch direction
725 assert(outRail.size() == 2);
726 e = outRail.front() == prev2 ? outRail.back() : outRail.front();
727 }
728 }
730 NBNode* e2From = nullptr;
731 NBNode* e2To = nullptr;
732 if (addAway) {
733 e2From = node;
734 e2To = e->getFromNode();
735 node = e2To;
736 } else {
737 e2From = e->getToNode();
738 e2To = node;
739 node = e2From;
740 }
741 NBEdge* e2 = addBidiEdge(ec, e);
742 if (e2 == nullptr) {
743 break;
744 }
745 prev = e;
746 prev2 = e2;
747 // numAddedBidi++;
748 numAddedBidiTotal++;
749 inRail.clear();
750 outRail.clear();
751 getRailEdges(node, inRail, outRail);
752 }
753 //if (numAddedBidi > 0) {
754 // WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
755 //}
756 }
757 }
758 if (numAddedBidiTotal > 0) {
759 WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
760 }
761 return numAddedBidiTotal;
762}
763
764NBEdge*
766 EdgeVector inRail, outRail;
767 getRailEdges(n, inRail, outRail);
768 if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
769 if (isStraight(n, inRail.front(), outRail.front())) {
770 return inRail.front();
771 } else if (isStraight(n, inRail.back(), outRail.front())) {
772 return inRail.back();
773 }
774 }
775 if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
776 if (isStraight(n, outRail.front(), inRail.front())) {
777 return outRail.front();
778 } else if (isStraight(n, outRail.back(), inRail.front())) {
779 return outRail.back();
780 }
781 }
782 return nullptr;
783}
784
785
786int
788 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
789 std::map<int, int> seqLengths;
790 int numAdded = 0;
791 int numSeqs = 0;
792 for (NBNode* n : brokenNodes) {
793 NBEdge* edge = isBidiSwitch(n);
794 if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
795 std::vector<NBNode*> nodeSeq;
796 EdgeVector edgeSeq;
797 NBNode* prev = n;
798 nodeSeq.push_back(prev);
799 edgeSeq.push_back(edge);
800 bool forward = true;
801 //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
802 // find a suitable end point for adding bidi edges
803 while (forward) {
804 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
805 EdgeVector allRail;
806 getRailEdges(next, allRail, allRail);
807 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
808 prev = next;
809 edge = allRail.front() == edge ? allRail.back() : allRail.front();
810 nodeSeq.push_back(prev);
811 edgeSeq.push_back(edge);
812 } else {
813 forward = false;
814 EdgeVector inRail2, outRail2;
815 getRailEdges(next, inRail2, outRail2);
816 if (isBidiSwitch(next) == edge) {
817 // suitable switch found as endpoint, add reverse edges
818 //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
819 // + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
820 for (NBEdge* e : edgeSeq) {
821 addBidiEdge(ec, e);
822 }
823 seqLengths[(int)edgeSeq.size()]++;
824 numSeqs++;
825 numAdded += (int)edgeSeq.size();
826 } else {
827 //std::cout << " sequence ended at junction " << next->getID()
828 // << " in=" << inRail2.size()
829 // << " out=" << outRail2.size()
830 // << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
831 // << "\n";
832 }
833
834 }
835 }
836
837 }
838 }
839 if (seqLengths.size() > 0) {
840 WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
841 }
842 return numAdded;
843}
844
845
846std::set<NBPTLine*>
848 std::set<NBPTLine*> result;
849 std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
850 for (const auto& item : lc.getLines()) {
851 const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
852 if (stops.size() > 1) {
853 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
854 std::shared_ptr<NBPTStop> fromStop = *it;
855 std::shared_ptr<NBPTStop> toStop = *(it + 1);
856 visited.insert({fromStop, toStop});
857 }
858 }
859 }
860 for (const auto& item : lc.getLines()) {
861 const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
862 if (stops.size() > 1) {
863 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
864 std::shared_ptr<NBPTStop> fromStop = *it;
865 std::shared_ptr<NBPTStop> toStop = *(it + 1);
866 std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
867 if (visited.count(reverseTrip)) {
868 result.insert(item.second);
869 break;
870 }
871 }
872 }
873 }
874 return result;
875}
876
877int
879 const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
880 // generate bidirectional routing graph
881 std::vector<Track*> tracks;
882 for (NBEdge* edge : ec.getAllEdges()) {
883 tracks.push_back(new Track(edge));
884 }
885 const int numEdges = (int)tracks.size();
886 for (NBEdge* edge : ec.getAllEdges()) {
887 tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
888 }
889 // add special tracks for starting end ending in both directions
890 std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
891 for (NBEdge* edge : ec.getAllEdges()) {
892 if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
893 Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
894 tracks.push_back(start);
895 Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
896 tracks.push_back(end);
897 stopTracks[edge] = {start, end};
898 }
899 }
900 // set successors based on angle (connections are not yet built)
901 for (NBNode* node : getRailNodes(ec)) {
902 EdgeVector railEdges;
903 getRailEdges(node, railEdges, railEdges);
904 for (NBEdge* e1 : railEdges) {
905 for (NBEdge* e2 : railEdges) {
906 if (e1 != e2 && isStraight(node, e1, e2)) {
907 int i = e1->getNumericalID();
908 int i2 = e2->getNumericalID();
909 if (e1->getToNode() == node) {
910 if (e2->getFromNode() == node) {
911 // case 1) plain forward connection
912 tracks[i]->addSuccessor(tracks[i2]);
913 // reverse edge (numerical id incremented by numEdges)
914 tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
915 } else {
916 // case 2) both edges pointing towards each other
917 tracks[i]->addSuccessor(tracks[i2 + numEdges]);
918 tracks[i2]->addSuccessor(tracks[i + numEdges]);
919 }
920 } else {
921 if (e2->getFromNode() == node) {
922 // case 3) both edges pointing away from each other
923 tracks[i + numEdges]->addSuccessor(tracks[i2]);
924 tracks[i2 + numEdges]->addSuccessor(tracks[i]);
925 } else {
926 // already handled via case 1)
927 }
928 }
929
930 }
931 }
932 }
933 }
934 // define start and end successors
935 for (auto& item : stopTracks) {
936 const int index = item.first->getNumericalID();
937 // start
938 item.second.first->addSuccessor(tracks[index]);
939 item.second.first->addSuccessor(tracks[index + numEdges]);
940 // end
941 tracks[index]->addSuccessor(item.second.second);
942 tracks[index + numEdges]->addSuccessor(item.second.second);
943 }
944 // DEBUG
945 /*
946 for (Track* t : tracks) {
947 std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
948 for (Track* s : t->getSuccessors(SVC_IGNORING)) {
949 std::cout << " succ=" << s->getID() << "\n";
950 }
951 }
952 */
953
955 tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
956
957 int added = 0;
958 int numDisconnected = 0;
959 std::set<std::shared_ptr<NBPTStop>> addBidiStops;
960 std::set<NBEdge*, ComparatorIdLess> addBidiStopEdges;
961 std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
962 std::set<std::pair<std::string, std::string> > visited;
963
964 // the isConsistent heuristic may fail in some cases. If we observe that a
965 // specific sequence of stop ids in encoded in both directions, we take this
966 // as a reason to overrule the heuristic
967 std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
968
969 for (const auto& item : lc.getLines()) {
970 NBPTLine* line = item.second;
971 std::vector<NBPTLine::PTStopInfo> stops = line->getStopEdges(ec);
972 std::vector<NBEdge*> stopEdges;
973 for (auto it : stops) {
974 stopEdges.push_back(it.edge);
975 }
976 NBEdge* routeStart = line->getRouteStart(ec);
977 NBEdge* routeEnd = line->getRouteEnd(ec);
978 if (routeStart != nullptr && (stopEdges.empty() || routeStart != stopEdges.front())) {
979 stops.insert(stops.begin(), NBPTLine::PTStopInfo(routeStart, routeStart->getID(), 0, false));
980 }
981 if (routeEnd != nullptr && (stopEdges.empty() || routeEnd != stopEdges.back())) {
982 stops.push_back(NBPTLine::PTStopInfo(routeEnd, routeEnd->getID(), routeEnd->getLength(), false));
983 }
984 if (stops.size() < 2) {
985 continue;
986 }
987 if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
988 WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
989 continue;
990 }
991 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
992 NBEdge* fromEdge = it->edge;
993 NBEdge* toEdge = (it + 1)->edge;
994 const std::string fromStop = it->stopID;
995 const std::string toStop = (it + 1)->stopID;
996 const double fromPos = it->pos;
997 const double toPos = (it + 1)->pos;
998 bool fromRevised = it->revised;
999 bool toRevised = (it + 1)->revised;
1000 std::pair<std::string, std::string> trip(fromStop, toStop);
1001 std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
1002 //if (line->getLineID() == "147471") {
1003 // 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";
1004 //}
1005 if (visited.count(trip) != 0) {
1006 continue;
1007 } else {
1008 visited.insert(trip);
1009 }
1010 if (stopTracks.count(fromEdge) == 0
1011 || stopTracks.count(toEdge) == 0) {
1012 continue;
1013 }
1014 bool needBidi = visited.count(reverseTrip) != 0;
1015 NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
1016 std::vector<const Track*> route;
1017 Track* from = fromRevised ? tracks[fromEdge->getNumericalID()] : stopTracks[fromEdge].first;
1018 Track* to = toRevised ? tracks[toEdge->getNumericalID()] : stopTracks[toEdge].second;
1019 int iStart = fromRevised ? 0 : 1;
1020 int iDeltaEnd = toRevised ? 0 : 1;
1021 if (fromEdge == toEdge && fromPos > toPos) {
1022 // must use reverse edge
1023 route.push_back(tracks[fromEdge->getNumericalID() + numEdges]);
1024 iStart = 0;
1025 iDeltaEnd = 0;
1026 needBidi = true;
1027 } else {
1028 router->compute(from, to, &veh, 0, route);
1029 }
1030 //if (line->getLineID() == "147471") {
1031 // std::cout << "DEBUG: route=" << toString(route) << "\n";
1032 //}
1033 if (route.size() > 0) {
1034 assert((int)route.size() > iStart + iDeltaEnd);
1035 for (int i = iStart; i < (int)route.size() - iDeltaEnd; ++i) {
1036 const bool isBidi = route[i]->getNumericalID() >= numEdges;
1037 bool isStop = i == iStart || i == (int)route.size() - 1 - iDeltaEnd;
1038 if (isBidi || needBidi) {
1039 NBEdge* edge = route[i]->edge;
1040 if (addBidiEdges.count(edge) == 0) {
1041 if (!edge->isBidiRail(true)) {
1043 addBidiEdges.insert(edge);
1044 if (isStop) {
1045 addBidiStopEdges.insert(edge);
1046 }
1047 } else {
1048 if (isStop) {
1049 WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
1050 }
1051 }
1052 }
1053 }
1054 }
1055 if (isStop && isBidi) {
1056 std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
1057 if (fs) {
1058 addBidiStops.insert(fs);
1059 }
1060 std::shared_ptr<NBPTStop> ts = sc.get(toStop);
1061 if (ts) {
1062 addBidiStops.insert(ts);
1063 }
1064 }
1065 }
1066 } else {
1067 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
1068 numDisconnected++;
1069 }
1070 }
1071 }
1072 for (NBEdge* edge : addBidiEdges) {
1073 if (!edge->isBidiRail()) {
1074 NBEdge* e2 = addBidiEdge(ec, edge);
1075 //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
1076 if (e2 != nullptr) {
1077 added++;
1078 if (!minimal) {
1079 added += extendBidiEdges(ec, edge->getToNode(), edge);
1080 added += extendBidiEdges(ec, edge->getFromNode(), e2);
1081 }
1082 }
1083 }
1084 }
1085 for (std::shared_ptr<NBPTStop> stop : addBidiStops) {
1086 std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(stop, ec);
1087 if (fromReverse) {
1088 sc.insert(fromReverse);
1089 stop->setBidiStop(fromReverse);
1090 }
1091 }
1092 if (addBidiEdges.size() > 0 || numDisconnected > 0) {
1093 WRITE_MESSAGE("Added " + toString(addBidiStopEdges.size()) + " bidi-edges for public transport stops and a total of "
1094 + toString(added) + " bidi-edges to ensure connectivity of stops ("
1095 + toString(numDisconnected) + " stops remain disconnected)");
1096 }
1097
1098 // clean up
1099 for (Track* t : tracks) {
1100 delete t;
1101 }
1102 delete router;
1103 return (int)addBidiEdges.size();
1104}
1105
1106
1107int
1109 int added = 0;
1110 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
1111 for (const auto& e : ec) {
1112 if (!hasRailway(e.second->getPermissions())) {
1113 continue;
1114 }
1115 NBNode* const from = e.second->getFromNode();
1116 NBNode* const to = e.second->getToNode();
1117 if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
1118 continue;
1119 }
1120 if (e.second->isBidiRail()) {
1121 continue;
1122 }
1123 EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
1124 getRailEdges(from, inRailFrom, outRailFrom);
1125 getRailEdges(to, inRailTo, outRailTo);
1126 // check whether there is a straight edge pointing away from this one at the from-node
1127 // and there is no straight incoming edge at the from-node
1128 bool haveStraight = false;
1129 bool haveStraightReverse = false;
1130 if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
1131 for (const NBEdge* fromStraightCand : outRailFrom) {
1132 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1133 haveStraightReverse = true;
1134 //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
1135 break;
1136 }
1137 }
1138 if (haveStraightReverse) {
1139 for (const NBEdge* fromStraightCand : inRailFrom) {
1140 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1141 haveStraight = true;
1142 //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
1143 break;
1144 }
1145 }
1146 }
1147 }
1148 if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
1149 // check whether there is a straight edge pointing towards this one at the to-node
1150 // and there is no straight outgoing edge at the to-node
1151 haveStraight = false;
1152 haveStraightReverse = false;
1153 for (const NBEdge* toStraightCand : inRailTo) {
1154 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1155 haveStraightReverse = true;
1156 //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
1157 break;
1158 }
1159 }
1160 if (haveStraightReverse) {
1161 for (const NBEdge* toStraightCand : outRailTo) {
1162 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1163 haveStraight = true;
1164 //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
1165 break;
1166 }
1167 }
1168 }
1169 }
1170 //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
1171 if (haveStraightReverse && !haveStraight) {
1172 NBEdge* e2 = addBidiEdge(ec, e.second);
1173 //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
1174 if (e2 != nullptr) {
1175 added++;
1176 added += extendBidiEdges(ec, to, e.second);
1177 added += extendBidiEdges(ec, from, e2);
1178 }
1179 }
1180 }
1181 if (added > 0) {
1182 if (geometryLike) {
1183 WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
1184 } else {
1185 WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
1186 }
1187 }
1188 return added;
1189}
1190
1191
1192void
1197
1198
1199double
1200NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1201 return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
1202}
1203
1204
1205void
1207 // if fromUniDir=true, assign priority value for each railway edge:
1208 // 4: edge is unidirectional
1209 // 3: edge is in main direction of bidirectional track
1210 // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
1211 // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
1212 // 0: edge is part of bidirectional track in reverse of main direction
1213 //
1214 // otherwise:
1215 // assign priority value for each railway edge with priority -1 (undefined):
1216 // x: edges with priority >= 0 keep their priority
1217 // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
1218 // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
1219 // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
1220 // x-4 : edge is reverse direction of an x-1 edge
1221
1222 std::set<NBEdge*, ComparatorIdLess> bidi;
1223 EdgeSet uni;
1224 for (NBEdge* edge : ec.getAllEdges()) {
1225 if (hasRailway(edge->getPermissions())) {
1226 if (fromUniDir) {
1227 if (!edge->isBidiRail()) {
1228 edge->setPriority(4);
1229 edge->setRoutingType("4");
1230 uni.insert(edge);
1231 } else {
1232 bidi.insert(edge);
1233 }
1234 } else {
1235 if (edge->getPriority() >= 0) {
1236 uni.insert(edge);
1237 } else {
1238 bidi.insert(edge);
1239 }
1240 }
1241 }
1242 }
1243
1244 if (uni.size() == 0) {
1245 if (bidi.size() != 0) {
1246 WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
1247 }
1248 return;
1249 }
1250 EdgeSet seen;
1251 EdgeSet check = uni;
1252 EdgeSet forward;
1253 while (!check.empty()) {
1254 NBEdge* edge = *check.begin();
1255 check.erase(edge);
1256 if (seen.count(edge) != 0) {
1257 continue;
1258 }
1259 seen.insert(edge);
1260 NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
1261 if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
1262 forward.insert(straightOut);
1263 check.insert(straightOut);
1264 }
1265 NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
1266 if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
1267 forward.insert(straightIn);
1268 check.insert(straightIn);
1269 }
1270#ifdef DEBUG_DIRECTION_PRIORITY
1271 std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
1272 << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
1273 << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
1274 << "\n";
1275#endif
1276 }
1277
1278 for (NBEdge* edge : bidi) {
1279 NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
1280 int prio;
1281 int bidiPrio;
1282 if (forward.count(edge) != 0) {
1283 if (forward.count(bidiEdge) == 0) {
1284 prio = 3;
1285 bidiPrio = 0;
1286 } else {
1287 // both forward
1288 prio = 2;
1289 bidiPrio = 2;
1290 }
1291 } else {
1292 if (forward.count(bidiEdge) != 0) {
1293 prio = 0;
1294 bidiPrio = 3;
1295 } else {
1296 // neither forward
1297 prio = 1;
1298 bidiPrio = 1;
1299 }
1300 }
1301 if (bidiEdge == nullptr) {
1302 WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
1303 }
1304 if (edge->getPriority() >= 0) {
1305 bidiPrio = 0;
1306 }
1307 if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
1308 prio = 0;
1309 }
1310 if (edge->getPriority() < 0) {
1311 edge->setPriority(prio);
1312 edge->setRoutingType(toString(prio));
1313 }
1314 if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
1315 bidiEdge->setPriority(bidiPrio);
1316 bidiEdge->setRoutingType(toString(bidiPrio));
1317 }
1318 }
1319 std::map<int, int> numPrios;
1320 for (NBEdge* edge : bidi) {
1321 numPrios[edge->getPriority()]++;
1322 }
1323 if (fromUniDir) {
1324 WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1325 } else {
1326 WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1327 }
1328}
1329
1330// ---------------------------------------------------------------------------
1331// NBRailwaySignalGuesser methods
1332// ---------------------------------------------------------------------------
1333
1334int
1337 int addedSignals = 0;
1338 if (oc.exists("railway.signal.guess.by-stops")) {
1339 if (oc.getBool("railway.signal.guess.by-stops")) {
1340 const double minLength = oc.getFloat("osm.stop-output.length.train");
1341 addedSignals += guessByStops(ec, sc, minLength);
1342 }
1343 }
1344 return addedSignals;
1345}
1346
1347
1348bool
1350 return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
1351}
1352
1353int
1355 int addedSignals = 0;
1356 for (auto& item : sc.getStops()) {
1357 const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
1358 if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
1359 NBNode* to = stopEdge->getToNode();
1360 if (canBeSignal(to)) {
1362 addedSignals++;
1363 }
1364 NBNode* from = stopEdge->getFromNode();
1365 if (stopEdge->getLoadedLength() >= minLength) {
1367 if (canBeSignal(from)) {
1369 addedSignals++;
1370 }
1371 } else {
1372 double searchDist = minLength - stopEdge->getLoadedLength();
1373 while (searchDist > 0 && from->geometryLike()) {
1374 for (const NBEdge* in : from->getIncomingEdges()) {
1375 if (in->getFromNode() != stopEdge->getToNode()) {
1376 // found edge that isn't a bidi predecessor
1377 stopEdge = in;
1378 break;
1379 }
1380 }
1381 if (stopEdge->getFromNode() == from) {
1382 // bidi edge without predecessor
1383 break;
1384 } else {
1385 from = stopEdge->getFromNode();
1386 }
1387 searchDist -= stopEdge->getLoadedLength();
1388 }
1389 if (searchDist <= 0 && canBeSignal(from)) {
1391 addedSignals++;
1392 }
1393 }
1394 }
1395 }
1396 WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
1397 return addedSignals;
1398}
1399
1400
1401int
1403 int moved = 0;
1404 int numCorridors = 0;
1405 std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
1406 std::set<NBNode*> railGeomNodes;
1407 for (NBNode* n : railNodes) {
1408 if (n->geometryLike()) {
1409 railGeomNodes.insert(n);
1410 }
1411 }
1412 std::set<NBNode*, ComparatorIdLess> kinkNodes;;
1413 for (NBNode* n : railGeomNodes) {
1414 NBEdge* in = n->getIncomingEdges().front();
1415 NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1416 const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
1417 if (maxAngle > 0 && relAngle > maxAngle) {
1418 kinkNodes.insert(n);
1419 }
1420 }
1421 while (!kinkNodes.empty()) {
1422 std::vector<NBNode*> corridor;
1423 std::vector<NBEdge*> corridorEdges;
1424 Boundary corridorBox;
1425 double length = 0;
1426 NBNode* n = *kinkNodes.begin();
1427 kinkNodes.erase(kinkNodes.begin());
1428 // go downstream and upstream, add kinkNodes until a "long" enough
1429 // non-kink stretch is found
1430 NBEdge* in = n->getIncomingEdges().front();
1431 NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1432 NBEdge* const centerIn = in;
1433 NBEdge* const centerOut = out;
1434 NBNode* up = in->getFromNode();
1435 NBNode* down = out->getToNode();
1436 corridor.push_back(up);
1437 corridor.push_back(n);
1438 corridor.push_back(down);
1439 corridorBox.add(up->getPosition());
1440 corridorBox.add(down->getPosition());
1441 corridorEdges.push_back(in);
1442 corridorEdges.push_back(out);
1443 length += in->getLoadedLength();
1444 length += out->getLoadedLength();
1445 Position cBeg, cEnd, delta;
1446 while (kinkNodes.count(up) != 0) {
1447 NBEdge* const out2 = in;
1448 NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1449 length += in2->getLoadedLength();
1450 up = in2->getFromNode();
1451 corridor.insert(corridor.begin(), up);
1452 corridorEdges.insert(corridorEdges.begin(), in2);
1453 kinkNodes.erase(up);
1454 corridorBox.add(up->getPosition());
1455 }
1456 cBeg = up->getPosition();
1457 cEnd = down->getPosition();
1458 delta = cEnd - cBeg;
1459 while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
1460 NBEdge* const out2 = in;
1461 NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1462 length += in2->getLoadedLength();
1463 up = in2->getFromNode();
1464 corridor.insert(corridor.begin(), up);
1465 corridorEdges.insert(corridorEdges.begin(), in2);
1466 kinkNodes.erase(up);
1467 corridorBox.add(up->getPosition());
1468 cBeg = up->getPosition();
1469 cEnd = down->getPosition();
1470 delta = cEnd - cBeg;
1471 }
1472 in = centerIn;
1473 out = centerOut;
1474 while (kinkNodes.count(down) != 0) {
1475 NBEdge* const in2 = out;
1476 NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1477 down = out2->getToNode();
1478 length += out2->getLoadedLength();
1479 corridor.push_back(down);
1480 corridorEdges.push_back(out2);
1481 kinkNodes.erase(down);
1482 corridorBox.add(down->getPosition());
1483 }
1484 cBeg = up->getPosition();
1485 cEnd = down->getPosition();
1486 delta = cEnd - cBeg;
1487 while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
1488 NBEdge* const in2 = out;
1489 NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1490 down = out2->getToNode();
1491 length += out2->getLoadedLength();
1492 corridor.push_back(down);
1493 corridorEdges.push_back(out2);
1494 kinkNodes.erase(down);
1495 corridorBox.add(down->getPosition());
1496 cBeg = up->getPosition();
1497 cEnd = down->getPosition();
1498 delta = cEnd - cBeg;
1499 }
1500 // straighten all edges in corridor (corridorEdges doesn't include bidi)
1501 std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
1502 for (NBNode* n2 : corridorNodes) {
1503 for (NBEdge* e : n2->getEdges()) {
1504 if (corridorNodes.count(e->getFromNode()) != 0
1505 && corridorNodes.count(e->getToNode()) != 0) {
1506 PositionVector simpleGeom;
1507 simpleGeom.push_back(e->getFromNode()->getPosition());
1508 simpleGeom.push_back(e->getToNode()->getPosition());
1509 e->setGeometry(simpleGeom);
1510 }
1511 }
1512 }
1513 if (delta.length2D() > 0) {
1514 double currLength = 0;
1515 for (int i = 1; i < (int)corridor.size() - 1; i++) {
1516 currLength += corridorEdges[i - 1]->getLoadedLength();
1517 const Position newPos = cBeg + delta * (currLength / length);
1518 NBNode* const n2 = corridor[i];
1519 n2->reinit(newPos, n2->getType());
1520 for (NBEdge* e : n2->getEdges()) {
1521 e->resetEndpointAtNode(n2);
1522 }
1523 moved += 1;
1524 }
1525 numCorridors += 1;
1526 } else {
1527 WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
1528 }
1529 }
1530 //std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
1531
1532 WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
1533 return moved;
1534}
1535
1536/****************************************************************************/
#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.
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:289
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
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
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:5027
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:5042
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)
mark this edge as a bidi edge
Definition NBEdge.h:1441
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:4026
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 int addBidiEdgesForStops(NBEdgeCont &ec, NBPTLineCont &lc, NBPTStopCont &sc, bool minimal)
add bidi-edges to connect successive public transport stops
static std::set< NBPTLine * > findBidiCandidates(NBPTLineCont &lc)
identify lines that are likely to require bidirectional tracks
static int addBidiEdgesForStraightConnectivity(NBEdgeCont &ec, bool geometryLike)
add bidi-edges to connect straight tracks
static bool allSharp(const NBNode *node, const EdgeVector &in, const EdgeVector &out, bool countBidiAsSharp=false)
static bool allBroken(const NBNode *node, NBEdge *candOut, const EdgeVector &in, const EdgeVector &out)
static std::set< NBNode * > getBrokenRailNodes(NBEdgeCont &ec, bool verbose=false)
static int addBidiEdgesBetweenSwitches(NBEdgeCont &ec)
add bidi-edges to connect switches that are approached in both directions
static bool allBidi(const EdgeVector &edges)
static int makeAllBidi(NBEdgeCont &ec)
static double getTravelTimeStatic(const Track *const track, const NBVehicle *const veh, double time)
static bool hasRailway(SVCPermissions permissions)
filter for rail edges but do not return (legacy) all purpose edges
static 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.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
void close()
Closes the device and removes it from the dictionary.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
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