Eclipse SUMO - Simulation of Urban MObility
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>
26 #include <utils/common/ToString.h>
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"
39 #include "NBAlgorithms_Railway.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 // ---------------------------------------------------------------------------
59 void
61  successors.push_back(track);
62  viaSuccessors.push_back(std::make_pair(track, nullptr));
63  minPermissions &= track->edge->getPermissions();
64 }
65 
66 
67 const 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 
86 const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
87 NBRailwayTopologyAnalyzer::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 // ---------------------------------------------------------------------------
107 void
109  getBrokenRailNodes(ec, true);
110 }
111 
112 
113 int
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 
135 int
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 
179 NBEdge*
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());
193  if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
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 
218 void
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 
234 std::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 
389 std::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 
413 bool
414 NBRailwayTopologyAnalyzer::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 
436 bool
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 
462 bool
463 NBRailwayTopologyAnalyzer::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 
484 bool
485 NBRailwayTopologyAnalyzer::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 
502 bool
504  for (NBEdge* e : edges) {
505  if (!e->isBidiRail()) {
506  return false;
507  }
508  }
509  return true;
510 }
511 
512 
513 int
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 
530 int
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 
571 int
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 sequenes 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 
683 int
685  std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
686  std::set<NBNode*> railNodes = getRailNodes(ec);
687  // find buffer stops and ensure that thay 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 
755 NBEdge*
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 
777 int
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 
837 std::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 
868 int
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 
1079 int
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 outoing 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 
1164 void
1168 }
1169 
1170 
1171 double
1172 NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1173  return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
1174 }
1175 
1176 
1177 void
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 
1303 int
1305  const OptionsCont& oc = OptionsCont::getOptions();
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 
1317 int
1319  int addedSignals = 0;
1320  for (auto& item : sc.getStops()) {
1321  const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
1322  if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
1323  NBNode* to = stopEdge->getToNode();
1324  if (to->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
1326  addedSignals++;
1327  }
1328  NBNode* from = stopEdge->getFromNode();
1329  if (stopEdge->getLoadedLength() >= minLength) {
1331  if (from->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
1333  addedSignals++;
1334  }
1335  } else {
1336  double searchDist = minLength - stopEdge->getLoadedLength();
1337  while (searchDist > 0 && from->geometryLike()) {
1338  for (const NBEdge* in : from->getIncomingEdges()) {
1339  if (in->getFromNode() != stopEdge->getToNode()) {
1340  // found edge that isn't a bidi predecessor
1341  stopEdge = in;
1342  break;
1343  }
1344  }
1345  if (stopEdge->getFromNode() == from) {
1346  // bidi edge without predecessor
1347  break;
1348  } else {
1349  from = stopEdge->getFromNode();
1350  }
1351  searchDist -= stopEdge->getLoadedLength();
1352  }
1353  if (searchDist <= 0 && from->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
1355  addedSignals++;
1356  }
1357  }
1358  }
1359  }
1360  WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
1361  return addedSignals;
1362 }
1363 
1364 
1365 
1366 /****************************************************************************/
#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:35
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission 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
Computes the shortest path through a network using the Dijkstra algorithm.
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
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:178
EdgeVector getAllEdges() const
return all edges
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:281
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.
Definition: NBEdgeCont.cpp:182
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:4308
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition: NBEdge.h:598
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:979
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition: NBEdge.cpp:730
NBEdge * getStraightContinuation(SVCPermissions permissions) const
return the straightest follower edge for the given permissions or nullptr (never returns turn-arounds...
Definition: NBEdge.cpp:4787
static double getTravelTimeStatic(const NBEdge *const edge, const NBVehicle *const, double)
Definition: NBEdge.h:1489
const std::string & getID() const
Definition: NBEdge.h:1522
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
NBEdge * getStraightPredecessor(SVCPermissions permissions) const
return the straightest predecessor edge for the given permissions or nullptr (never returns turn-arou...
Definition: NBEdge.cpp:4802
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition: NBEdge.cpp:973
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:779
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition: NBEdge.cpp:1494
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition: NBEdge.cpp:3937
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2110
void setPriority(int priority)
Sets the priority of the edge.
Definition: NBEdge.h:528
int getPriority() const
Returns the priority of the edge.
Definition: NBEdge.h:523
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:535
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 & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:273
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:268
const Position & getPosition() const
Definition: NBNode.h:260
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3753
const std::map< std::string, NBPTLine * > & getLines() const
Definition: NBPTLineCont.h:43
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
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
const std::string & getRef() const
get line reference (not unique)
Definition: NBPTLine.h:70
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.
Definition: NBPTStopCont.h:44
const std::map< std::string, std::shared_ptr< NBPTStop > > & getStops() const
Returns an unmodifiable reference to the stored pt stops.
Definition: NBPTStopCont.h:62
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 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.
Definition: OptionsCont.cpp:60
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
void close()
Closes the device and removes it from the dictionary.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:254
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.
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