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-2024 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); // 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 for (NBNode* n : brokenNodes) {
577 EdgeVector inRail, outRail;
578 getRailEdges(n, inRail, outRail);
579 for (NBEdge* start : outRail) {
580 EdgeVector tmp;
581 tmp.push_back(start);
582 // only reverse edges where the node would be unbroken afterwards
583 if (!allBroken(n, start, inRail, outRail)
584 || (inRail.size() == 1 && outRail.size() == 1)) {
585#ifdef DEBUG_SEQSTOREVERSE
586 if (n->getID() == DEBUGNODEID) {
587 std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
588 }
589#endif
590 continue;
591 }
592 //std::cout << " get sequences from " << start->getID() << "\n";
593 bool forward = true;
594 EdgeVector seq;
595 while (forward) {
596 seq.push_back(start);
597 //std::cout << " seq=" << toString(seq) << "\n";
598 NBNode* n2 = start->getToNode();
599 EdgeVector inRail2, outRail2;
600 getRailEdges(n2, inRail2, outRail2);
601 if (brokenNodes.count(n2) != 0) {
602 EdgeVector tmp2;
603 tmp2.push_back(start);
604 if (allBroken(n2, start, outRail2, inRail2)) {
605 seqsToReverse.push_back(seq);
606 } else {
607#ifdef DEBUG_SEQSTOREVERSE
608 if (n->getID() == DEBUGNODEID) {
609 std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
610 }
611#endif
612 }
613 forward = false;
614 } else {
615 if (outRail2.size() == 0) {
616 // stop at network border
617 forward = false;
618#ifdef DEBUG_SEQSTOREVERSE
619 if (n->getID() == DEBUGNODEID) {
620 std::cout << " abort at n2=" << n2->getID() << " (border)\n";
621 }
622#endif
623 } else if (outRail2.size() > 1 || inRail2.size() > 1) {
624 // stop at switch
625 forward = false;
626#ifdef DEBUG_SEQSTOREVERSE
627 if (n->getID() == DEBUGNODEID) {
628 std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
629 }
630#endif
631 } else {
632 start = outRail2.front();
633 }
634 }
635 }
636 }
637 }
638 // sort by sequence length
639 if (seqsToReverse.size() > 0) {
640 WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
641 }
642 std::sort(seqsToReverse.begin(), seqsToReverse.end(),
643 [](const EdgeVector & a, const EdgeVector & b) {
644 return a.size() < b.size();
645 });
646 int numReversed = 0;
647 std::set<NBNode*> affectedEndpoints;
648 std::set<std::string> reversedIDs;
649 std::map<int, int> seqLengths;
650 for (EdgeVector& seq : seqsToReverse) {
651 NBNode* seqStart = seq.front()->getFromNode();
652 NBNode* seqEnd = seq.back()->getToNode();
653 // avoid reversing sequences on both sides of a broken node
654 if (affectedEndpoints.count(seqStart) == 0
655 && affectedEndpoints.count(seqEnd) == 0) {
656 affectedEndpoints.insert(seqStart);
657 affectedEndpoints.insert(seqEnd);
658 //WRITE_MESSAGE(" reversed seq=" + toString(seq));
659 for (NBEdge* e : seq) {
660 e->reinitNodes(e->getToNode(), e->getFromNode());
661 e->setGeometry(e->getGeometry().reverse());
662 reversedIDs.insert(e->getID());
663 if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
665 }
666 }
667 seqLengths[(int)seq.size()]++;
668 numReversed++;
669 }
670 }
671 if (numReversed > 0) {
672 WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
673 for (auto& item : sc.getStops()) {
674 if (reversedIDs.count(item.second->getEdgeId())) {
675 item.second->findLaneAndComputeBusStopExtent(ec);
676 }
677 }
678 }
679 return numReversed;
680}
681
682
683int
685 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
686 std::set<NBNode*> railNodes = getRailNodes(ec);
687 // find buffer stops and ensure that they are connect to the network in both directions
688 int numBufferStops = 0;
689 int numAddedBidiTotal = 0;
690 for (NBNode* node : railNodes) {
691 if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
692 if (node->getEdges().size() != 1) {
693 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
694 continue;
695 }
696 // int numAddedBidi = 0;
697 numBufferStops++;
698 NBEdge* prev = nullptr;
699 NBEdge* prev2 = nullptr;
700 EdgeVector inRail, outRail;
701 getRailEdges(node, inRail, outRail);
702 bool addAway = true; // add new edges away from buffer stop
703 while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
704 NBEdge* e = nullptr;
705 if (prev == nullptr) {
706 assert(node->getEdges().size() == 1);
707 e = node->getEdges().front();
708 addAway = node == e->getToNode();
709 } else {
710 if (addAway) {
711 // XXX if node is broken we need to switch direction
712 assert(inRail.size() == 2);
713 e = inRail.front() == prev2 ? inRail.back() : inRail.front();
714 } else {
715 // XXX if node is broken we need to switch direction
716 assert(outRail.size() == 2);
717 e = outRail.front() == prev2 ? outRail.back() : outRail.front();
718 }
719 }
721 NBNode* e2From = nullptr;
722 NBNode* e2To = nullptr;
723 if (addAway) {
724 e2From = node;
725 e2To = e->getFromNode();
726 node = e2To;
727 } else {
728 e2From = e->getToNode();
729 e2To = node;
730 node = e2From;
731 }
732 NBEdge* e2 = addBidiEdge(ec, e);
733 if (e2 == nullptr) {
734 break;
735 }
736 prev = e;
737 prev2 = e2;
738 // numAddedBidi++;
739 numAddedBidiTotal++;
740 inRail.clear();
741 outRail.clear();
742 getRailEdges(node, inRail, outRail);
743 }
744 //if (numAddedBidi > 0) {
745 // WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
746 //}
747 }
748 }
749 if (numAddedBidiTotal > 0) {
750 WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
751 }
752 return numAddedBidiTotal;
753}
754
755NBEdge*
757 EdgeVector inRail, outRail;
758 getRailEdges(n, inRail, outRail);
759 if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
760 if (isStraight(n, inRail.front(), outRail.front())) {
761 return inRail.front();
762 } else if (isStraight(n, inRail.back(), outRail.front())) {
763 return inRail.back();
764 }
765 }
766 if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
767 if (isStraight(n, outRail.front(), inRail.front())) {
768 return outRail.front();
769 } else if (isStraight(n, outRail.back(), inRail.front())) {
770 return outRail.back();
771 }
772 }
773 return nullptr;
774}
775
776
777int
779 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
780 std::map<int, int> seqLengths;
781 int numAdded = 0;
782 int numSeqs = 0;
783 for (NBNode* n : brokenNodes) {
784 NBEdge* edge = isBidiSwitch(n);
785 if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
786 std::vector<NBNode*> nodeSeq;
787 EdgeVector edgeSeq;
788 NBNode* prev = n;
789 nodeSeq.push_back(prev);
790 edgeSeq.push_back(edge);
791 bool forward = true;
792 //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
793 // find a suitable end point for adding bidi edges
794 while (forward) {
795 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
796 EdgeVector allRail;
797 getRailEdges(next, allRail, allRail);
798 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
799 prev = next;
800 edge = allRail.front() == edge ? allRail.back() : allRail.front();
801 nodeSeq.push_back(prev);
802 edgeSeq.push_back(edge);
803 } else {
804 forward = false;
805 EdgeVector inRail2, outRail2;
806 getRailEdges(next, inRail2, outRail2);
807 if (isBidiSwitch(next) == edge) {
808 // suitable switch found as endpoint, add reverse edges
809 //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
810 // + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
811 for (NBEdge* e : edgeSeq) {
812 addBidiEdge(ec, e);
813 }
814 seqLengths[(int)edgeSeq.size()]++;
815 numSeqs++;
816 numAdded += (int)edgeSeq.size();
817 } else {
818 //std::cout << " sequence ended at junction " << next->getID()
819 // << " in=" << inRail2.size()
820 // << " out=" << outRail2.size()
821 // << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
822 // << "\n";
823 }
824
825 }
826 }
827
828 }
829 }
830 if (seqLengths.size() > 0) {
831 WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
832 }
833 return numAdded;
834}
835
836
837std::set<NBPTLine*>
839 std::set<NBPTLine*> result;
840 std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
841 for (const auto& item : lc.getLines()) {
842 const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
843 if (stops.size() > 1) {
844 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
845 std::shared_ptr<NBPTStop> fromStop = *it;
846 std::shared_ptr<NBPTStop> toStop = *(it + 1);
847 visited.insert({fromStop, toStop});
848 }
849 }
850 }
851 for (const auto& item : lc.getLines()) {
852 const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
853 if (stops.size() > 1) {
854 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
855 std::shared_ptr<NBPTStop> fromStop = *it;
856 std::shared_ptr<NBPTStop> toStop = *(it + 1);
857 std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
858 if (visited.count(reverseTrip)) {
859 result.insert(item.second);
860 break;
861 }
862 }
863 }
864 }
865 return result;
866}
867
868int
870 const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
871 // generate bidirectional routing graph
872 std::vector<Track*> tracks;
873 for (NBEdge* edge : ec.getAllEdges()) {
874 tracks.push_back(new Track(edge));
875 }
876 const int numEdges = (int)tracks.size();
877 for (NBEdge* edge : ec.getAllEdges()) {
878 tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
879 }
880 // add special tracks for starting end ending in both directions
881 std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
882 for (NBEdge* edge : ec.getAllEdges()) {
883 if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
884 Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
885 tracks.push_back(start);
886 Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
887 tracks.push_back(end);
888 stopTracks[edge] = {start, end};
889 }
890 }
891 // set successors based on angle (connections are not yet built)
892 for (NBNode* node : getRailNodes(ec)) {
893 EdgeVector railEdges;
894 getRailEdges(node, railEdges, railEdges);
895 for (NBEdge* e1 : railEdges) {
896 for (NBEdge* e2 : railEdges) {
897 if (e1 != e2 && isStraight(node, e1, e2)) {
898 int i = e1->getNumericalID();
899 int i2 = e2->getNumericalID();
900 if (e1->getToNode() == node) {
901 if (e2->getFromNode() == node) {
902 // case 1) plain forward connection
903 tracks[i]->addSuccessor(tracks[i2]);
904 // reverse edge (numerical id incremented by numEdges)
905 tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
906 } else {
907 // case 2) both edges pointing towards each other
908 tracks[i]->addSuccessor(tracks[i2 + numEdges]);
909 tracks[i2]->addSuccessor(tracks[i + numEdges]);
910 }
911 } else {
912 if (e2->getFromNode() == node) {
913 // case 3) both edges pointing away from each other
914 tracks[i + numEdges]->addSuccessor(tracks[i2]);
915 tracks[i2 + numEdges]->addSuccessor(tracks[i]);
916 } else {
917 // already handled via case 1)
918 }
919 }
920
921 }
922 }
923 }
924 }
925 // define start and end successors
926 for (auto& item : stopTracks) {
927 const int index = item.first->getNumericalID();
928 // start
929 item.second.first->addSuccessor(tracks[index]);
930 item.second.first->addSuccessor(tracks[index + numEdges]);
931 // end
932 tracks[index]->addSuccessor(item.second.second);
933 tracks[index + numEdges]->addSuccessor(item.second.second);
934 }
935 // DEBUG
936 /*
937 for (Track* t : tracks) {
938 std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
939 for (Track* s : t->getSuccessors(SVC_IGNORING)) {
940 std::cout << " succ=" << s->getID() << "\n";
941 }
942 }
943 */
944
946 tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
947
948 int added = 0;
949 int numDisconnected = 0;
950 std::set<NBEdge*, ComparatorIdLess> addBidiStops;
951 std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
952 std::set<std::pair<std::string, std::string> > visited;
953
954 // the isConsistent heuristic may fail in some cases. If we observe that a
955 // specific sequence of stop ids in encoded in both directions, we take this
956 // as a reason to overrule the heuristic
957 std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
958
959 for (const auto& item : lc.getLines()) {
960 NBPTLine* line = item.second;
961 std::vector<std::pair<NBEdge*, std::string> > stops = line->getStopEdges(ec);
962 std::vector<NBEdge*> stopEdges;
963 for (auto it : stops) {
964 stopEdges.push_back(it.first);
965 }
966 NBEdge* routeStart = line->getRouteStart(ec);
967 NBEdge* routeEnd = line->getRouteEnd(ec);
968 if (routeStart != nullptr) {
969 stops.insert(stops.begin(), {routeStart, routeStart->getID()});
970 }
971 if (routeEnd != nullptr) {
972 stops.push_back({routeEnd, routeEnd->getID()});
973 }
974 if (stops.size() < 2) {
975 continue;
976 }
977 if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
978 WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
979 continue;
980 }
981 for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
982 NBEdge* fromEdge = it->first;
983 NBEdge* toEdge = (it + 1)->first;
984 const std::string fromStop = it->second;
985 const std::string toStop = (it + 1)->second;
986 std::pair<std::string, std::string> trip(fromStop, toStop);
987 std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
988 //std::cout << " line=" << line->getLineID() << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << " fromStop=" << fromStop << " toStop=" << toStop << "\n";
989 if (visited.count(trip) != 0) {
990 continue;
991 } else {
992 visited.insert(trip);
993 }
994 if (stopTracks.count(fromEdge) == 0
995 || stopTracks.count(toEdge) == 0) {
996 continue;
997 }
998 const bool needBidi = visited.count(reverseTrip) != 0;
999 NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
1000 std::vector<const Track*> route;
1001 router->compute(stopTracks[fromEdge].first, stopTracks[toEdge].second, &veh, 0, route);
1002 //if (fromEdge->getID() == "356053025#1" && toEdge->getID() == "23256161") {
1003 // std::cout << "DEBUG: route=" << toString(route) << "\n";
1004 //}
1005 if (route.size() > 0) {
1006 assert(route.size() > 2);
1007 for (int i = 1; i < (int)route.size() - 1; ++i) {
1008 if (route[i]->getNumericalID() >= numEdges || needBidi) {
1009 NBEdge* edge = route[i]->edge;
1010 if (addBidiEdges.count(edge) == 0) {
1011 bool isStop = i == 1 || i == (int)route.size() - 2;
1012 if (!edge->isBidiRail(true)) {
1014 addBidiEdges.insert(edge);
1015 if (isStop) {
1016 addBidiStops.insert(edge);
1017 }
1018 } else {
1019 if (isStop) {
1020 WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
1021 }
1022 }
1023 } else if (isStop && needBidi) {
1024 std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
1025 if (fs) {
1026 std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(fs, ec);
1027 if (fromReverse) {
1028 sc.insert(fromReverse);
1029 fs->setBidiStop(fromReverse);
1030 }
1031 }
1032 std::shared_ptr<NBPTStop> ts = sc.get(toStop);
1033 if (ts) {
1034 std::shared_ptr<NBPTStop> toReverse = sc.getReverseStop(ts, ec);
1035 if (toReverse) {
1036 sc.insert(toReverse);
1037 ts->setBidiStop(toReverse);
1038 }
1039 }
1040 }
1041 }
1042 }
1043 }
1044 } else {
1045 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
1046 numDisconnected++;
1047 }
1048 }
1049 }
1050 for (NBEdge* edge : addBidiEdges) {
1051 if (!edge->isBidiRail()) {
1052 NBEdge* e2 = addBidiEdge(ec, edge);
1053 //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
1054 if (e2 != nullptr) {
1055 added++;
1056 if (!minimal) {
1057 added += extendBidiEdges(ec, edge->getToNode(), edge);
1058 added += extendBidiEdges(ec, edge->getFromNode(), e2);
1059 }
1060 }
1061 }
1062 }
1063
1064 if (addBidiEdges.size() > 0 || numDisconnected > 0) {
1065 WRITE_MESSAGE("Added " + toString(addBidiStops.size()) + " bidi-edges for public transport stops and a total of "
1066 + toString(added) + " bidi-edges to ensure connectivity of stops ("
1067 + toString(numDisconnected) + " stops remain disconnected)");
1068 }
1069
1070 // clean up
1071 for (Track* t : tracks) {
1072 delete t;
1073 }
1074 delete router;
1075 return (int)addBidiEdges.size();
1076}
1077
1078
1079int
1081 int added = 0;
1082 std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
1083 for (const auto& e : ec) {
1084 if (!hasRailway(e.second->getPermissions())) {
1085 continue;
1086 }
1087 NBNode* const from = e.second->getFromNode();
1088 NBNode* const to = e.second->getToNode();
1089 if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
1090 continue;
1091 }
1092 if (e.second->isBidiRail()) {
1093 continue;
1094 }
1095 EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
1096 getRailEdges(from, inRailFrom, outRailFrom);
1097 getRailEdges(to, inRailTo, outRailTo);
1098 // check whether there is a straight edge pointing away from this one at the from-node
1099 // and there is no straight incoming edge at the from-node
1100 bool haveStraight = false;
1101 bool haveStraightReverse = false;
1102 if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
1103 for (const NBEdge* fromStraightCand : outRailFrom) {
1104 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1105 haveStraightReverse = true;
1106 //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
1107 break;
1108 }
1109 }
1110 if (haveStraightReverse) {
1111 for (const NBEdge* fromStraightCand : inRailFrom) {
1112 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1113 haveStraight = true;
1114 //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
1115 break;
1116 }
1117 }
1118 }
1119 }
1120 if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
1121 // check whether there is a straight edge pointing towards this one at the to-node
1122 // and there is no straight outgoing edge at the to-node
1123 haveStraight = false;
1124 haveStraightReverse = false;
1125 for (const NBEdge* toStraightCand : inRailTo) {
1126 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1127 haveStraightReverse = true;
1128 //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
1129 break;
1130 }
1131 }
1132 if (haveStraightReverse) {
1133 for (const NBEdge* toStraightCand : outRailTo) {
1134 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1135 haveStraight = true;
1136 //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
1137 break;
1138 }
1139 }
1140 }
1141 }
1142 //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
1143 if (haveStraightReverse && !haveStraight) {
1144 NBEdge* e2 = addBidiEdge(ec, e.second);
1145 //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
1146 if (e2 != nullptr) {
1147 added++;
1148 added += extendBidiEdges(ec, to, e.second);
1149 added += extendBidiEdges(ec, from, e2);
1150 }
1151 }
1152 }
1153 if (added > 0) {
1154 if (geometryLike) {
1155 WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
1156 } else {
1157 WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
1158 }
1159 }
1160 return added;
1161}
1162
1163
1164void
1169
1170
1171double
1172NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1173 return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
1174}
1175
1176
1177void
1179 // if fromUniDir=true, assign priority value for each railway edge:
1180 // 4: edge is unidirectional
1181 // 3: edge is in main direction of bidirectional track
1182 // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
1183 // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
1184 // 0: edge is part of bidirectional track in reverse of main direction
1185 //
1186 // otherwise:
1187 // assign priority value for each railway edge with priority -1 (undefined):
1188 // x: edges with priority >= 0 keep their priority
1189 // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
1190 // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
1191 // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
1192 // x-4 : edge is reverse direction of an x-1 edge
1193
1194 std::set<NBEdge*, ComparatorIdLess> bidi;
1195 EdgeSet uni;
1196 for (NBEdge* edge : ec.getAllEdges()) {
1197 if (hasRailway(edge->getPermissions())) {
1198 if (fromUniDir) {
1199 if (!edge->isBidiRail()) {
1200 edge->setPriority(4);
1201 uni.insert(edge);
1202 } else {
1203 bidi.insert(edge);
1204 }
1205 } else {
1206 if (edge->getPriority() >= 0) {
1207 uni.insert(edge);
1208 } else {
1209 bidi.insert(edge);
1210 }
1211 }
1212 }
1213 }
1214
1215 if (uni.size() == 0) {
1216 if (bidi.size() != 0) {
1217 WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
1218 }
1219 return;
1220 }
1221 EdgeSet seen;
1222 EdgeSet check = uni;
1223 EdgeSet forward;
1224 while (!check.empty()) {
1225 NBEdge* edge = *check.begin();
1226 check.erase(edge);
1227 if (seen.count(edge) != 0) {
1228 continue;
1229 }
1230 seen.insert(edge);
1231 NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
1232 if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
1233 forward.insert(straightOut);
1234 check.insert(straightOut);
1235 }
1236 NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
1237 if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
1238 forward.insert(straightIn);
1239 check.insert(straightIn);
1240 }
1241#ifdef DEBUG_DIRECTION_PRIORITY
1242 std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
1243 << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
1244 << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
1245 << "\n";
1246#endif
1247 }
1248
1249 for (NBEdge* edge : bidi) {
1250 NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
1251 int prio;
1252 int bidiPrio;
1253 if (forward.count(edge) != 0) {
1254 if (forward.count(bidiEdge) == 0) {
1255 prio = 3;
1256 bidiPrio = 0;
1257 } else {
1258 // both forward
1259 prio = 2;
1260 bidiPrio = 2;
1261 }
1262 } else {
1263 if (forward.count(bidiEdge) != 0) {
1264 prio = 0;
1265 bidiPrio = 3;
1266 } else {
1267 // neither forward
1268 prio = 1;
1269 bidiPrio = 1;
1270 }
1271 }
1272 if (bidiEdge == nullptr) {
1273 WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
1274 }
1275 if (edge->getPriority() >= 0) {
1276 bidiPrio = 0;
1277 }
1278 if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
1279 prio = 0;
1280 }
1281 if (edge->getPriority() < 0) {
1282 edge->setPriority(prio);
1283 }
1284 if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
1285 bidiEdge->setPriority(bidiPrio);
1286 }
1287 }
1288 std::map<int, int> numPrios;
1289 for (NBEdge* edge : bidi) {
1290 numPrios[edge->getPriority()]++;
1291 }
1292 if (fromUniDir) {
1293 WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1294 } else {
1295 WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1296 }
1297}
1298
1299// ---------------------------------------------------------------------------
1300// NBRailwaySignalGuesser methods
1301// ---------------------------------------------------------------------------
1302
1303int
1306 int addedSignals = 0;
1307 if (oc.exists("railway.signal.guess.by-stops")) {
1308 if (oc.getBool("railway.signal.guess.by-stops")) {
1309 const double minLength = oc.getFloat("osm.stop-output.length.train");
1310 addedSignals += guessByStops(ec, sc, minLength);
1311 }
1312 }
1313 return addedSignals;
1314}
1315
1316
1317bool
1319 return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
1320}
1321
1322int
1324 int addedSignals = 0;
1325 for (auto& item : sc.getStops()) {
1326 const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
1327 if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
1328 NBNode* to = stopEdge->getToNode();
1329 if (canBeSignal(to)) {
1331 addedSignals++;
1332 }
1333 NBNode* from = stopEdge->getFromNode();
1334 if (stopEdge->getLoadedLength() >= minLength) {
1336 if (canBeSignal(from)) {
1338 addedSignals++;
1339 }
1340 } else {
1341 double searchDist = minLength - stopEdge->getLoadedLength();
1342 while (searchDist > 0 && from->geometryLike()) {
1343 for (const NBEdge* in : from->getIncomingEdges()) {
1344 if (in->getFromNode() != stopEdge->getToNode()) {
1345 // found edge that isn't a bidi predecessor
1346 stopEdge = in;
1347 break;
1348 }
1349 }
1350 if (stopEdge->getFromNode() == from) {
1351 // bidi edge without predecessor
1352 break;
1353 } else {
1354 from = stopEdge->getFromNode();
1355 }
1356 searchDist -= stopEdge->getLoadedLength();
1357 }
1358 if (searchDist <= 0 && canBeSignal(from)) {
1360 addedSignals++;
1361 }
1362 }
1363 }
1364 }
1365 WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
1366 return addedSignals;
1367}
1368
1369
1370int
1372 int moved = 0;
1373 int numCorridors = 0;
1374 std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
1375 std::set<NBNode*> railGeomNodes;
1376 for (NBNode* n : railNodes) {
1377 if (n->geometryLike()) {
1378 railGeomNodes.insert(n);
1379 }
1380 }
1381 std::set<NBNode*, ComparatorIdLess> kinkNodes;;
1382 for (NBNode* n : railGeomNodes) {
1383 NBEdge* in = n->getIncomingEdges().front();
1384 NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1385 const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
1386 if (maxAngle > 0 && relAngle > maxAngle) {
1387 kinkNodes.insert(n);
1388 }
1389 }
1390 while (!kinkNodes.empty()) {
1391 std::vector<NBNode*> corridor;
1392 std::vector<NBEdge*> corridorEdges;
1393 Boundary corridorBox;
1394 double length = 0;
1395 NBNode* n = *kinkNodes.begin();
1396 kinkNodes.erase(kinkNodes.begin());
1397 // go downstream and upstream, add kinkNodes until a "long" enough
1398 // non-kink stretch is found
1399 NBEdge* in = n->getIncomingEdges().front();
1400 NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1401 NBEdge* const centerIn = in;
1402 NBEdge* const centerOut = out;
1403 NBNode* up = in->getFromNode();
1404 NBNode* down = out->getToNode();
1405 corridor.push_back(up);
1406 corridor.push_back(n);
1407 corridor.push_back(down);
1408 corridorBox.add(up->getPosition());
1409 corridorBox.add(down->getPosition());
1410 corridorEdges.push_back(in);
1411 corridorEdges.push_back(out);
1412 length += in->getLoadedLength();
1413 length += out->getLoadedLength();
1414 Position cBeg, cEnd, delta;
1415 while (kinkNodes.count(up) != 0) {
1416 NBEdge* const out2 = in;
1417 NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1418 length += in2->getLoadedLength();
1419 up = in2->getFromNode();
1420 corridor.insert(corridor.begin(), up);
1421 corridorEdges.insert(corridorEdges.begin(), in2);
1422 kinkNodes.erase(up);
1423 corridorBox.add(up->getPosition());
1424 }
1425 cBeg = up->getPosition();
1426 cEnd = down->getPosition();
1427 delta = cEnd - cBeg;
1428 while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
1429 NBEdge* const out2 = in;
1430 NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1431 length += in2->getLoadedLength();
1432 up = in2->getFromNode();
1433 corridor.insert(corridor.begin(), up);
1434 corridorEdges.insert(corridorEdges.begin(), in2);
1435 kinkNodes.erase(up);
1436 corridorBox.add(up->getPosition());
1437 cBeg = up->getPosition();
1438 cEnd = down->getPosition();
1439 delta = cEnd - cBeg;
1440 }
1441 in = centerIn;
1442 out = centerOut;
1443 while (kinkNodes.count(down) != 0) {
1444 NBEdge* const in2 = out;
1445 NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1446 down = out2->getToNode();
1447 length += out2->getLoadedLength();
1448 corridor.push_back(down);
1449 corridorEdges.push_back(out2);
1450 kinkNodes.erase(down);
1451 corridorBox.add(down->getPosition());
1452 }
1453 cBeg = up->getPosition();
1454 cEnd = down->getPosition();
1455 delta = cEnd - cBeg;
1456 while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
1457 NBEdge* const in2 = out;
1458 NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1459 down = out2->getToNode();
1460 length += out2->getLoadedLength();
1461 corridor.push_back(down);
1462 corridorEdges.push_back(out2);
1463 kinkNodes.erase(down);
1464 corridorBox.add(down->getPosition());
1465 cBeg = up->getPosition();
1466 cEnd = down->getPosition();
1467 delta = cEnd - cBeg;
1468 }
1469 // straighten all edges in corridor (corridorEdges doesn't include bidi)
1470 std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
1471 for (NBNode* n2 : corridorNodes) {
1472 for (NBEdge* e : n2->getEdges()) {
1473 if (corridorNodes.count(e->getFromNode()) != 0
1474 && corridorNodes.count(e->getToNode()) != 0) {
1475 PositionVector simpleGeom;
1476 simpleGeom.push_back(e->getFromNode()->getPosition());
1477 simpleGeom.push_back(e->getToNode()->getPosition());
1478 e->setGeometry(simpleGeom);
1479 }
1480 }
1481 }
1482 if (delta.length2D() > 0) {
1483 double currLength = 0;
1484 for (int i = 1; i < (int)corridor.size() - 1; i++) {
1485 currLength += corridorEdges[i - 1]->getLoadedLength();
1486 const Position newPos = cBeg + delta * (currLength / length);
1487 NBNode* const n2 = corridor[i];
1488 n2->reinit(newPos, n2->getType());
1489 for (NBEdge* e : n2->getEdges()) {
1490 e->resetEndpointAtNode(n2);
1491 }
1492 moved += 1;
1493 }
1494 numCorridors += 1;
1495 } else {
1496 WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
1497 }
1498 }
1499 //std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
1500
1501 WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
1502 return moved;
1503}
1504
1505/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define RAD2DEG(x)
Definition GeomHelper.h:36
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:298
#define WRITE_MESSAGE(msg)
Definition MsgHandler.h:297
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
#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 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:37
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
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:78
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:481
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
The representation of a single edge during network building.
Definition NBEdge.h:92
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4378
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition NBEdge.h:602
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:546
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition NBEdge.h:783
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition NBEdge.cpp:998
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition NBEdge.cpp:749
NBEdge * getStraightContinuation(SVCPermissions permissions) const
return the straightest follower edge for the given permissions or nullptr (never returns turn-arounds...
Definition NBEdge.cpp:4857
static double getTravelTimeStatic(const NBEdge *const edge, const NBVehicle *const, double)
Definition NBEdge.h:1495
NBEdge * getStraightPredecessor(SVCPermissions permissions) const
return the straightest predecessor edge for the given permissions or nullptr (never returns turn-arou...
Definition NBEdge.cpp:4872
const std::string & getID() const
Definition NBEdge.h:1528
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition NBEdge.cpp:992
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition NBEdge.cpp:3664
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition NBEdge.cpp:1528
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:539
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4007
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2158
void setPriority(int priority)
Sets the priority of the edge.
Definition NBEdge.h:532
int getPriority() const
Returns the priority of the edge.
Definition NBEdge.h:527
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1392
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:338
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:3783
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:253
std::vector< std::pair< NBEdge *, std::string > > getStopEdges(const NBEdgeCont &ec) const
get stop edges and stop ids
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:223
NBEdge * getRouteStart(const NBEdgeCont &ec) const
return first valid edge of myRoute (if it doest not lie after the first stop)
Definition NBPTLine.cpp:193
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 void analyzeTopology(NBEdgeCont &ec)
static int addBidiEdgesForStops(NBEdgeCont &ec, NBPTLineCont &lc, NBPTStopCont &sc, bool minimal)
add bidi-edges to connect successive public transport stops
static std::set< NBPTLine * > findBidiCandidates(NBPTLineCont &lc)
identify lines that are likely to require bidirectional tracks
static int addBidiEdgesForStraightConnectivity(NBEdgeCont &ec, bool geometryLike)
add bidi-edges to connect straight tracks
static bool allSharp(const NBNode *node, const EdgeVector &in, const EdgeVector &out, bool countBidiAsSharp=false)
static bool allBroken(const NBNode *node, NBEdge *candOut, const EdgeVector &in, const EdgeVector &out)
static std::set< NBNode * > getBrokenRailNodes(NBEdgeCont &ec, bool verbose=false)
static int addBidiEdgesBetweenSwitches(NBEdgeCont &ec)
add bidi-edges to connect switches that are approached in both directions
static bool allBidi(const EdgeVector &edges)
static int makeAllBidi(NBEdgeCont &ec)
static double getTravelTimeStatic(const Track *const track, const NBVehicle *const veh, double time)
static bool hasRailway(SVCPermissions permissions)
filter for rail edges but do not return (legacy) all purpose edges
static int reverseEdges(NBEdgeCont &ec, NBPTStopCont &sc)
reverse edges sequences that are to broken nodes on both sides
static bool hasStraightPair(const NBNode *node, const EdgeVector &edges, const EdgeVector &edges2)
static int addBidiEdgesForBufferStops(NBEdgeCont &ec)
add bidi-edges to connect buffers stops in both directions
static NBEdge * addBidiEdge(NBEdgeCont &ec, NBEdge *edge, bool update=true)
add bidi-edge for the given edge
static int extendBidiEdges(NBEdgeCont &ec)
add further bidi-edges near existing bidi-edges
static 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:177
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