LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms_Railway.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 84.1 % 744 626
Test Date: 2026-03-26 16:31:35 Functions: 93.9 % 33 31

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

Generated by: LCOV version 2.0-1