LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms_Railway.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 84.0 % 719 604
Test Date: 2026-03-02 16:00:03 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-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      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        29592 : NBRailwayTopologyAnalyzer::Track::addSuccessor(Track* track) {
      61        29592 :     successors.push_back(track);
      62        29592 :     viaSuccessors.push_back(std::make_pair(track, nullptr));
      63        29592 :     minPermissions &= track->edge->getPermissions();
      64        29592 : }
      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         6566 : NBRailwayTopologyAnalyzer::Track::getViaSuccessors(SUMOVehicleClass svc, bool /*ignoreTransientPermissions*/) const {
      88         6566 :     if ((minPermissions & svc) != 0) {
      89         6566 :         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           39 : NBRailwayTopologyAnalyzer::repairTopology(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
     115           39 :     const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
     116              :     int addedBidi = 0;
     117           39 :     if (!minimal) {
     118           35 :         addedBidi += extendBidiEdges(ec);
     119           35 :         addedBidi += reverseEdges(ec, sc, lc); // technically not bidi but new edges nevertheless
     120           35 :         addedBidi += addBidiEdgesForBufferStops(ec);
     121           35 :         addedBidi += addBidiEdgesBetweenSwitches(ec);
     122              :     }
     123           39 :     if (lc.getLines().size() > 0) {
     124           31 :         addedBidi += addBidiEdgesForStops(ec, lc, sc, minimal);
     125              :     }
     126           78 :     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           39 :     return addedBidi;
     132              : }
     133              : 
     134              : 
     135              : int
     136            7 : NBRailwayTopologyAnalyzer::makeAllBidi(NBEdgeCont& ec) {
     137            7 :     int numNotCenterEdges = 0;
     138            7 :     int numAddedBidiEdges = 0;
     139           14 :     std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
     140              :     std::vector<NBEdge*> edges;
     141            7 :     if (inputfile == "") {
     142           81 :         for (NBEdge* edge : ec.getAllEdges()) {
     143           75 :             edges.push_back(edge);
     144            6 :         }
     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           93 :     for (NBEdge* edge : edges) {
     156           86 :         if (hasRailway(edge->getPermissions())) {
     157              :             // rebuild connections if given from an earlier network
     158           86 :             edge->invalidateConnections(true);
     159           86 :             if (!edge->isBidiRail()) {
     160           82 :                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     161           82 :                     NBEdge* e2 = addBidiEdge(ec, edge, false);
     162           82 :                     if (e2 != nullptr) {
     163           82 :                         numAddedBidiEdges++;
     164              :                     }
     165              :                 } else {
     166            0 :                     numNotCenterEdges++;
     167              :                 }
     168              :             }
     169              :         }
     170              :     }
     171           14 :     WRITE_MESSAGEF(TL("Added % bidi-edges to ensure that all tracks are usable in both directions."), toString(numAddedBidiEdges));
     172            7 :     if (numNotCenterEdges) {
     173            0 :         WRITE_WARNINGF(TL("Ignore % edges because they have the wrong spreadType"), toString(numNotCenterEdges));
     174              :     }
     175            7 :     return numAddedBidiEdges;
     176            7 : }
     177              : 
     178              : 
     179              : NBEdge*
     180         1111 : NBRailwayTopologyAnalyzer::addBidiEdge(NBEdgeCont& ec, NBEdge* edge, bool update) {
     181              :     assert(edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER);
     182              :     assert(!edge->isBidiRail());
     183         1111 :     const std::string id2 = (edge->getID()[0] == '-'
     184         1111 :                              ? edge->getID().substr(1)
     185         1111 :                              : "-" + edge->getID());
     186         1111 :     if (ec.wasIgnored(id2)) {
     187              :         // we had it before so the warning is already there
     188              :         return nullptr;
     189              :     }
     190         1108 :     if (ec.retrieve(id2) == nullptr) {
     191              :         NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
     192         1108 :                                 edge, edge->getGeometry().reverse());
     193         2216 :         if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     194          714 :             e2->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     195              :         }
     196         1108 :         ec.insert(e2);
     197         1108 :         if (ec.retrieve(id2) == nullptr) {
     198            3 :             WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
     199            1 :             return nullptr;
     200              :         }
     201         1107 :         if (update) {
     202         1025 :             updateTurns(edge);
     203              :             // reconnected added edges
     204         2931 :             for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
     205         1906 :                 if (hasRailway(incoming->getPermissions())) {
     206         1735 :                     incoming->invalidateConnections(true);
     207              :                 }
     208              :             }
     209              :         }
     210         1107 :         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        19354 : NBRailwayTopologyAnalyzer::getRailEdges(const NBNode* node,
     220              :                                         EdgeVector& inEdges, EdgeVector& outEdges) {
     221        52730 :     for (NBEdge* e : node->getIncomingEdges()) {
     222        33376 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     223        31520 :             inEdges.push_back(e);
     224              :         }
     225              :     }
     226        52467 :     for (NBEdge* e : node->getOutgoingEdges()) {
     227        33113 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     228        31260 :             outEdges.push_back(e);
     229              :         }
     230              :     }
     231        19354 : }
     232              : 
     233              : 
     234              : std::set<NBNode*>
     235          140 : NBRailwayTopologyAnalyzer::getBrokenRailNodes(NBEdgeCont& ec, bool verbose) {
     236              :     std::set<NBNode*> brokenNodes;
     237          140 :     OutputDevice& device = OutputDevice::getDevice(verbose
     238          309 :                            ? OptionsCont::getOptions().getString("railway.topology.output")
     239              :                            : "/dev/null");
     240              : 
     241          280 :     device.writeXMLHeader("railwayTopology", "");
     242          140 :     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         6123 :     for (NBNode* node : railNodes) {
     247              :         EdgeVector inEdges, outEdges;
     248         5983 :         getRailEdges(node, inEdges, outEdges);
     249        11966 :         types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
     250        15249 :         for (NBEdge* e : outEdges) {
     251        11483 :             if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
     252         4527 :                 NBEdge* primary = e;
     253         4527 :                 NBEdge* secondary = e->getTurnDestination(true);
     254         4527 :                 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         5983 :     }
     266              : 
     267          140 :     int numBrokenA = 0;
     268          140 :     int numBrokenB = 0;
     269          140 :     int numBrokenC = 0;
     270          140 :     int numBrokenD = 0;
     271          140 :     int numBufferStops = 0;
     272          140 :     if (verbose && types.size() > 0) {
     273           58 :         WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
     274              :     }
     275          140 :     device.openTag("legend");
     276          140 :     device.openTag("error");
     277          140 :     device.writeAttr(SUMO_ATTR_ID, "a");
     278          140 :     device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
     279          140 :     device.closeTag();
     280          140 :     device.openTag("error");
     281          140 :     device.writeAttr(SUMO_ATTR_ID, "b");
     282          140 :     device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
     283          140 :     device.closeTag();
     284          140 :     device.openTag("error");
     285          140 :     device.writeAttr(SUMO_ATTR_ID, "c");
     286          140 :     device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
     287          140 :     device.closeTag();
     288          140 :     device.openTag("error");
     289          140 :     device.writeAttr(SUMO_ATTR_ID, "d");
     290          140 :     device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
     291          140 :     device.closeTag();
     292          280 :     device.closeTag();
     293              : 
     294          779 :     for (auto it : types) {
     295          639 :         int numBrokenType = 0;
     296          639 :         device.openTag("railNodeType");
     297          639 :         int in = it.first.first;
     298          639 :         int out = it.first.second;
     299          639 :         device.writeAttr("in", in);
     300         1278 :         device.writeAttr("out", out);
     301         6622 :         for (NBNode* n : it.second) {
     302         5983 :             device.openTag(SUMO_TAG_NODE);
     303         5983 :             device.writeAttr(SUMO_ATTR_ID, n->getID());
     304              :             EdgeVector inRail, outRail;
     305         5983 :             getRailEdges(n, inRail, outRail);
     306              :             // check if there is a mismatch between angle and edge direction
     307              :             // (see above)
     308              : 
     309         5983 :             std::string broken = "";
     310         5983 :             if (in < 2 && hasStraightPair(n, outRail, outRail)) {
     311              :                 broken += "a";
     312          106 :                 numBrokenA++;
     313              :             }
     314         5983 :             if (out < 2 && hasStraightPair(n, inRail, inRail)) {
     315              :                 broken += "b";
     316          149 :                 numBrokenB++;
     317              :             }
     318         5983 :             if (out > 0) {
     319        14409 :                 for (NBEdge* e : inRail) {
     320              :                     EdgeVector tmp;
     321         8844 :                     tmp.push_back(e);
     322         8844 :                     if (allSharp(n, tmp, outRail)) {
     323              :                         broken += "c";
     324           90 :                         numBrokenC++;
     325              :                         break;
     326              :                     }
     327         8844 :                 }
     328              :             }
     329         5983 :             if (in > 0) {
     330        14361 :                 for (NBEdge* e : outRail) {
     331              :                     EdgeVector tmp;
     332         8815 :                     tmp.push_back(e);
     333         8815 :                     if (allSharp(n, inRail, tmp)) {
     334              :                         broken += "d";
     335           71 :                         numBrokenD++;
     336              :                         break;
     337              :                     }
     338         8815 :                 }
     339              :             }
     340              :             // do not mark bidi nodes as broken
     341         5983 :             if (((in == 1 && out == 1) || (in == 2 && out == 2))
     342        10870 :                     && allBidi(inRail) && allBidi(outRail)) {
     343              :                 broken = "";
     344              :             }
     345              : 
     346         5983 :             if (broken.size() > 0) {
     347          514 :                 device.writeAttr("broken", broken);
     348              :                 brokenNodes.insert(n);
     349          257 :                 numBrokenType++;
     350              :             }
     351        11966 :             if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
     352           39 :                 device.writeAttr("buffer_stop", "true");
     353           39 :                 numBufferStops++;
     354              :             }
     355        11966 :             device.closeTag();
     356         5983 :         }
     357          639 :         device.closeTag();
     358          639 :         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          140 :     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         3512 :     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          140 :     if (verbose) {
     381           58 :         WRITE_MESSAGEF(TL("Found % bidirectional rail edges"), toString(bidiEdges.size()));
     382              :     }
     383              : 
     384          140 :     device.close();
     385          140 :     return brokenNodes;
     386              : }
     387              : 
     388              : 
     389              : std::set<NBNode*>
     390          206 : NBRailwayTopologyAnalyzer::getRailNodes(NBEdgeCont& ec, bool verbose) {
     391              :     std::set<NBNode*> railNodes;
     392          206 :     int numRailEdges = 0;
     393        43919 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     394        43713 :         if (hasRailway(it->second->getPermissions())) {
     395        15540 :             numRailEdges++;
     396        15540 :             railNodes.insert(it->second->getFromNode());
     397        15540 :             railNodes.insert(it->second->getToNode());
     398              :         }
     399              :     }
     400          206 :     int numRailSignals = 0;
     401        10245 :     for (const NBNode* const node : railNodes) {
     402        10039 :         if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     403         2512 :             numRailSignals++;
     404              :         }
     405              :     }
     406          206 :     if (verbose) {
     407           58 :         WRITE_MESSAGEF(TL("Found % railway edges and % railway nodes (% signals)."), toString(numRailEdges), toString(railNodes.size()), toString(numRailSignals));
     408              :     }
     409          206 :     return railNodes;
     410              : }
     411              : 
     412              : 
     413              : bool
     414        44038 : NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
     415        44038 :     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        34790 :     if ((e1->getToNode() == node && e2->getFromNode() == node)
     426        46891 :             || (e1->getFromNode() == node && e2->getToNode() == node)) {
     427              :         // edges go in the same direction
     428        38337 :         return fabs(relAngle) < SHARP_THRESHOLD;
     429              :     } else {
     430              :         // edges go in the opposite direction (both incoming or outgoing)
     431         5701 :         return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
     432              :     }
     433              : }
     434              : 
     435              : 
     436              : bool
     437         4831 : 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         8980 :     for (NBEdge* e1 : edges) {
     445         9209 :         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         5060 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     454              :                 return true;
     455              :             }
     456              :         }
     457              :     }
     458              :     return false;
     459              : }
     460              : 
     461              : 
     462              : bool
     463           83 : NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
     464          112 :     for (NBEdge* e : in) {
     465           70 :         if (e != candOut && isStraight(node, e, candOut)) {
     466           41 :             if (gDebugFlag1) {
     467            0 :                 std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
     468              :             }
     469              :             return false;
     470              :         }
     471              :     }
     472          120 :     for (NBEdge* e : out) {
     473           84 :         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        18714 : NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
     486              :     bool allBidi = true;
     487        23760 :     for (NBEdge* e1 : in) {
     488        30680 :         for (NBEdge* e2 : out) {
     489        25634 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     490              :                 return false;
     491              :             }
     492         8484 :             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         1564 :     return !allBidi || countBidiAsSharp;
     499              : }
     500              : 
     501              : 
     502              : bool
     503         8234 : NBRailwayTopologyAnalyzer::allBidi(const EdgeVector& edges) {
     504        21296 :     for (NBEdge* e : edges) {
     505        14602 :         if (!e->isBidiRail()) {
     506              :             return false;
     507              :         }
     508              :     }
     509              :     return true;
     510              : }
     511              : 
     512              : 
     513              : int
     514           38 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec) {
     515           38 :     int added = 0;
     516         8314 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     517         8276 :         NBEdge* e = it->second;
     518         8276 :         if (e->isBidiRail()) {
     519         1853 :             added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
     520         1853 :             added += extendBidiEdges(ec, e->getToNode(), e);
     521              :         }
     522              :     }
     523           38 :     if (added > 0) {
     524           24 :         WRITE_MESSAGEF(TL("Added % bidi-edges as extension of existing bidi edges."), toString(added));
     525              :     }
     526           38 :     return added;
     527              : }
     528              : 
     529              : 
     530              : int
     531         4733 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec, NBNode* node, NBEdge* bidiIn) {
     532              :     assert(bidiIn->getToNode() == node);
     533         4733 :     NBEdge* bidiOut = bidiIn->getTurnDestination(true);
     534         4733 :     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         4733 :     tmpBidiOut.push_back(bidiOut);
     540              :     EdgeVector tmpBidiIn;
     541         4733 :     tmpBidiIn.push_back(bidiIn);
     542              :     int added = 0;
     543              :     EdgeVector inRail, outRail;
     544         4733 :     getRailEdges(node, inRail, outRail);
     545        13600 :     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         9340 :         if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
     548          435 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     549         9294 :                 && allSharp(node, inRail, tmpBidiOut, true)) {
     550          387 :             NBEdge* e2 = addBidiEdge(ec, cand);
     551          387 :             if (e2 != nullptr) {
     552          387 :                 added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
     553              :             }
     554              :         }
     555              :     }
     556        13794 :     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          637 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     560         9689 :                 && allSharp(node, outRail, tmpBidiIn, true)) {
     561          584 :             NBEdge* e2 = addBidiEdge(ec, cand);
     562          584 :             if (e2 != nullptr) {
     563          582 :                 added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
     564              :             }
     565              :         }
     566              :     }
     567              :     return added;
     568         4733 : }
     569              : 
     570              : 
     571              : int
     572           35 : NBRailwayTopologyAnalyzer::reverseEdges(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
     573           35 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     574              :     // find reversible edge sequences between broken nodes
     575              :     std::vector<EdgeVector> seqsToReverse;
     576              :     EdgeSet lineEdges;
     577          174 :     for (auto item : lc.getLines()) {
     578              :         const EdgeVector& route = item.second->getEdges();
     579              :         lineEdges.insert(route.begin(), route.end());
     580              :     }
     581          112 :     for (NBNode* n : brokenNodes) {
     582              :         EdgeVector inRail, outRail;
     583           77 :         getRailEdges(n, inRail, outRail);
     584          175 :         for (NBEdge* start : outRail) {
     585           24 :             if (lineEdges.count(start)) {
     586              :                 // ptline edges should never be reversed. They are fixed by a different algorithm require their original ids and orientation
     587           68 :                 continue;
     588              :             }
     589              :             EdgeVector tmp;
     590           74 :             tmp.push_back(start);
     591              :             // only reverse edges where the node would be unbroken afterwards
     592           74 :             if (!allBroken(n, start, inRail, outRail)
     593           74 :                     || (inRail.size() == 1 && outRail.size() == 1)) {
     594              : #ifdef DEBUG_SEQSTOREVERSE
     595              :                 if (n->getID() == DEBUGNODEID) {
     596              :                     std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
     597              :                 }
     598              : #endif
     599              :                 continue;
     600              :             }
     601              :             //std::cout << " get sequences from " << start->getID() << "\n";
     602              :             bool forward = true;
     603              :             EdgeVector seq;
     604           74 :             while (forward) {
     605           44 :                 seq.push_back(start);
     606              :                 //std::cout << " seq=" << toString(seq) << "\n";
     607           44 :                 NBNode* n2 = start->getToNode();
     608              :                 EdgeVector inRail2, outRail2;
     609           44 :                 getRailEdges(n2, inRail2, outRail2);
     610              :                 if (brokenNodes.count(n2) != 0) {
     611              :                     EdgeVector tmp2;
     612            9 :                     tmp2.push_back(start);
     613            9 :                     if (allBroken(n2, start, outRail2, inRail2)) {
     614            6 :                         seqsToReverse.push_back(seq);
     615              :                     } else {
     616              : #ifdef DEBUG_SEQSTOREVERSE
     617              :                         if (n->getID() == DEBUGNODEID) {
     618              :                             std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
     619              :                         }
     620              : #endif
     621              :                     }
     622              :                     forward = false;
     623            9 :                 } else {
     624           35 :                     if (outRail2.size() == 0) {
     625              :                         // stop at network border
     626              :                         forward = false;
     627              : #ifdef DEBUG_SEQSTOREVERSE
     628              :                         if (n->getID() == DEBUGNODEID) {
     629              :                             std::cout << " abort at n2=" << n2->getID() << " (border)\n";
     630              :                         }
     631              : #endif
     632           19 :                     } else if (outRail2.size() > 1 || inRail2.size() > 1) {
     633              :                         // stop at switch
     634              :                         forward = false;
     635              : #ifdef DEBUG_SEQSTOREVERSE
     636              :                         if (n->getID() == DEBUGNODEID) {
     637              :                             std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
     638              :                         }
     639              : #endif
     640              :                     } else {
     641           14 :                         start = outRail2.front();
     642              :                     }
     643              :                 }
     644           44 :             }
     645           74 :         }
     646           77 :     }
     647              :     // sort by sequence length
     648           35 :     if (seqsToReverse.size() > 0) {
     649            8 :         WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
     650              :     }
     651           35 :     std::sort(seqsToReverse.begin(), seqsToReverse.end(),
     652              :     [](const EdgeVector & a, const EdgeVector & b) {
     653              :         return a.size() < b.size();
     654              :     });
     655           35 :     int numReversed = 0;
     656              :     std::set<NBNode*> affectedEndpoints;
     657              :     std::set<std::string> reversedIDs;
     658              :     std::map<int, int> seqLengths;
     659           41 :     for (EdgeVector& seq : seqsToReverse) {
     660            6 :         NBNode* seqStart = seq.front()->getFromNode();
     661            6 :         NBNode* seqEnd = seq.back()->getToNode();
     662              :         // avoid reversing sequences on both sides of a broken node
     663              :         if (affectedEndpoints.count(seqStart) == 0
     664              :                 && affectedEndpoints.count(seqEnd) == 0) {
     665              :             affectedEndpoints.insert(seqStart);
     666              :             affectedEndpoints.insert(seqEnd);
     667              :             //WRITE_MESSAGE("  reversed seq=" + toString(seq));
     668           15 :             for (NBEdge* e : seq) {
     669            9 :                 e->reinitNodes(e->getToNode(), e->getFromNode());
     670            9 :                 e->setGeometry(e->getGeometry().reverse());
     671            9 :                 reversedIDs.insert(e->getID());
     672           18 :                 if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     673            0 :                     e->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     674              :                 }
     675              :             }
     676            6 :             seqLengths[(int)seq.size()]++;
     677            6 :             numReversed++;
     678              :         }
     679              :     }
     680           35 :     if (numReversed > 0) {
     681            8 :         WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
     682          137 :         for (auto& item : sc.getStops()) {
     683          133 :             if (reversedIDs.count(item.second->getEdgeId())) {
     684            0 :                 item.second->findLaneAndComputeBusStopExtent(ec);
     685              :             }
     686              :         }
     687              :     }
     688           35 :     return numReversed;
     689           35 : }
     690              : 
     691              : 
     692              : int
     693           35 : NBRailwayTopologyAnalyzer::addBidiEdgesForBufferStops(NBEdgeCont& ec) {
     694           35 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     695           35 :     std::set<NBNode*> railNodes = getRailNodes(ec);
     696              :     // find buffer stops and ensure that they are connect to the network in both directions
     697           35 :     int numBufferStops = 0;
     698           35 :     int numAddedBidiTotal = 0;
     699         1872 :     for (NBNode* node : railNodes) {
     700         3674 :         if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
     701           10 :             if (node->getEdges().size() != 1) {
     702            3 :                 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
     703            1 :                 continue;
     704              :             }
     705              :             // int numAddedBidi = 0;
     706            9 :             numBufferStops++;
     707              :             NBEdge* prev = nullptr;
     708              :             NBEdge* prev2 = nullptr;
     709              :             EdgeVector inRail, outRail;
     710            9 :             getRailEdges(node, inRail, outRail);
     711              :             bool addAway = true; // add new edges away from buffer stop
     712           22 :             while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
     713              :                 NBEdge* e = nullptr;
     714           13 :                 if (prev == nullptr) {
     715              :                     assert(node->getEdges().size() == 1);
     716            9 :                     e = node->getEdges().front();
     717            9 :                     addAway = node == e->getToNode();
     718              :                 } else {
     719            4 :                     if (addAway) {
     720              :                         // XXX if node is broken we need to switch direction
     721              :                         assert(inRail.size() == 2);
     722            3 :                         e = inRail.front() == prev2 ? inRail.back() : inRail.front();
     723              :                     } else {
     724              :                         // XXX if node is broken we need to switch direction
     725              :                         assert(outRail.size() == 2);
     726            1 :                         e = outRail.front() == prev2 ? outRail.back() : outRail.front();
     727              :                     }
     728              :                 }
     729           13 :                 e->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
     730              :                 NBNode* e2From = nullptr;
     731              :                 NBNode* e2To = nullptr;
     732           13 :                 if (addAway) {
     733              :                     e2From = node;
     734              :                     e2To = e->getFromNode();
     735              :                     node = e2To;
     736              :                 } else {
     737              :                     e2From = e->getToNode();
     738              :                     e2To = node;
     739              :                     node = e2From;
     740              :                 }
     741           13 :                 NBEdge* e2 = addBidiEdge(ec, e);
     742           13 :                 if (e2 == nullptr) {
     743              :                     break;
     744              :                 }
     745              :                 prev = e;
     746              :                 prev2 = e2;
     747              :                 // numAddedBidi++;
     748           13 :                 numAddedBidiTotal++;
     749              :                 inRail.clear();
     750              :                 outRail.clear();
     751           13 :                 getRailEdges(node, inRail, outRail);
     752              :             }
     753              :             //if (numAddedBidi > 0) {
     754              :             //    WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
     755              :             //}
     756            9 :         }
     757              :     }
     758           35 :     if (numAddedBidiTotal > 0) {
     759           10 :         WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
     760              :     }
     761           35 :     return numAddedBidiTotal;
     762              : }
     763              : 
     764              : NBEdge*
     765          106 : NBRailwayTopologyAnalyzer::isBidiSwitch(const NBNode* n) {
     766              :     EdgeVector inRail, outRail;
     767          106 :     getRailEdges(n, inRail, outRail);
     768          106 :     if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
     769           28 :         if (isStraight(n, inRail.front(), outRail.front())) {
     770           23 :             return inRail.front();
     771            5 :         } else if (isStraight(n, inRail.back(), outRail.front())) {
     772            5 :             return inRail.back();
     773              :         }
     774              :     }
     775           78 :     if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
     776           19 :         if (isStraight(n, outRail.front(), inRail.front())) {
     777           10 :             return outRail.front();
     778            9 :         } else if (isStraight(n, outRail.back(), inRail.front())) {
     779            9 :             return outRail.back();
     780              :         }
     781              :     }
     782              :     return nullptr;
     783          106 : }
     784              : 
     785              : 
     786              : int
     787           35 : NBRailwayTopologyAnalyzer::addBidiEdgesBetweenSwitches(NBEdgeCont& ec) {
     788           35 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     789              :     std::map<int, int> seqLengths;
     790           35 :     int numAdded = 0;
     791           35 :     int numSeqs = 0;
     792          101 :     for (NBNode* n : brokenNodes) {
     793           66 :         NBEdge* edge = isBidiSwitch(n);
     794           66 :         if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     795              :             std::vector<NBNode*> nodeSeq;
     796              :             EdgeVector edgeSeq;
     797           40 :             NBNode* prev = n;
     798           40 :             nodeSeq.push_back(prev);
     799           40 :             edgeSeq.push_back(edge);
     800              :             bool forward = true;
     801              :             //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
     802              :             // find a suitable end point for adding bidi edges
     803          129 :             while (forward) {
     804           89 :                 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
     805              :                 EdgeVector allRail;
     806           89 :                 getRailEdges(next, allRail, allRail);
     807           89 :                 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
     808           49 :                     prev = next;
     809           49 :                     edge = allRail.front() == edge ? allRail.back() : allRail.front();
     810           49 :                     nodeSeq.push_back(prev);
     811           49 :                     edgeSeq.push_back(edge);
     812              :                 } else {
     813              :                     forward = false;
     814              :                     EdgeVector inRail2, outRail2;
     815           40 :                     getRailEdges(next, inRail2, outRail2);
     816           40 :                     if (isBidiSwitch(next) == edge) {
     817              :                         // suitable switch found as endpoint, add reverse edges
     818              :                         //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
     819              :                         //        + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
     820           10 :                         for (NBEdge* e : edgeSeq) {
     821            6 :                             addBidiEdge(ec, e);
     822              :                         }
     823            4 :                         seqLengths[(int)edgeSeq.size()]++;
     824            4 :                         numSeqs++;
     825            4 :                         numAdded += (int)edgeSeq.size();
     826              :                     } else {
     827              :                         //std::cout << " sequence ended at junction " << next->getID()
     828              :                         //    << " in=" << inRail2.size()
     829              :                         //    << " out=" << outRail2.size()
     830              :                         //    << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
     831              :                         //    << "\n";
     832              :                     }
     833              : 
     834           40 :                 }
     835           89 :             }
     836              : 
     837           40 :         }
     838              :     }
     839           35 :     if (seqLengths.size() > 0) {
     840            4 :         WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
     841              :     }
     842           70 :     return numAdded;
     843              : }
     844              : 
     845              : 
     846              : std::set<NBPTLine*>
     847           31 : NBRailwayTopologyAnalyzer::findBidiCandidates(NBPTLineCont& lc) {
     848              :     std::set<NBPTLine*>  result;
     849              :     std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
     850          195 :     for (const auto& item : lc.getLines()) {
     851          164 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     852          164 :         if (stops.size() > 1) {
     853          659 :             for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     854              :                 std::shared_ptr<NBPTStop> fromStop = *it;
     855              :                 std::shared_ptr<NBPTStop> toStop = *(it + 1);
     856          517 :                 visited.insert({fromStop, toStop});
     857              :             }
     858              :         }
     859              :     }
     860          195 :     for (const auto& item : lc.getLines()) {
     861          164 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     862          164 :         if (stops.size() > 1) {
     863          581 :             for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     864              :                 std::shared_ptr<NBPTStop> fromStop = *it;
     865              :                 std::shared_ptr<NBPTStop> toStop = *(it + 1);
     866              :                 std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
     867              :                 if (visited.count(reverseTrip)) {
     868           14 :                     result.insert(item.second);
     869              :                     break;
     870              :                 }
     871          453 :             }
     872              :         }
     873              :     }
     874           31 :     return result;
     875              : }
     876              : 
     877              : int
     878           31 : NBRailwayTopologyAnalyzer::addBidiEdgesForStops(NBEdgeCont& ec, NBPTLineCont& lc, NBPTStopCont& sc, bool minimal) {
     879           62 :     const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
     880              :     // generate bidirectional routing graph
     881              :     std::vector<Track*> tracks;
     882         9066 :     for (NBEdge* edge : ec.getAllEdges()) {
     883        18070 :         tracks.push_back(new Track(edge));
     884           31 :     }
     885           31 :     const int numEdges = (int)tracks.size();
     886         9066 :     for (NBEdge* edge : ec.getAllEdges()) {
     887        18070 :         tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
     888           31 :     }
     889              :     // add special tracks for starting end ending in both directions
     890              :     std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
     891         9066 :     for (NBEdge* edge : ec.getAllEdges()) {
     892         9035 :         if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     893         3384 :             Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
     894         3384 :             tracks.push_back(start);
     895         3384 :             Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
     896         3384 :             tracks.push_back(end);
     897         3384 :             stopTracks[edge] = {start, end};
     898              :         }
     899           31 :     }
     900              :     // set successors based on angle (connections are not yet built)
     901         2250 :     for (NBNode* node : getRailNodes(ec)) {
     902              :         EdgeVector railEdges;
     903         2219 :         getRailEdges(node, railEdges, railEdges);
     904         8986 :         for (NBEdge* e1 : railEdges) {
     905        29834 :             for (NBEdge* e2 : railEdges) {
     906        23067 :                 if (e1 != e2 && isStraight(node, e1, e2)) {
     907        11332 :                     int i = e1->getNumericalID();
     908        11332 :                     int i2 = e2->getNumericalID();
     909        11332 :                     if (e1->getToNode() == node) {
     910         5676 :                         if (e2->getFromNode() == node) {
     911              :                             // case 1) plain forward connection
     912         3304 :                             tracks[i]->addSuccessor(tracks[i2]);
     913              :                             // reverse edge (numerical id incremented by numEdges)
     914         3304 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
     915              :                         } else {
     916              :                             // case 2) both edges pointing towards each other
     917         2372 :                             tracks[i]->addSuccessor(tracks[i2 + numEdges]);
     918         2372 :                             tracks[i2]->addSuccessor(tracks[i + numEdges]);
     919              :                         }
     920              :                     } else {
     921         5656 :                         if (e2->getFromNode() == node) {
     922              :                             // case 3) both edges pointing away from each other
     923         2352 :                             tracks[i + numEdges]->addSuccessor(tracks[i2]);
     924         2352 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i]);
     925              :                         } else {
     926              :                             // already handled via case 1)
     927              :                         }
     928              :                     }
     929              : 
     930              :                 }
     931              :             }
     932              :         }
     933         2219 :     }
     934              :     // define start and end successors
     935         3415 :     for (auto& item : stopTracks) {
     936         3384 :         const int index = item.first->getNumericalID();
     937              :         // start
     938         3384 :         item.second.first->addSuccessor(tracks[index]);
     939         3384 :         item.second.first->addSuccessor(tracks[index + numEdges]);
     940              :         // end
     941         3384 :         tracks[index]->addSuccessor(item.second.second);
     942         3384 :         tracks[index + numEdges]->addSuccessor(item.second.second);
     943              :     }
     944              :     // DEBUG
     945              :     /*
     946              :     for (Track* t : tracks) {
     947              :         std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
     948              :         for (Track* s : t->getSuccessors(SVC_IGNORING)) {
     949              :             std::cout << "   succ=" << s->getID() << "\n";
     950              :         }
     951              :     }
     952              :     */
     953              : 
     954              :     SUMOAbstractRouter<Track, NBVehicle>* const router = new DijkstraRouter<Track, NBVehicle>(
     955           31 :         tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
     956              : 
     957           31 :     int added = 0;
     958           31 :     int numDisconnected = 0;
     959              :     std::set<std::shared_ptr<NBPTStop>> addBidiStops;
     960              :     std::set<NBEdge*, ComparatorIdLess> addBidiStopEdges;
     961              :     std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
     962              :     std::set<std::pair<std::string, std::string> > visited;
     963              : 
     964              :     // the isConsistent heuristic may fail in some cases. If we observe that a
     965              :     // specific sequence of stop ids in encoded in both directions, we take this
     966              :     // as a reason to overrule the heuristic
     967           31 :     std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
     968              : 
     969          195 :     for (const auto& item : lc.getLines()) {
     970          164 :         NBPTLine* line = item.second;
     971          164 :         std::vector<NBPTLine::PTStopInfo> stops = line->getStopEdges(ec);
     972              :         std::vector<NBEdge*> stopEdges;
     973          844 :         for (auto it : stops) {
     974          680 :             stopEdges.push_back(it.edge);
     975              :         }
     976          164 :         NBEdge* routeStart = line->getRouteStart(ec);
     977          164 :         NBEdge* routeEnd = line->getRouteEnd(ec);
     978          164 :         if (routeStart != nullptr && (stopEdges.empty() || routeStart != stopEdges.front())) {
     979          160 :             stops.insert(stops.begin(), NBPTLine::PTStopInfo(routeStart, routeStart->getID(), 0, false));
     980              :         }
     981          164 :         if (routeEnd != nullptr && (stopEdges.empty() || routeEnd != stopEdges.back())) {
     982          174 :             stops.push_back(NBPTLine::PTStopInfo(routeEnd, routeEnd->getID(), routeEnd->getLength(), false));
     983              :         }
     984          164 :         if (stops.size() < 2) {
     985            7 :             continue;
     986              :         }
     987          314 :         if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
     988           27 :             WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
     989            9 :             continue;
     990              :         }
     991          796 :         for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     992          648 :             NBEdge* fromEdge = it->edge;
     993          648 :             NBEdge* toEdge = (it + 1)->edge;
     994              :             const std::string fromStop = it->stopID;
     995              :             const std::string toStop = (it + 1)->stopID;
     996          648 :             const double fromPos = it->pos;
     997          648 :             const double toPos = (it + 1)->pos;
     998          648 :             bool fromRevised = it->revised;
     999          648 :             bool toRevised = (it + 1)->revised;
    1000          648 :             std::pair<std::string, std::string> trip(fromStop, toStop);
    1001          648 :             std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
    1002              :             //if (line->getLineID() == "147471") {
    1003              :             //    std::cout << " line=" << line->getLineID() << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << " fromStop=" << fromStop << " toStop=" << toStop << " fromRevised=" << fromRevised << " toRevised=" << toRevised << "\n";
    1004              :             //}
    1005          222 :             if (visited.count(trip) != 0) {
    1006          222 :                 continue;
    1007              :             } else {
    1008              :                 visited.insert(trip);
    1009              :             }
    1010           98 :             if (stopTracks.count(fromEdge) == 0
    1011              :                     || stopTracks.count(toEdge) == 0) {
    1012           98 :                 continue;
    1013              :             }
    1014          328 :             bool needBidi = visited.count(reverseTrip) != 0;
    1015          328 :             NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
    1016              :             std::vector<const Track*> route;
    1017          328 :             Track* from = fromRevised ? tracks[fromEdge->getNumericalID()] : stopTracks[fromEdge].first;
    1018          328 :             Track* to = toRevised ? tracks[toEdge->getNumericalID()] : stopTracks[toEdge].second;
    1019          328 :             int iStart = fromRevised ? 0 : 1;
    1020          328 :             int iDeltaEnd = toRevised ? 0 : 1;
    1021          328 :             if (fromEdge == toEdge && fromPos > toPos) {
    1022              :                 // must use reverse edge
    1023           18 :                 route.push_back(tracks[fromEdge->getNumericalID() + numEdges]);
    1024              :                 iStart = 0;
    1025              :                 iDeltaEnd = 0;
    1026              :                 needBidi = true;
    1027              :             } else {
    1028          310 :                 router->compute(from, to, &veh, 0, route);
    1029              :             }
    1030              :             //if (line->getLineID() == "147471") {
    1031              :             //    std::cout << "DEBUG: route=" << toString(route) << "\n";
    1032              :             //}
    1033          328 :             if (route.size() > 0) {
    1034              :                 assert((int)route.size() > iStart + iDeltaEnd);
    1035         2665 :                 for (int i = iStart; i < (int)route.size() - iDeltaEnd; ++i) {
    1036         2337 :                     const bool isBidi = route[i]->getNumericalID() >= numEdges;
    1037         2337 :                     bool isStop = i == iStart || i == (int)route.size() - 1 - iDeltaEnd;
    1038         2337 :                     if (isBidi || needBidi) {
    1039          129 :                         NBEdge* edge = route[i]->edge;
    1040              :                         if (addBidiEdges.count(edge) == 0) {
    1041          105 :                             if (!edge->isBidiRail(true)) {
    1042           79 :                                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
    1043              :                                     addBidiEdges.insert(edge);
    1044           79 :                                     if (isStop) {
    1045              :                                         addBidiStopEdges.insert(edge);
    1046              :                                     }
    1047              :                                 } else {
    1048            0 :                                     if (isStop) {
    1049            0 :                                         WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
    1050              :                                     }
    1051              :                                 }
    1052              :                             }
    1053              :                         }
    1054              :                     }
    1055           86 :                     if (isStop && isBidi) {
    1056          146 :                         std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
    1057           73 :                         if (fs) {
    1058              :                             addBidiStops.insert(fs);
    1059              :                         }
    1060          146 :                         std::shared_ptr<NBPTStop> ts = sc.get(toStop);
    1061           73 :                         if (ts) {
    1062              :                             addBidiStops.insert(ts);
    1063              :                         }
    1064              :                     }
    1065              :                 }
    1066              :             } else {
    1067            0 :                 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
    1068            0 :                 numDisconnected++;
    1069              :             }
    1070          328 :         }
    1071          164 :     }
    1072          110 :     for (NBEdge* edge : addBidiEdges) {
    1073           79 :         if (!edge->isBidiRail()) {
    1074           33 :             NBEdge* e2 = addBidiEdge(ec, edge);
    1075              :             //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
    1076           33 :             if (e2 != nullptr) {
    1077           33 :                 added++;
    1078           33 :                 if (!minimal) {
    1079           23 :                     added += extendBidiEdges(ec, edge->getToNode(), edge);
    1080           23 :                     added += extendBidiEdges(ec, edge->getFromNode(), e2);
    1081              :                 }
    1082              :             }
    1083              :         }
    1084              :     }
    1085          110 :     for (std::shared_ptr<NBPTStop> stop : addBidiStops) {
    1086          158 :         std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(stop, ec);
    1087           79 :         if (fromReverse) {
    1088          140 :             sc.insert(fromReverse);
    1089           70 :             stop->setBidiStop(fromReverse);
    1090              :         }
    1091              :     }
    1092           31 :     if (addBidiEdges.size() > 0 || numDisconnected > 0) {
    1093           95 :         WRITE_MESSAGE("Added " + toString(addBidiStopEdges.size()) + " bidi-edges for public transport stops and a total of "
    1094              :                       + toString(added) + " bidi-edges to ensure connectivity of stops ("
    1095              :                       + toString(numDisconnected) + " stops remain disconnected)");
    1096              :     }
    1097              : 
    1098              :     // clean up
    1099        24869 :     for (Track* t : tracks) {
    1100        24838 :         delete t;
    1101              :     }
    1102           31 :     delete router;
    1103           62 :     return (int)addBidiEdges.size();
    1104           31 : }
    1105              : 
    1106              : 
    1107              : int
    1108            6 : NBRailwayTopologyAnalyzer::addBidiEdgesForStraightConnectivity(NBEdgeCont& ec, bool geometryLike) {
    1109            6 :     int added = 0;
    1110            6 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
    1111          107 :     for (const auto& e : ec) {
    1112          101 :         if (!hasRailway(e.second->getPermissions())) {
    1113           72 :             continue;
    1114              :         }
    1115          101 :         NBNode* const from = e.second->getFromNode();
    1116              :         NBNode* const to = e.second->getToNode();
    1117           59 :         if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
    1118           59 :             continue;
    1119              :         }
    1120           42 :         if (e.second->isBidiRail()) {
    1121           13 :             continue;
    1122              :         }
    1123              :         EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
    1124           29 :         getRailEdges(from, inRailFrom, outRailFrom);
    1125           29 :         getRailEdges(to, inRailTo, outRailTo);
    1126              :         // check whether there is a straight edge pointing away from this one at the from-node
    1127              :         // and there is no straight incoming edge at the from-node
    1128              :         bool haveStraight = false;
    1129              :         bool haveStraightReverse = false;
    1130           29 :         if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
    1131           25 :             for (const NBEdge* fromStraightCand : outRailFrom) {
    1132           17 :                 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
    1133              :                     haveStraightReverse = true;
    1134              :                     //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
    1135              :                     break;
    1136              :                 }
    1137              :             }
    1138           12 :             if (haveStraightReverse) {
    1139            5 :                 for (const NBEdge* fromStraightCand : inRailFrom) {
    1140            4 :                     if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
    1141              :                         haveStraight = true;
    1142              :                         //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
    1143              :                         break;
    1144              :                     }
    1145              :                 }
    1146              :             }
    1147              :         }
    1148           29 :         if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
    1149              :             // check whether there is a straight edge pointing towards this one at the to-node
    1150              :             // and there is no straight outgoing edge at the to-node
    1151              :             haveStraight = false;
    1152              :             haveStraightReverse = false;
    1153           26 :             for (const NBEdge* toStraightCand : inRailTo) {
    1154           22 :                 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
    1155              :                     haveStraightReverse = true;
    1156              :                     //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
    1157              :                     break;
    1158              :                 }
    1159              :             }
    1160           13 :             if (haveStraightReverse) {
    1161           13 :                 for (const NBEdge* toStraightCand : outRailTo) {
    1162            8 :                     if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
    1163              :                         haveStraight = true;
    1164              :                         //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
    1165              :                         break;
    1166              :                     }
    1167              :                 }
    1168              :             }
    1169              :         }
    1170              :         //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
    1171           29 :         if (haveStraightReverse && !haveStraight) {
    1172            6 :             NBEdge* e2 = addBidiEdge(ec, e.second);
    1173              :             //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
    1174            6 :             if (e2 != nullptr) {
    1175            6 :                 added++;
    1176            6 :                 added += extendBidiEdges(ec, to, e.second);
    1177            6 :                 added += extendBidiEdges(ec, from, e2);
    1178              :             }
    1179              :         }
    1180           29 :     }
    1181            6 :     if (added > 0) {
    1182            4 :         if (geometryLike) {
    1183            4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
    1184              :         } else {
    1185            4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
    1186              :         }
    1187              :     }
    1188            6 :     return added;
    1189              : }
    1190              : 
    1191              : 
    1192              : void
    1193         1025 : NBRailwayTopologyAnalyzer::updateTurns(NBEdge* edge) {
    1194         1025 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getFromNode(), false);
    1195         1025 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getToNode(), false);
    1196         1025 : }
    1197              : 
    1198              : 
    1199              : double
    1200         6566 : NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
    1201         6566 :     return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
    1202              : }
    1203              : 
    1204              : 
    1205              : void
    1206            4 : NBRailwayTopologyAnalyzer::extendDirectionPriority(NBEdgeCont& ec, bool fromUniDir) {
    1207              :     // if fromUniDir=true, assign priority value for each railway edge:
    1208              :     // 4: edge is unidirectional
    1209              :     // 3: edge is in main direction of bidirectional track
    1210              :     // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
    1211              :     // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
    1212              :     // 0: edge is part of bidirectional track in reverse of main direction
    1213              :     //
    1214              :     // otherwise:
    1215              :     // assign priority value for each railway edge with priority -1 (undefined):
    1216              :     // x: edges with priority >= 0 keep their priority
    1217              :     // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
    1218              :     // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
    1219              :     // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
    1220              :     // x-4 : edge is reverse direction of an x-1 edge
    1221              : 
    1222              :     std::set<NBEdge*, ComparatorIdLess> bidi;
    1223              :     EdgeSet uni;
    1224          174 :     for (NBEdge* edge : ec.getAllEdges()) {
    1225          170 :         if (hasRailway(edge->getPermissions())) {
    1226          170 :             if (fromUniDir) {
    1227           46 :                 if (!edge->isBidiRail()) {
    1228              :                     edge->setPriority(4);
    1229           28 :                     edge->setRoutingType("4");
    1230              :                     uni.insert(edge);
    1231              :                 } else {
    1232              :                     bidi.insert(edge);
    1233              :                 }
    1234              :             } else {
    1235          124 :                 if (edge->getPriority() >= 0) {
    1236              :                     uni.insert(edge);
    1237              :                 } else {
    1238              :                     bidi.insert(edge);
    1239              :                 }
    1240              :             }
    1241              :         }
    1242            4 :     }
    1243              : 
    1244            4 :     if (uni.size() == 0) {
    1245            0 :         if (bidi.size() != 0) {
    1246            0 :             WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
    1247              :         }
    1248              :         return;
    1249              :     }
    1250              :     EdgeSet seen;
    1251              :     EdgeSet check = uni;
    1252              :     EdgeSet forward;
    1253          120 :     while (!check.empty()) {
    1254          116 :         NBEdge* edge = *check.begin();
    1255              :         check.erase(edge);
    1256           48 :         if (seen.count(edge) != 0) {
    1257           48 :             continue;
    1258              :         }
    1259              :         seen.insert(edge);
    1260           68 :         NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
    1261           68 :         if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
    1262              :             forward.insert(straightOut);
    1263              :             check.insert(straightOut);
    1264              :         }
    1265           68 :         NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
    1266           68 :         if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
    1267              :             forward.insert(straightIn);
    1268              :             check.insert(straightIn);
    1269              :         }
    1270              : #ifdef DEBUG_DIRECTION_PRIORITY
    1271              :         std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
    1272              :                   << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
    1273              :                   << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
    1274              :                   << "\n";
    1275              : #endif
    1276              :     }
    1277              : 
    1278          130 :     for (NBEdge* edge : bidi) {
    1279          126 :         NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
    1280              :         int prio;
    1281              :         int bidiPrio;
    1282              :         if (forward.count(edge) != 0) {
    1283              :             if (forward.count(bidiEdge) == 0) {
    1284           12 :                 prio = 3;
    1285           12 :                 bidiPrio = 0;
    1286              :             } else {
    1287              :                 // both forward
    1288           12 :                 prio = 2;
    1289           12 :                 bidiPrio = 2;
    1290              :             }
    1291              :         } else {
    1292              :             if (forward.count(bidiEdge) != 0) {
    1293           25 :                 prio = 0;
    1294           25 :                 bidiPrio = 3;
    1295              :             } else {
    1296              :                 // neither forward
    1297           77 :                 prio = 1;
    1298           77 :                 bidiPrio = 1;
    1299              :             }
    1300              :         }
    1301          126 :         if (bidiEdge == nullptr) {
    1302            6 :             WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
    1303              :         }
    1304          126 :         if (edge->getPriority() >= 0) {
    1305           55 :             bidiPrio = 0;
    1306              :         }
    1307          126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
    1308           69 :             prio = 0;
    1309              :         }
    1310          126 :         if (edge->getPriority() < 0) {
    1311           71 :             edge->setPriority(prio);
    1312          142 :             edge->setRoutingType(toString(prio));
    1313              :         }
    1314          126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
    1315           55 :             bidiEdge->setPriority(bidiPrio);
    1316          110 :             bidiEdge->setRoutingType(toString(bidiPrio));
    1317              :         }
    1318              :     }
    1319              :     std::map<int, int> numPrios;
    1320          130 :     for (NBEdge* edge : bidi) {
    1321          126 :         numPrios[edge->getPriority()]++;
    1322              :     }
    1323            4 :     if (fromUniDir) {
    1324            3 :         WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1325              :     } else {
    1326            9 :         WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1327              :     }
    1328              : }
    1329              : 
    1330              : // ---------------------------------------------------------------------------
    1331              : // NBRailwaySignalGuesser methods
    1332              : // ---------------------------------------------------------------------------
    1333              : 
    1334              : int
    1335         1841 : NBRailwaySignalGuesser::guessRailSignals(NBEdgeCont& ec, NBPTStopCont& sc) {
    1336         1841 :     const OptionsCont& oc = OptionsCont::getOptions();
    1337              :     int addedSignals = 0;
    1338         3682 :     if (oc.exists("railway.signal.guess.by-stops")) {
    1339         3506 :         if (oc.getBool("railway.signal.guess.by-stops")) {
    1340            1 :             const double minLength = oc.getFloat("osm.stop-output.length.train");
    1341            1 :             addedSignals += guessByStops(ec, sc, minLength);
    1342              :         }
    1343              :     }
    1344         1841 :     return addedSignals;
    1345              : }
    1346              : 
    1347              : 
    1348              : bool
    1349            6 : NBRailwaySignalGuesser::canBeSignal(const NBNode* node) {
    1350            6 :     return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
    1351              : }
    1352              : 
    1353              : int
    1354            1 : NBRailwaySignalGuesser::guessByStops(NBEdgeCont& ec, NBPTStopCont& sc, double minLength) {
    1355              :     int addedSignals = 0;
    1356            5 :     for (auto& item : sc.getStops()) {
    1357            4 :         const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
    1358            4 :         if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
    1359              :             NBNode* to = stopEdge->getToNode();
    1360            4 :             if (canBeSignal(to)) {
    1361            2 :                 to->reinit(to->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1362            2 :                 addedSignals++;
    1363              :             }
    1364              :             NBNode* from = stopEdge->getFromNode();
    1365            4 :             if (stopEdge->getLoadedLength() >= minLength) {
    1366              :                 /// XXX should split edge if it is too long
    1367            2 :                 if (canBeSignal(from)) {
    1368            0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1369            0 :                     addedSignals++;
    1370              :                 }
    1371              :             } else {
    1372            2 :                 double searchDist = minLength - stopEdge->getLoadedLength();
    1373            4 :                 while (searchDist > 0 && from->geometryLike()) {
    1374            3 :                     for (const NBEdge* in : from->getIncomingEdges()) {
    1375            3 :                         if (in->getFromNode() != stopEdge->getToNode()) {
    1376              :                             // found edge that isn't a bidi predecessor
    1377              :                             stopEdge = in;
    1378              :                             break;
    1379              :                         }
    1380              :                     }
    1381            2 :                     if (stopEdge->getFromNode() == from) {
    1382              :                         // bidi edge without predecessor
    1383              :                         break;
    1384              :                     } else {
    1385              :                         from = stopEdge->getFromNode();
    1386              :                     }
    1387            2 :                     searchDist -= stopEdge->getLoadedLength();
    1388              :                 }
    1389            2 :                 if (searchDist <= 0 && canBeSignal(from)) {
    1390            0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1391            0 :                     addedSignals++;
    1392              :                 }
    1393              :             }
    1394              :         }
    1395              :     }
    1396            1 :     WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
    1397            1 :     return addedSignals;
    1398              : }
    1399              : 
    1400              : 
    1401              : int
    1402            0 : NBRailwayGeometryHelper::straigthenCorrdidor(NBEdgeCont& ec, double maxAngle) {
    1403              :     int moved = 0;
    1404              :     int numCorridors = 0;
    1405            0 :     std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
    1406              :     std::set<NBNode*> railGeomNodes;
    1407            0 :     for (NBNode* n : railNodes) {
    1408            0 :         if (n->geometryLike()) {
    1409              :             railGeomNodes.insert(n);
    1410              :         }
    1411              :     }
    1412              :     std::set<NBNode*, ComparatorIdLess> kinkNodes;;
    1413            0 :     for (NBNode* n : railGeomNodes) {
    1414            0 :         NBEdge* in = n->getIncomingEdges().front();
    1415            0 :         NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
    1416            0 :         const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
    1417            0 :         if (maxAngle > 0 && relAngle > maxAngle) {
    1418              :             kinkNodes.insert(n);
    1419              :         }
    1420              :     }
    1421            0 :     while (!kinkNodes.empty()) {
    1422              :         std::vector<NBNode*> corridor;
    1423              :         std::vector<NBEdge*> corridorEdges;
    1424            0 :         Boundary corridorBox;
    1425              :         double length = 0;
    1426            0 :         NBNode* n = *kinkNodes.begin();
    1427              :         kinkNodes.erase(kinkNodes.begin());
    1428              :         // go downstream and upstream, add kinkNodes until a "long" enough
    1429              :         // non-kink stretch is found
    1430            0 :         NBEdge* in = n->getIncomingEdges().front();
    1431            0 :         NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
    1432              :         NBEdge* const centerIn = in;
    1433              :         NBEdge* const centerOut = out;
    1434            0 :         NBNode* up = in->getFromNode();
    1435            0 :         NBNode* down = out->getToNode();
    1436            0 :         corridor.push_back(up);
    1437            0 :         corridor.push_back(n);
    1438            0 :         corridor.push_back(down);
    1439            0 :         corridorBox.add(up->getPosition());
    1440            0 :         corridorBox.add(down->getPosition());
    1441            0 :         corridorEdges.push_back(in);
    1442            0 :         corridorEdges.push_back(out);
    1443            0 :         length += in->getLoadedLength();
    1444            0 :         length += out->getLoadedLength();
    1445              :         Position cBeg, cEnd, delta;
    1446              :         while (kinkNodes.count(up) != 0) {
    1447              :             NBEdge* const out2 = in;
    1448            0 :             NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
    1449            0 :             length += in2->getLoadedLength();
    1450            0 :             up = in2->getFromNode();
    1451            0 :             corridor.insert(corridor.begin(), up);
    1452            0 :             corridorEdges.insert(corridorEdges.begin(), in2);
    1453              :             kinkNodes.erase(up);
    1454            0 :             corridorBox.add(up->getPosition());
    1455              :         }
    1456            0 :         cBeg = up->getPosition();
    1457            0 :         cEnd = down->getPosition();
    1458              :         delta = cEnd - cBeg;
    1459            0 :         while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
    1460              :             NBEdge* const out2 = in;
    1461            0 :             NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
    1462            0 :             length += in2->getLoadedLength();
    1463            0 :             up = in2->getFromNode();
    1464            0 :             corridor.insert(corridor.begin(), up);
    1465            0 :             corridorEdges.insert(corridorEdges.begin(), in2);
    1466              :             kinkNodes.erase(up);
    1467            0 :             corridorBox.add(up->getPosition());
    1468            0 :             cBeg = up->getPosition();
    1469            0 :             cEnd = down->getPosition();
    1470              :             delta = cEnd - cBeg;
    1471              :         }
    1472              :         in = centerIn;
    1473              :         out = centerOut;
    1474              :         while (kinkNodes.count(down) != 0) {
    1475              :             NBEdge* const in2 = out;
    1476            0 :             NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
    1477            0 :             down = out2->getToNode();
    1478            0 :             length += out2->getLoadedLength();
    1479            0 :             corridor.push_back(down);
    1480            0 :             corridorEdges.push_back(out2);
    1481              :             kinkNodes.erase(down);
    1482            0 :             corridorBox.add(down->getPosition());
    1483              :         }
    1484            0 :         cBeg = up->getPosition();
    1485            0 :         cEnd = down->getPosition();
    1486              :         delta = cEnd - cBeg;
    1487            0 :         while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
    1488              :             NBEdge* const in2 = out;
    1489            0 :             NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
    1490            0 :             down = out2->getToNode();
    1491            0 :             length += out2->getLoadedLength();
    1492            0 :             corridor.push_back(down);
    1493            0 :             corridorEdges.push_back(out2);
    1494              :             kinkNodes.erase(down);
    1495            0 :             corridorBox.add(down->getPosition());
    1496            0 :             cBeg = up->getPosition();
    1497            0 :             cEnd = down->getPosition();
    1498              :             delta = cEnd - cBeg;
    1499              :         }
    1500              :         // straighten all edges in corridor (corridorEdges doesn't include bidi)
    1501            0 :         std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
    1502            0 :         for (NBNode* n2 : corridorNodes) {
    1503            0 :             for (NBEdge* e : n2->getEdges()) {
    1504              :                 if (corridorNodes.count(e->getFromNode()) != 0
    1505              :                         && corridorNodes.count(e->getToNode()) != 0) {
    1506            0 :                     PositionVector simpleGeom;
    1507            0 :                     simpleGeom.push_back(e->getFromNode()->getPosition());
    1508            0 :                     simpleGeom.push_back(e->getToNode()->getPosition());
    1509            0 :                     e->setGeometry(simpleGeom);
    1510            0 :                 }
    1511              :             }
    1512              :         }
    1513            0 :         if (delta.length2D() > 0) {
    1514              :             double currLength = 0;
    1515            0 :             for (int i = 1; i < (int)corridor.size() - 1; i++) {
    1516            0 :                 currLength += corridorEdges[i - 1]->getLoadedLength();
    1517            0 :                 const Position newPos = cBeg + delta * (currLength / length);
    1518            0 :                 NBNode* const n2 = corridor[i];
    1519            0 :                 n2->reinit(newPos, n2->getType());
    1520            0 :                 for (NBEdge* e : n2->getEdges()) {
    1521            0 :                     e->resetEndpointAtNode(n2);
    1522              :                 }
    1523            0 :                 moved += 1;
    1524              :             }
    1525            0 :             numCorridors += 1;
    1526              :         } else {
    1527            0 :             WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
    1528              :         }
    1529            0 :     }
    1530              :     //std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
    1531              : 
    1532            0 :     WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
    1533            0 :     return moved;
    1534              : }
    1535              : 
    1536              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1