LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms_Railway.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 83.4 % 688 574
Test Date: 2024-12-21 15:45:41 Functions: 93.5 % 31 29

            Line data    Source code
       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              : /****************************************************************************/
      14              : /// @file    NBAlgorithms_Railway.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @author  Melanie Weber
      17              : /// @date    29. March 2018
      18              : ///
      19              : // Algorithms for highway on-/off-ramps computation
      20              : /****************************************************************************/
      21              : #include <config.h>
      22              : 
      23              : #include <cassert>
      24              : #include <utils/options/OptionsCont.h>
      25              : #include <utils/common/MsgHandler.h>
      26              : #include <utils/common/ToString.h>
      27              : #include <utils/common/StringUtils.h>
      28              : #include <utils/iodevices/OutputDevice.h>
      29              : #include <utils/iodevices/OutputDevice_String.h>
      30              : #include <utils/router/DijkstraRouter.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
      60        29220 : NBRailwayTopologyAnalyzer::Track::addSuccessor(Track* track) {
      61        29220 :     successors.push_back(track);
      62        29220 :     viaSuccessors.push_back(std::make_pair(track, nullptr));
      63        29220 :     minPermissions &= track->edge->getPermissions();
      64        29220 : }
      65              : 
      66              : 
      67              : const std::vector<NBRailwayTopologyAnalyzer::Track*>&
      68            0 : NBRailwayTopologyAnalyzer::Track::getSuccessors(SUMOVehicleClass svc) const {
      69            0 :     if ((minPermissions & svc) != 0) {
      70            0 :         return successors;
      71              :     } else {
      72              :         if (svcSuccessors.count(svc) == 0) {
      73              :             std::vector<Track*> succ;
      74            0 :             for (Track* t : successors) {
      75            0 :                 if ((t->edge->getPermissions() & svc) != 0) {
      76            0 :                     succ.push_back(t);
      77              :                 }
      78              :             }
      79            0 :             svcSuccessors[svc] = succ;
      80            0 :         }
      81            0 :         return svcSuccessors[svc];
      82              :     }
      83              : }
      84              : 
      85              : 
      86              : const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
      87        14505 : NBRailwayTopologyAnalyzer::Track::getViaSuccessors(SUMOVehicleClass svc, bool /*ignoreTransientPermissions*/) const {
      88        14505 :     if ((minPermissions & svc) != 0) {
      89        14505 :         return viaSuccessors;
      90              :     } else {
      91              :         if (svcViaSuccessors.count(svc) == 0) {
      92            0 :             std::vector<std::pair<const Track*, const Track*> >& succ = svcViaSuccessors[svc];
      93            0 :             for (const Track* const t : successors) {
      94            0 :                 if ((t->edge->getPermissions() & svc) != 0) {
      95            0 :                     succ.push_back(std::make_pair(t, nullptr));
      96              :                 }
      97              :             }
      98              :         }
      99            0 :         return svcViaSuccessors[svc];
     100              :     }
     101              : }
     102              : 
     103              : 
     104              : // ---------------------------------------------------------------------------
     105              : // NBRailwayTopologyAnalyzer methods
     106              : // ---------------------------------------------------------------------------
     107              : void
     108           29 : NBRailwayTopologyAnalyzer::analyzeTopology(NBEdgeCont& ec) {
     109           29 :     getBrokenRailNodes(ec, true);
     110           29 : }
     111              : 
     112              : 
     113              : int
     114           35 : NBRailwayTopologyAnalyzer::repairTopology(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
     115           35 :     const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
     116              :     int addedBidi = 0;
     117           35 :     if (!minimal) {
     118           32 :         addedBidi += extendBidiEdges(ec);
     119           32 :         addedBidi += reverseEdges(ec, sc); // technically not bidi but new edges nevertheless
     120           32 :         addedBidi += addBidiEdgesForBufferStops(ec);
     121           32 :         addedBidi += addBidiEdgesBetweenSwitches(ec);
     122              :     }
     123           35 :     if (lc.getLines().size() > 0) {
     124           27 :         addedBidi += addBidiEdgesForStops(ec, lc, sc, minimal);
     125              :     }
     126           70 :     if (OptionsCont::getOptions().getBool("railway.topology.repair.connect-straight")) {
     127            3 :         addedBidi += addBidiEdgesForStraightConnectivity(ec, true);
     128            3 :         addedBidi += addBidiEdgesForStraightConnectivity(ec, false);
     129            3 :         addedBidi += extendBidiEdges(ec);
     130              :     }
     131           35 :     return addedBidi;
     132              : }
     133              : 
     134              : 
     135              : int
     136            6 : NBRailwayTopologyAnalyzer::makeAllBidi(NBEdgeCont& ec) {
     137            6 :     int numNotCenterEdges = 0;
     138            6 :     int numAddedBidiEdges = 0;
     139           12 :     std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
     140              :     std::vector<NBEdge*> edges;
     141            6 :     if (inputfile == "") {
     142           46 :         for (NBEdge* edge : ec.getAllEdges()) {
     143           41 :             edges.push_back(edge);
     144            5 :         }
     145              :     } else {
     146              :         std::set<std::string> edgeIDs;
     147            1 :         NBHelpers::loadEdgesFromFile(inputfile, edgeIDs);
     148           26 :         for (const std::string& edgeID : edgeIDs) {
     149           25 :             NBEdge* edge = ec.retrieve(edgeID);
     150           25 :             if (edge != nullptr) {
     151           11 :                 edges.push_back(edge);
     152              :             }
     153              :         }
     154              :     }
     155           58 :     for (NBEdge* edge : edges) {
     156           52 :         if (hasRailway(edge->getPermissions())) {
     157              :             // rebuild connections if given from an earlier network
     158           52 :             edge->invalidateConnections(true);
     159           52 :             if (!edge->isBidiRail()) {
     160           48 :                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     161           48 :                     NBEdge* e2 = addBidiEdge(ec, edge, false);
     162           48 :                     if (e2 != nullptr) {
     163           48 :                         numAddedBidiEdges++;
     164              :                     }
     165              :                 } else {
     166            0 :                     numNotCenterEdges++;
     167              :                 }
     168              :             }
     169              :         }
     170              :     }
     171           12 :     WRITE_MESSAGEF(TL("Added % bidi-edges to ensure that all tracks are usable in both directions."), toString(numAddedBidiEdges));
     172            6 :     if (numNotCenterEdges) {
     173            0 :         WRITE_WARNINGF(TL("Ignore % edges because they have the wrong spreadType"), toString(numNotCenterEdges));
     174              :     }
     175            6 :     return numAddedBidiEdges;
     176            6 : }
     177              : 
     178              : 
     179              : NBEdge*
     180         1064 : NBRailwayTopologyAnalyzer::addBidiEdge(NBEdgeCont& ec, NBEdge* edge, bool update) {
     181              :     assert(edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER);
     182              :     assert(!edge->isBidiRail());
     183         1064 :     const std::string id2 = (edge->getID()[0] == '-'
     184         1064 :                              ? edge->getID().substr(1)
     185         1064 :                              : "-" + edge->getID());
     186         1064 :     if (ec.wasIgnored(id2)) {
     187              :         // we had it before so the warning is already there
     188              :         return nullptr;
     189              :     }
     190         1061 :     if (ec.retrieve(id2) == nullptr) {
     191              :         NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
     192         1061 :                                 edge, edge->getGeometry().reverse());
     193         2122 :         if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     194          714 :             e2->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     195              :         }
     196         1061 :         ec.insert(e2);
     197         1061 :         if (ec.retrieve(id2) == nullptr) {
     198            3 :             WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
     199            1 :             return nullptr;
     200              :         }
     201         1060 :         if (update) {
     202         1012 :             updateTurns(edge);
     203              :             // reconnected added edges
     204         2917 :             for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
     205         1905 :                 if (hasRailway(incoming->getPermissions())) {
     206         1735 :                     incoming->invalidateConnections(true);
     207              :                 }
     208              :             }
     209              :         }
     210         1060 :         return e2;
     211              :     } else {
     212            0 :         WRITE_WARNINGF(TL("Could not add bidi-edge '%'."), id2);
     213            0 :         return nullptr;
     214              :     }
     215              : }
     216              : 
     217              : 
     218              : void
     219        19181 : NBRailwayTopologyAnalyzer::getRailEdges(const NBNode* node,
     220              :                                         EdgeVector& inEdges, EdgeVector& outEdges) {
     221        52429 :     for (NBEdge* e : node->getIncomingEdges()) {
     222        33248 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     223        31359 :             inEdges.push_back(e);
     224              :         }
     225              :     }
     226        52120 :     for (NBEdge* e : node->getOutgoingEdges()) {
     227        32939 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     228        31053 :             outEdges.push_back(e);
     229              :         }
     230              :     }
     231        19181 : }
     232              : 
     233              : 
     234              : std::set<NBNode*>
     235          131 : NBRailwayTopologyAnalyzer::getBrokenRailNodes(NBEdgeCont& ec, bool verbose) {
     236              :     std::set<NBNode*> brokenNodes;
     237          131 :     OutputDevice& device = OutputDevice::getDevice(verbose
     238          291 :                            ? OptionsCont::getOptions().getString("railway.topology.output")
     239              :                            : "/dev/null");
     240              : 
     241          262 :     device.writeXMLHeader("railwayTopology", "");
     242          131 :     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         6057 :     for (NBNode* node : railNodes) {
     247              :         EdgeVector inEdges, outEdges;
     248         5926 :         getRailEdges(node, inEdges, outEdges);
     249        11852 :         types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
     250        15135 :         for (NBEdge* e : outEdges) {
     251        11417 :             if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
     252         4536 :                 NBEdge* primary = e;
     253         4536 :                 NBEdge* secondary = e->getTurnDestination(true);
     254         4536 :                 if (e->getID()[0] == '-') {
     255              :                     std::swap(primary, secondary);
     256         3378 :                 } 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         5926 :     }
     266              : 
     267          131 :     int numBrokenA = 0;
     268          131 :     int numBrokenB = 0;
     269          131 :     int numBrokenC = 0;
     270          131 :     int numBrokenD = 0;
     271          131 :     int numBufferStops = 0;
     272          131 :     if (verbose && types.size() > 0) {
     273           58 :         WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
     274              :     }
     275          131 :     device.openTag("legend");
     276          262 :     device.openTag("error");
     277              :     device.writeAttr(SUMO_ATTR_ID, "a");
     278          131 :     device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
     279          131 :     device.closeTag();
     280          262 :     device.openTag("error");
     281              :     device.writeAttr(SUMO_ATTR_ID, "b");
     282          131 :     device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
     283          131 :     device.closeTag();
     284          262 :     device.openTag("error");
     285              :     device.writeAttr(SUMO_ATTR_ID, "c");
     286          131 :     device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
     287          131 :     device.closeTag();
     288          262 :     device.openTag("error");
     289              :     device.writeAttr(SUMO_ATTR_ID, "d");
     290          131 :     device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
     291          131 :     device.closeTag();
     292          262 :     device.closeTag();
     293              : 
     294          725 :     for (auto it : types) {
     295          594 :         int numBrokenType = 0;
     296          594 :         device.openTag("railNodeType");
     297          594 :         int in = it.first.first;
     298          594 :         int out = it.first.second;
     299          594 :         device.writeAttr("in", in);
     300         1188 :         device.writeAttr("out", out);
     301         6520 :         for (NBNode* n : it.second) {
     302         5926 :             device.openTag(SUMO_TAG_NODE);
     303              :             device.writeAttr(SUMO_ATTR_ID, n->getID());
     304              :             EdgeVector inRail, outRail;
     305         5926 :             getRailEdges(n, inRail, outRail);
     306              :             // check if there is a mismatch between angle and edge direction
     307              :             // (see above)
     308              : 
     309         5926 :             std::string broken = "";
     310         5926 :             if (in < 2 && hasStraightPair(n, outRail, outRail)) {
     311              :                 broken += "a";
     312           93 :                 numBrokenA++;
     313              :             }
     314         5926 :             if (out < 2 && hasStraightPair(n, inRail, inRail)) {
     315              :                 broken += "b";
     316          136 :                 numBrokenB++;
     317              :             }
     318         5926 :             if (out > 0) {
     319        14334 :                 for (NBEdge* e : inRail) {
     320              :                     EdgeVector tmp;
     321         8804 :                     tmp.push_back(e);
     322         8804 :                     if (allSharp(n, tmp, outRail)) {
     323              :                         broken += "c";
     324           81 :                         numBrokenC++;
     325              :                         break;
     326              :                     }
     327         8804 :                 }
     328              :             }
     329         5926 :             if (in > 0) {
     330        14295 :                 for (NBEdge* e : outRail) {
     331              :                     EdgeVector tmp;
     332         8784 :                     tmp.push_back(e);
     333         8784 :                     if (allSharp(n, inRail, tmp)) {
     334              :                         broken += "d";
     335           62 :                         numBrokenD++;
     336              :                         break;
     337              :                     }
     338         8784 :                 }
     339              :             }
     340              :             // do not mark bidi nodes as broken
     341         5926 :             if (((in == 1 && out == 1) || (in == 2 && out == 2))
     342        10800 :                     && allBidi(inRail) && allBidi(outRail)) {
     343              :                 broken = "";
     344              :             }
     345              : 
     346         5926 :             if (broken.size() > 0) {
     347          462 :                 device.writeAttr("broken", broken);
     348              :                 brokenNodes.insert(n);
     349          231 :                 numBrokenType++;
     350              :             }
     351        11852 :             if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
     352           39 :                 device.writeAttr("buffer_stop", "true");
     353           39 :                 numBufferStops++;
     354              :             }
     355        11852 :             device.closeTag();
     356         5926 :         }
     357          594 :         device.closeTag();
     358          594 :         if (verbose) {
     359          625 :             WRITE_MESSAGE("   " + toString(it.first.first) + "," + toString(it.first.second)
     360              :                           + " count: " + toString(it.second.size()) + " broken: " + toString(numBrokenType));
     361              :         }
     362              : 
     363              :     }
     364          131 :     if (verbose) {
     365          203 :         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           58 :         WRITE_MESSAGEF(TL("Found % railway nodes marked as buffer_stop"), toString(numBufferStops));
     372              :     }
     373              : 
     374         3503 :     for (NBEdge* e : bidiEdges) {
     375         3372 :         device.openTag("bidiEdge");
     376         3372 :         device.writeAttr(SUMO_ATTR_ID, e->getID());
     377         3372 :         device.writeAttr("bidi", e->getTurnDestination(true)->getID());
     378         6744 :         device.closeTag();
     379              :     }
     380          131 :     if (verbose) {
     381           58 :         WRITE_MESSAGEF(TL("Found % bidirectional rail edges"), toString(bidiEdges.size()));
     382              :     }
     383              : 
     384          131 :     device.close();
     385          131 :     return brokenNodes;
     386              : }
     387              : 
     388              : 
     389              : std::set<NBNode*>
     390          190 : NBRailwayTopologyAnalyzer::getRailNodes(NBEdgeCont& ec, bool verbose) {
     391              :     std::set<NBNode*> railNodes;
     392          190 :     int numRailEdges = 0;
     393        43764 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     394        43574 :         if (hasRailway(it->second->getPermissions())) {
     395        15411 :             numRailEdges++;
     396        15411 :             railNodes.insert(it->second->getFromNode());
     397        15411 :             railNodes.insert(it->second->getToNode());
     398              :         }
     399              :     }
     400          190 :     int numRailSignals = 0;
     401        10106 :     for (const NBNode* const node : railNodes) {
     402         9916 :         if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     403         2512 :             numRailSignals++;
     404              :         }
     405              :     }
     406          190 :     if (verbose) {
     407           58 :         WRITE_MESSAGEF(TL("Found % railway edges and % railway nodes (% signals)."), toString(numRailEdges), toString(railNodes.size()), toString(numRailSignals));
     408              :     }
     409          190 :     return railNodes;
     410              : }
     411              : 
     412              : 
     413              : bool
     414        43710 : NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
     415        43710 :     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        34552 :     if ((e1->getToNode() == node && e2->getFromNode() == node)
     426        46523 :             || (e1->getFromNode() == node && e2->getToNode() == node)) {
     427              :         // edges go in the same direction
     428        38087 :         return fabs(relAngle) < SHARP_THRESHOLD;
     429              :     } else {
     430              :         // edges go in the opposite direction (both incoming or outgoing)
     431         5623 :         return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
     432              :     }
     433              : }
     434              : 
     435              : 
     436              : bool
     437         4743 : NBRailwayTopologyAnalyzer::hasStraightPair(const NBNode* node, const EdgeVector& edges,
     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         8848 :     for (NBEdge* e1 : edges) {
     445         9069 :         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         4964 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     454              :                 return true;
     455              :             }
     456              :         }
     457              :     }
     458              :     return false;
     459              : }
     460              : 
     461              : 
     462              : bool
     463          100 : NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
     464          133 :     for (NBEdge* e : in) {
     465           83 :         if (e != candOut && isStraight(node, e, candOut)) {
     466           50 :             if (gDebugFlag1) {
     467            0 :                 std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
     468              :             }
     469              :             return false;
     470              :         }
     471              :     }
     472          144 :     for (NBEdge* e : out) {
     473          100 :         if (e != candOut && !isStraight(node, e, candOut)) {
     474            6 :             if (gDebugFlag1) {
     475            0 :                 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        18624 : NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
     486              :     bool allBidi = true;
     487        23642 :     for (NBEdge* e1 : in) {
     488        30550 :         for (NBEdge* e2 : out) {
     489        25532 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     490              :                 return false;
     491              :             }
     492         8447 :             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         1539 :     return !allBidi || countBidiAsSharp;
     499              : }
     500              : 
     501              : 
     502              : bool
     503         8221 : NBRailwayTopologyAnalyzer::allBidi(const EdgeVector& edges) {
     504        21283 :     for (NBEdge* e : edges) {
     505        14589 :         if (!e->isBidiRail()) {
     506              :             return false;
     507              :         }
     508              :     }
     509              :     return true;
     510              : }
     511              : 
     512              : 
     513              : int
     514           35 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec) {
     515           35 :     int added = 0;
     516         8290 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     517         8255 :         NBEdge* e = it->second;
     518         8255 :         if (e->isBidiRail()) {
     519         1853 :             added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
     520         1853 :             added += extendBidiEdges(ec, e->getToNode(), e);
     521              :         }
     522              :     }
     523           35 :     if (added > 0) {
     524           24 :         WRITE_MESSAGEF(TL("Added % bidi-edges as extension of existing bidi edges."), toString(added));
     525              :     }
     526           35 :     return added;
     527              : }
     528              : 
     529              : 
     530              : int
     531         4714 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec, NBNode* node, NBEdge* bidiIn) {
     532              :     assert(bidiIn->getToNode() == node);
     533         4714 :     NBEdge* bidiOut = bidiIn->getTurnDestination(true);
     534         4714 :     if (bidiOut == nullptr) {
     535            0 :         WRITE_WARNINGF(TL("Could not find bidi-edge for edge '%'"), bidiIn->getID());
     536            0 :         return 0;
     537              :     }
     538              :     EdgeVector tmpBidiOut;
     539         4714 :     tmpBidiOut.push_back(bidiOut);
     540              :     EdgeVector tmpBidiIn;
     541         4714 :     tmpBidiIn.push_back(bidiIn);
     542              :     int added = 0;
     543              :     EdgeVector inRail, outRail;
     544         4714 :     getRailEdges(node, inRail, outRail);
     545        13531 :     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         9254 :         if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
     548          404 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     549         9213 :                 && allSharp(node, inRail, tmpBidiOut, true)) {
     550          362 :             NBEdge* e2 = addBidiEdge(ec, cand);
     551          362 :             if (e2 != nullptr) {
     552          362 :                 added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
     553              :             }
     554              :         }
     555              :     }
     556        13768 :     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         9728 :         if (!cand->isBidiRail() && isStraight(node, cand, bidiOut)
     559          649 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     560         9694 :                 && allSharp(node, outRail, tmpBidiIn, true)) {
     561          602 :             NBEdge* e2 = addBidiEdge(ec, cand);
     562          602 :             if (e2 != nullptr) {
     563          600 :                 added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
     564              :             }
     565              :         }
     566              :     }
     567              :     return added;
     568         4714 : }
     569              : 
     570              : 
     571              : int
     572           32 : NBRailwayTopologyAnalyzer::reverseEdges(NBEdgeCont& ec, NBPTStopCont& sc) {
     573           32 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     574              :     // find reversible edge sequences between broken nodes
     575              :     std::vector<EdgeVector> seqsToReverse;
     576          103 :     for (NBNode* n : brokenNodes) {
     577              :         EdgeVector inRail, outRail;
     578           71 :         getRailEdges(n, inRail, outRail);
     579          160 :         for (NBEdge* start : outRail) {
     580              :             EdgeVector tmp;
     581           89 :             tmp.push_back(start);
     582              :             // only reverse edges where the node would be unbroken afterwards
     583           89 :             if (!allBroken(n, start, inRail, outRail)
     584           89 :                     || (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          128 :             while (forward) {
     596           92 :                 seq.push_back(start);
     597              :                 //std::cout << " seq=" << toString(seq) << "\n";
     598           92 :                 NBNode* n2 = start->getToNode();
     599              :                 EdgeVector inRail2, outRail2;
     600           92 :                 getRailEdges(n2, inRail2, outRail2);
     601              :                 if (brokenNodes.count(n2) != 0) {
     602              :                     EdgeVector tmp2;
     603           11 :                     tmp2.push_back(start);
     604           11 :                     if (allBroken(n2, start, outRail2, inRail2)) {
     605            8 :                         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           11 :                 } else {
     615           81 :                     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           64 :                     } 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           56 :                         start = outRail2.front();
     633              :                     }
     634              :                 }
     635           92 :             }
     636           89 :         }
     637           71 :     }
     638              :     // sort by sequence length
     639           32 :     if (seqsToReverse.size() > 0) {
     640            8 :         WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
     641              :     }
     642           32 :     std::sort(seqsToReverse.begin(), seqsToReverse.end(),
     643              :     [](const EdgeVector & a, const EdgeVector & b) {
     644              :         return a.size() < b.size();
     645              :     });
     646           32 :     int numReversed = 0;
     647              :     std::set<NBNode*> affectedEndpoints;
     648              :     std::set<std::string> reversedIDs;
     649              :     std::map<int, int> seqLengths;
     650           40 :     for (EdgeVector& seq : seqsToReverse) {
     651            8 :         NBNode* seqStart = seq.front()->getFromNode();
     652            8 :         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           35 :             for (NBEdge* e : seq) {
     660           27 :                 e->reinitNodes(e->getToNode(), e->getFromNode());
     661           27 :                 e->setGeometry(e->getGeometry().reverse());
     662           27 :                 reversedIDs.insert(e->getID());
     663           54 :                 if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     664            0 :                     e->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     665              :                 }
     666              :             }
     667            8 :             seqLengths[(int)seq.size()]++;
     668            8 :             numReversed++;
     669              :         }
     670              :     }
     671           32 :     if (numReversed > 0) {
     672            8 :         WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
     673          137 :         for (auto& item : sc.getStops()) {
     674          133 :             if (reversedIDs.count(item.second->getEdgeId())) {
     675            2 :                 item.second->findLaneAndComputeBusStopExtent(ec);
     676              :             }
     677              :         }
     678              :     }
     679           32 :     return numReversed;
     680           32 : }
     681              : 
     682              : 
     683              : int
     684           32 : NBRailwayTopologyAnalyzer::addBidiEdgesForBufferStops(NBEdgeCont& ec) {
     685           32 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     686           32 :     std::set<NBNode*> railNodes = getRailNodes(ec);
     687              :     // find buffer stops and ensure that they are connect to the network in both directions
     688           32 :     int numBufferStops = 0;
     689           32 :     int numAddedBidiTotal = 0;
     690         1850 :     for (NBNode* node : railNodes) {
     691         3636 :         if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
     692           10 :             if (node->getEdges().size() != 1) {
     693            3 :                 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
     694            1 :                 continue;
     695              :             }
     696              :             // int numAddedBidi = 0;
     697            9 :             numBufferStops++;
     698              :             NBEdge* prev = nullptr;
     699              :             NBEdge* prev2 = nullptr;
     700              :             EdgeVector inRail, outRail;
     701            9 :             getRailEdges(node, inRail, outRail);
     702              :             bool addAway = true; // add new edges away from buffer stop
     703           22 :             while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
     704              :                 NBEdge* e = nullptr;
     705           13 :                 if (prev == nullptr) {
     706              :                     assert(node->getEdges().size() == 1);
     707            9 :                     e = node->getEdges().front();
     708            9 :                     addAway = node == e->getToNode();
     709              :                 } else {
     710            4 :                     if (addAway) {
     711              :                         // XXX if node is broken we need to switch direction
     712              :                         assert(inRail.size() == 2);
     713            3 :                         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            1 :                         e = outRail.front() == prev2 ? outRail.back() : outRail.front();
     718              :                     }
     719              :                 }
     720           13 :                 e->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
     721              :                 NBNode* e2From = nullptr;
     722              :                 NBNode* e2To = nullptr;
     723           13 :                 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           13 :                 NBEdge* e2 = addBidiEdge(ec, e);
     733           13 :                 if (e2 == nullptr) {
     734              :                     break;
     735              :                 }
     736              :                 prev = e;
     737              :                 prev2 = e2;
     738              :                 // numAddedBidi++;
     739           13 :                 numAddedBidiTotal++;
     740              :                 inRail.clear();
     741              :                 outRail.clear();
     742           13 :                 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            9 :         }
     748              :     }
     749           32 :     if (numAddedBidiTotal > 0) {
     750           10 :         WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
     751              :     }
     752           32 :     return numAddedBidiTotal;
     753              : }
     754              : 
     755              : NBEdge*
     756           90 : NBRailwayTopologyAnalyzer::isBidiSwitch(const NBNode* n) {
     757              :     EdgeVector inRail, outRail;
     758           90 :     getRailEdges(n, inRail, outRail);
     759           90 :     if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
     760           25 :         if (isStraight(n, inRail.front(), outRail.front())) {
     761           20 :             return inRail.front();
     762            5 :         } else if (isStraight(n, inRail.back(), outRail.front())) {
     763            5 :             return inRail.back();
     764              :         }
     765              :     }
     766           65 :     if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
     767           16 :         if (isStraight(n, outRail.front(), inRail.front())) {
     768           10 :             return outRail.front();
     769            6 :         } else if (isStraight(n, outRail.back(), inRail.front())) {
     770            6 :             return outRail.back();
     771              :         }
     772              :     }
     773              :     return nullptr;
     774           90 : }
     775              : 
     776              : 
     777              : int
     778           32 : NBRailwayTopologyAnalyzer::addBidiEdgesBetweenSwitches(NBEdgeCont& ec) {
     779           32 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     780              :     std::map<int, int> seqLengths;
     781           32 :     int numAdded = 0;
     782           32 :     int numSeqs = 0;
     783           88 :     for (NBNode* n : brokenNodes) {
     784           56 :         NBEdge* edge = isBidiSwitch(n);
     785           56 :         if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     786              :             std::vector<NBNode*> nodeSeq;
     787              :             EdgeVector edgeSeq;
     788           34 :             NBNode* prev = n;
     789           34 :             nodeSeq.push_back(prev);
     790           34 :             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          110 :             while (forward) {
     795           76 :                 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
     796              :                 EdgeVector allRail;
     797           76 :                 getRailEdges(next, allRail, allRail);
     798           76 :                 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
     799           42 :                     prev = next;
     800           42 :                     edge = allRail.front() == edge ? allRail.back() : allRail.front();
     801           42 :                     nodeSeq.push_back(prev);
     802           42 :                     edgeSeq.push_back(edge);
     803              :                 } else {
     804              :                     forward = false;
     805              :                     EdgeVector inRail2, outRail2;
     806           34 :                     getRailEdges(next, inRail2, outRail2);
     807           34 :                     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           10 :                         for (NBEdge* e : edgeSeq) {
     812            6 :                             addBidiEdge(ec, e);
     813              :                         }
     814            4 :                         seqLengths[(int)edgeSeq.size()]++;
     815            4 :                         numSeqs++;
     816            4 :                         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           34 :                 }
     826           76 :             }
     827              : 
     828           34 :         }
     829              :     }
     830           32 :     if (seqLengths.size() > 0) {
     831            4 :         WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
     832              :     }
     833           64 :     return numAdded;
     834              : }
     835              : 
     836              : 
     837              : std::set<NBPTLine*>
     838           27 : NBRailwayTopologyAnalyzer::findBidiCandidates(NBPTLineCont& lc) {
     839              :     std::set<NBPTLine*>  result;
     840              :     std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
     841          179 :     for (const auto& item : lc.getLines()) {
     842          152 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     843          152 :         if (stops.size() > 1) {
     844          613 :             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          482 :                 visited.insert({fromStop, toStop});
     848              :             }
     849              :         }
     850              :     }
     851          179 :     for (const auto& item : lc.getLines()) {
     852          152 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     853          152 :         if (stops.size() > 1) {
     854          565 :             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            8 :                     result.insert(item.second);
     860              :                     break;
     861              :                 }
     862          442 :             }
     863              :         }
     864              :     }
     865           27 :     return result;
     866              : }
     867              : 
     868              : int
     869           27 : NBRailwayTopologyAnalyzer::addBidiEdgesForStops(NBEdgeCont& ec, NBPTLineCont& lc, NBPTStopCont& sc, bool minimal) {
     870           54 :     const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
     871              :     // generate bidirectional routing graph
     872              :     std::vector<Track*> tracks;
     873         9007 :     for (NBEdge* edge : ec.getAllEdges()) {
     874        17960 :         tracks.push_back(new Track(edge));
     875           27 :     }
     876           27 :     const int numEdges = (int)tracks.size();
     877         9007 :     for (NBEdge* edge : ec.getAllEdges()) {
     878        17960 :         tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
     879           27 :     }
     880              :     // add special tracks for starting end ending in both directions
     881              :     std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
     882         9007 :     for (NBEdge* edge : ec.getAllEdges()) {
     883         8980 :         if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     884         3331 :             Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
     885         3331 :             tracks.push_back(start);
     886         3331 :             Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
     887         3331 :             tracks.push_back(end);
     888         3331 :             stopTracks[edge] = {start, end};
     889              :         }
     890           27 :     }
     891              :     // set successors based on angle (connections are not yet built)
     892         2199 :     for (NBNode* node : getRailNodes(ec)) {
     893              :         EdgeVector railEdges;
     894         2172 :         getRailEdges(node, railEdges, railEdges);
     895         8833 :         for (NBEdge* e1 : railEdges) {
     896        29452 :             for (NBEdge* e2 : railEdges) {
     897        22791 :                 if (e1 != e2 && isStraight(node, e1, e2)) {
     898        11208 :                     int i = e1->getNumericalID();
     899        11208 :                     int i2 = e2->getNumericalID();
     900        11208 :                     if (e1->getToNode() == node) {
     901         5614 :                         if (e2->getFromNode() == node) {
     902              :                             // case 1) plain forward connection
     903         3260 :                             tracks[i]->addSuccessor(tracks[i2]);
     904              :                             // reverse edge (numerical id incremented by numEdges)
     905         3260 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
     906              :                         } else {
     907              :                             // case 2) both edges pointing towards each other
     908         2354 :                             tracks[i]->addSuccessor(tracks[i2 + numEdges]);
     909         2354 :                             tracks[i2]->addSuccessor(tracks[i + numEdges]);
     910              :                         }
     911              :                     } else {
     912         5594 :                         if (e2->getFromNode() == node) {
     913              :                             // case 3) both edges pointing away from each other
     914         2334 :                             tracks[i + numEdges]->addSuccessor(tracks[i2]);
     915         2334 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i]);
     916              :                         } else {
     917              :                             // already handled via case 1)
     918              :                         }
     919              :                     }
     920              : 
     921              :                 }
     922              :             }
     923              :         }
     924         2172 :     }
     925              :     // define start and end successors
     926         3358 :     for (auto& item : stopTracks) {
     927         3331 :         const int index = item.first->getNumericalID();
     928              :         // start
     929         3331 :         item.second.first->addSuccessor(tracks[index]);
     930         3331 :         item.second.first->addSuccessor(tracks[index + numEdges]);
     931              :         // end
     932         3331 :         tracks[index]->addSuccessor(item.second.second);
     933         3331 :         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              : 
     945              :     SUMOAbstractRouter<Track, NBVehicle>* const router = new DijkstraRouter<Track, NBVehicle>(
     946           27 :         tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
     947              : 
     948           27 :     int added = 0;
     949           27 :     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           27 :     std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
     958              : 
     959          179 :     for (const auto& item : lc.getLines()) {
     960          152 :         NBPTLine* line = item.second;
     961          152 :         std::vector<std::pair<NBEdge*, std::string> > stops = line->getStopEdges(ec);
     962              :         std::vector<NBEdge*> stopEdges;
     963          785 :         for (auto it : stops) {
     964          633 :             stopEdges.push_back(it.first);
     965              :         }
     966          152 :         NBEdge* routeStart = line->getRouteStart(ec);
     967          152 :         NBEdge* routeEnd = line->getRouteEnd(ec);
     968          152 :         if (routeStart != nullptr) {
     969          262 :             stops.insert(stops.begin(), {routeStart, routeStart->getID()});
     970              :         }
     971          152 :         if (routeEnd != nullptr) {
     972          258 :             stops.push_back({routeEnd, routeEnd->getID()});
     973              :         }
     974          152 :         if (stops.size() < 2) {
     975            7 :             continue;
     976              :         }
     977          290 :         if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
     978           21 :             WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
     979            7 :             continue;
     980              :         }
     981          847 :         for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     982          709 :             NBEdge* fromEdge = it->first;
     983          709 :             NBEdge* toEdge = (it + 1)->first;
     984              :             const std::string fromStop = it->second;
     985              :             const std::string toStop = (it + 1)->second;
     986          709 :             std::pair<std::string, std::string> trip(fromStop, toStop);
     987          709 :             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          255 :             if (visited.count(trip) != 0) {
     990          255 :                 continue;
     991              :             } else {
     992              :                 visited.insert(trip);
     993              :             }
     994          114 :             if (stopTracks.count(fromEdge) == 0
     995              :                     || stopTracks.count(toEdge) == 0) {
     996          114 :                 continue;
     997              :             }
     998          340 :             const bool needBidi = visited.count(reverseTrip) != 0;
     999          340 :             NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
    1000              :             std::vector<const Track*> route;
    1001          340 :             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          340 :             if (route.size() > 0) {
    1006              :                 assert(route.size() > 2);
    1007         2645 :                 for (int i = 1; i < (int)route.size() - 1; ++i) {
    1008         2305 :                     if (route[i]->getNumericalID() >= numEdges || needBidi) {
    1009           91 :                         NBEdge* edge = route[i]->edge;
    1010              :                         if (addBidiEdges.count(edge) == 0) {
    1011           89 :                             bool isStop = i == 1 || i == (int)route.size() - 2;
    1012           89 :                             if (!edge->isBidiRail(true)) {
    1013           57 :                                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
    1014              :                                     addBidiEdges.insert(edge);
    1015           57 :                                     if (isStop) {
    1016              :                                         addBidiStops.insert(edge);
    1017              :                                     }
    1018              :                                 } else {
    1019            0 :                                     if (isStop) {
    1020            0 :                                         WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
    1021              :                                     }
    1022              :                                 }
    1023           32 :                             } else if (isStop && needBidi) {
    1024           46 :                                 std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
    1025           23 :                                 if (fs) {
    1026           42 :                                     std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(fs, ec);
    1027           21 :                                     if (fromReverse) {
    1028           42 :                                         sc.insert(fromReverse);
    1029           21 :                                         fs->setBidiStop(fromReverse);
    1030              :                                     }
    1031              :                                 }
    1032           46 :                                 std::shared_ptr<NBPTStop> ts = sc.get(toStop);
    1033           23 :                                 if (ts) {
    1034           36 :                                     std::shared_ptr<NBPTStop> toReverse = sc.getReverseStop(ts, ec);
    1035           18 :                                     if (toReverse) {
    1036           36 :                                         sc.insert(toReverse);
    1037           18 :                                         ts->setBidiStop(toReverse);
    1038              :                                     }
    1039              :                                 }
    1040              :                             }
    1041              :                         }
    1042              :                     }
    1043              :                 }
    1044              :             } else {
    1045            0 :                 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
    1046            0 :                 numDisconnected++;
    1047              :             }
    1048          340 :         }
    1049          152 :     }
    1050           84 :     for (NBEdge* edge : addBidiEdges) {
    1051           57 :         if (!edge->isBidiRail()) {
    1052           27 :             NBEdge* e2 = addBidiEdge(ec, edge);
    1053              :             //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
    1054           27 :             if (e2 != nullptr) {
    1055           27 :                 added++;
    1056           27 :                 if (!minimal) {
    1057           17 :                     added += extendBidiEdges(ec, edge->getToNode(), edge);
    1058           17 :                     added += extendBidiEdges(ec, edge->getFromNode(), e2);
    1059              :                 }
    1060              :             }
    1061              :         }
    1062              :     }
    1063              : 
    1064           27 :     if (addBidiEdges.size() > 0 || numDisconnected > 0) {
    1065           80 :         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        24649 :     for (Track* t : tracks) {
    1072        24622 :         delete t;
    1073              :     }
    1074           27 :     delete router;
    1075           54 :     return (int)addBidiEdges.size();
    1076           27 : }
    1077              : 
    1078              : 
    1079              : int
    1080            6 : NBRailwayTopologyAnalyzer::addBidiEdgesForStraightConnectivity(NBEdgeCont& ec, bool geometryLike) {
    1081            6 :     int added = 0;
    1082            6 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
    1083          107 :     for (const auto& e : ec) {
    1084          101 :         if (!hasRailway(e.second->getPermissions())) {
    1085           72 :             continue;
    1086              :         }
    1087          101 :         NBNode* const from = e.second->getFromNode();
    1088              :         NBNode* const to = e.second->getToNode();
    1089           59 :         if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
    1090           59 :             continue;
    1091              :         }
    1092           42 :         if (e.second->isBidiRail()) {
    1093           13 :             continue;
    1094              :         }
    1095              :         EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
    1096           29 :         getRailEdges(from, inRailFrom, outRailFrom);
    1097           29 :         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           29 :         if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
    1103           25 :             for (const NBEdge* fromStraightCand : outRailFrom) {
    1104           17 :                 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           12 :             if (haveStraightReverse) {
    1111            5 :                 for (const NBEdge* fromStraightCand : inRailFrom) {
    1112            4 :                     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           29 :         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           26 :             for (const NBEdge* toStraightCand : inRailTo) {
    1126           22 :                 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           13 :             if (haveStraightReverse) {
    1133           13 :                 for (const NBEdge* toStraightCand : outRailTo) {
    1134            8 :                     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           29 :         if (haveStraightReverse && !haveStraight) {
    1144            6 :             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            6 :             if (e2 != nullptr) {
    1147            6 :                 added++;
    1148            6 :                 added += extendBidiEdges(ec, to, e.second);
    1149            6 :                 added += extendBidiEdges(ec, from, e2);
    1150              :             }
    1151              :         }
    1152           29 :     }
    1153            6 :     if (added > 0) {
    1154            4 :         if (geometryLike) {
    1155            4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
    1156              :         } else {
    1157            4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
    1158              :         }
    1159              :     }
    1160            6 :     return added;
    1161              : }
    1162              : 
    1163              : 
    1164              : void
    1165         1012 : NBRailwayTopologyAnalyzer::updateTurns(NBEdge* edge) {
    1166         1012 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getFromNode(), false);
    1167         1012 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getToNode(), false);
    1168         1012 : }
    1169              : 
    1170              : 
    1171              : double
    1172        14505 : NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
    1173        14505 :     return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
    1174              : }
    1175              : 
    1176              : 
    1177              : void
    1178            4 : NBRailwayTopologyAnalyzer::extendDirectionPriority(NBEdgeCont& ec, bool fromUniDir) {
    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          174 :     for (NBEdge* edge : ec.getAllEdges()) {
    1197          170 :         if (hasRailway(edge->getPermissions())) {
    1198          170 :             if (fromUniDir) {
    1199           46 :                 if (!edge->isBidiRail()) {
    1200              :                     edge->setPriority(4);
    1201              :                     uni.insert(edge);
    1202              :                 } else {
    1203              :                     bidi.insert(edge);
    1204              :                 }
    1205              :             } else {
    1206          124 :                 if (edge->getPriority() >= 0) {
    1207              :                     uni.insert(edge);
    1208              :                 } else {
    1209              :                     bidi.insert(edge);
    1210              :                 }
    1211              :             }
    1212              :         }
    1213            4 :     }
    1214              : 
    1215            4 :     if (uni.size() == 0) {
    1216            0 :         if (bidi.size() != 0) {
    1217            0 :             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          120 :     while (!check.empty()) {
    1225          116 :         NBEdge* edge = *check.begin();
    1226              :         check.erase(edge);
    1227           48 :         if (seen.count(edge) != 0) {
    1228           48 :             continue;
    1229              :         }
    1230              :         seen.insert(edge);
    1231           68 :         NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
    1232           68 :         if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
    1233              :             forward.insert(straightOut);
    1234              :             check.insert(straightOut);
    1235              :         }
    1236           68 :         NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
    1237           68 :         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          130 :     for (NBEdge* edge : bidi) {
    1250          126 :         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          126 :         if (bidiEdge == nullptr) {
    1273            6 :             WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
    1274              :         }
    1275          126 :         if (edge->getPriority() >= 0) {
    1276              :             bidiPrio = 0;
    1277              :         }
    1278          126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
    1279              :             prio = 0;
    1280              :         }
    1281          126 :         if (edge->getPriority() < 0) {
    1282              :             edge->setPriority(prio);
    1283              :         }
    1284          126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
    1285              :             bidiEdge->setPriority(bidiPrio);
    1286              :         }
    1287              :     }
    1288              :     std::map<int, int> numPrios;
    1289          130 :     for (NBEdge* edge : bidi) {
    1290          126 :         numPrios[edge->getPriority()]++;
    1291              :     }
    1292            4 :     if (fromUniDir) {
    1293            3 :         WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1294              :     } else {
    1295            9 :         WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1296              :     }
    1297              : }
    1298              : 
    1299              : // ---------------------------------------------------------------------------
    1300              : // NBRailwaySignalGuesser methods
    1301              : // ---------------------------------------------------------------------------
    1302              : 
    1303              : int
    1304         1704 : NBRailwaySignalGuesser::guessRailSignals(NBEdgeCont& ec, NBPTStopCont& sc) {
    1305         1704 :     const OptionsCont& oc = OptionsCont::getOptions();
    1306              :     int addedSignals = 0;
    1307         3408 :     if (oc.exists("railway.signal.guess.by-stops")) {
    1308         3232 :         if (oc.getBool("railway.signal.guess.by-stops")) {
    1309            1 :             const double minLength = oc.getFloat("osm.stop-output.length.train");
    1310            1 :             addedSignals += guessByStops(ec, sc, minLength);
    1311              :         }
    1312              :     }
    1313         1704 :     return addedSignals;
    1314              : }
    1315              : 
    1316              : 
    1317              : bool
    1318            6 : NBRailwaySignalGuesser::canBeSignal(const NBNode* node) {
    1319            6 :     return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
    1320              : }
    1321              : 
    1322              : int
    1323            1 : NBRailwaySignalGuesser::guessByStops(NBEdgeCont& ec, NBPTStopCont& sc, double minLength) {
    1324              :     int addedSignals = 0;
    1325            5 :     for (auto& item : sc.getStops()) {
    1326            4 :         const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
    1327            4 :         if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
    1328              :             NBNode* to = stopEdge->getToNode();
    1329            4 :             if (canBeSignal(to)) {
    1330            2 :                 to->reinit(to->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1331            2 :                 addedSignals++;
    1332              :             }
    1333              :             NBNode* from = stopEdge->getFromNode();
    1334            4 :             if (stopEdge->getLoadedLength() >= minLength) {
    1335              :                 /// XXX should split edge if it is too long
    1336            2 :                 if (canBeSignal(from)) {
    1337            0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1338            0 :                     addedSignals++;
    1339              :                 }
    1340              :             } else {
    1341            2 :                 double searchDist = minLength - stopEdge->getLoadedLength();
    1342            4 :                 while (searchDist > 0 && from->geometryLike()) {
    1343            3 :                     for (const NBEdge* in : from->getIncomingEdges()) {
    1344            3 :                         if (in->getFromNode() != stopEdge->getToNode()) {
    1345              :                             // found edge that isn't a bidi predecessor
    1346              :                             stopEdge = in;
    1347              :                             break;
    1348              :                         }
    1349              :                     }
    1350            2 :                     if (stopEdge->getFromNode() == from) {
    1351              :                         // bidi edge without predecessor
    1352              :                         break;
    1353              :                     } else {
    1354              :                         from = stopEdge->getFromNode();
    1355              :                     }
    1356            2 :                     searchDist -= stopEdge->getLoadedLength();
    1357              :                 }
    1358            2 :                 if (searchDist <= 0 && canBeSignal(from)) {
    1359            0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1360            0 :                     addedSignals++;
    1361              :                 }
    1362              :             }
    1363              :         }
    1364              :     }
    1365            1 :     WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
    1366            1 :     return addedSignals;
    1367              : }
    1368              : 
    1369              : 
    1370              : int
    1371            0 : NBRailwayGeometryHelper::straigthenCorrdidor(NBEdgeCont& ec, double maxAngle) {
    1372              :     int moved = 0;
    1373              :     int numCorridors = 0;
    1374            0 :     std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
    1375              :     std::set<NBNode*> railGeomNodes;
    1376            0 :     for (NBNode* n : railNodes) {
    1377            0 :         if (n->geometryLike()) {
    1378              :             railGeomNodes.insert(n);
    1379              :         }
    1380              :     }
    1381              :     std::set<NBNode*, ComparatorIdLess> kinkNodes;;
    1382            0 :     for (NBNode* n : railGeomNodes) {
    1383            0 :         NBEdge* in = n->getIncomingEdges().front();
    1384            0 :         NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
    1385            0 :         const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
    1386            0 :         if (maxAngle > 0 && relAngle > maxAngle) {
    1387              :             kinkNodes.insert(n);
    1388              :         }
    1389              :     }
    1390            0 :     while (!kinkNodes.empty()) {
    1391              :         std::vector<NBNode*> corridor;
    1392              :         std::vector<NBEdge*> corridorEdges;
    1393            0 :         Boundary corridorBox;
    1394              :         double length = 0;
    1395            0 :         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            0 :         NBEdge* in = n->getIncomingEdges().front();
    1400            0 :         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            0 :         NBNode* up = in->getFromNode();
    1404            0 :         NBNode* down = out->getToNode();
    1405            0 :         corridor.push_back(up);
    1406            0 :         corridor.push_back(n);
    1407            0 :         corridor.push_back(down);
    1408            0 :         corridorBox.add(up->getPosition());
    1409            0 :         corridorBox.add(down->getPosition());
    1410            0 :         corridorEdges.push_back(in);
    1411            0 :         corridorEdges.push_back(out);
    1412            0 :         length += in->getLoadedLength();
    1413            0 :         length += out->getLoadedLength();
    1414              :         Position cBeg, cEnd, delta;
    1415              :         while (kinkNodes.count(up) != 0) {
    1416              :             NBEdge* const out2 = in;
    1417            0 :             NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
    1418            0 :             length += in2->getLoadedLength();
    1419            0 :             up = in2->getFromNode();
    1420            0 :             corridor.insert(corridor.begin(), up);
    1421            0 :             corridorEdges.insert(corridorEdges.begin(), in2);
    1422              :             kinkNodes.erase(up);
    1423            0 :             corridorBox.add(up->getPosition());
    1424              :         }
    1425            0 :         cBeg = up->getPosition();
    1426            0 :         cEnd = down->getPosition();
    1427              :         delta = cEnd - cBeg;
    1428            0 :         while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
    1429              :             NBEdge* const out2 = in;
    1430            0 :             NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
    1431            0 :             length += in2->getLoadedLength();
    1432            0 :             up = in2->getFromNode();
    1433            0 :             corridor.insert(corridor.begin(), up);
    1434            0 :             corridorEdges.insert(corridorEdges.begin(), in2);
    1435              :             kinkNodes.erase(up);
    1436            0 :             corridorBox.add(up->getPosition());
    1437            0 :             cBeg = up->getPosition();
    1438            0 :             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            0 :             NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
    1446            0 :             down = out2->getToNode();
    1447            0 :             length += out2->getLoadedLength();
    1448            0 :             corridor.push_back(down);
    1449            0 :             corridorEdges.push_back(out2);
    1450              :             kinkNodes.erase(down);
    1451            0 :             corridorBox.add(down->getPosition());
    1452              :         }
    1453            0 :         cBeg = up->getPosition();
    1454            0 :         cEnd = down->getPosition();
    1455              :         delta = cEnd - cBeg;
    1456            0 :         while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
    1457              :             NBEdge* const in2 = out;
    1458            0 :             NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
    1459            0 :             down = out2->getToNode();
    1460            0 :             length += out2->getLoadedLength();
    1461            0 :             corridor.push_back(down);
    1462            0 :             corridorEdges.push_back(out2);
    1463              :             kinkNodes.erase(down);
    1464            0 :             corridorBox.add(down->getPosition());
    1465            0 :             cBeg = up->getPosition();
    1466            0 :             cEnd = down->getPosition();
    1467              :             delta = cEnd - cBeg;
    1468              :         }
    1469              :         // straighten all edges in corridor (corridorEdges doesn't include bidi)
    1470            0 :         std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
    1471            0 :         for (NBNode* n2 : corridorNodes) {
    1472            0 :             for (NBEdge* e : n2->getEdges()) {
    1473              :                 if (corridorNodes.count(e->getFromNode()) != 0
    1474              :                         && corridorNodes.count(e->getToNode()) != 0) {
    1475            0 :                     PositionVector simpleGeom;
    1476            0 :                     simpleGeom.push_back(e->getFromNode()->getPosition());
    1477            0 :                     simpleGeom.push_back(e->getToNode()->getPosition());
    1478            0 :                     e->setGeometry(simpleGeom);
    1479            0 :                 }
    1480              :             }
    1481              :         }
    1482            0 :         if (delta.length2D() > 0) {
    1483              :             double currLength = 0;
    1484            0 :             for (int i = 1; i < (int)corridor.size() - 1; i++) {
    1485            0 :                 currLength += corridorEdges[i - 1]->getLoadedLength();
    1486            0 :                 const Position newPos = cBeg + delta * (currLength / length);
    1487            0 :                 NBNode* const n2 = corridor[i];
    1488            0 :                 n2->reinit(newPos, n2->getType());
    1489            0 :                 for (NBEdge* e : n2->getEdges()) {
    1490            0 :                     e->resetEndpointAtNode(n2);
    1491              :                 }
    1492            0 :                 moved += 1;
    1493              :             }
    1494            0 :             numCorridors += 1;
    1495              :         } else {
    1496            0 :             WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
    1497              :         }
    1498            0 :     }
    1499              :     //std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
    1500              : 
    1501            0 :     WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
    1502            0 :     return moved;
    1503              : }
    1504              : 
    1505              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1