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