LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms_Railway.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 531 583 91.1 %
Date: 2024-05-06 15:32:35 Functions: 27 29 93.1 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2012-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    NBAlgorithms_Railway.cpp
      15             : /// @author  Jakob Erdmann
      16             : /// @author  Melanie Weber
      17             : /// @date    29. March 2018
      18             : ///
      19             : // Algorithms for highway on-/off-ramps computation
      20             : /****************************************************************************/
      21             : #include <config.h>
      22             : 
      23             : #include <cassert>
      24             : #include <utils/options/OptionsCont.h>
      25             : #include <utils/common/MsgHandler.h>
      26             : #include <utils/common/ToString.h>
      27             : #include <utils/common/StringUtils.h>
      28             : #include <utils/iodevices/OutputDevice.h>
      29             : #include <utils/iodevices/OutputDevice_String.h>
      30             : #include <utils/router/DijkstraRouter.h>
      31             : #include "NBNetBuilder.h"
      32             : #include "NBAlgorithms.h"
      33             : #include "NBNodeCont.h"
      34             : #include "NBEdgeCont.h"
      35             : #include "NBNode.h"
      36             : #include "NBEdge.h"
      37             : #include "NBPTStop.h"
      38             : #include "NBVehicle.h"
      39             : #include "NBAlgorithms_Railway.h"
      40             : 
      41             : //#define DEBUG_SEQSTOREVERSE
      42             : //#define DEBUG_DIRECTION_PRIORITY
      43             : 
      44             : #define DEBUGNODEID  "gneJ34"
      45             : #define DEBUGNODEID2  "28842974"
      46             : #define DEBUGEDGEID  "22820560#0"
      47             : #define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
      48             : 
      49             : #define SHARP_THRESHOLD_SAMEDIR 100
      50             : #define SHARP_THRESHOLD 80
      51             : 
      52             : 
      53             : // ===========================================================================
      54             : // method definitions
      55             : // ===========================================================================
      56             : // ---------------------------------------------------------------------------
      57             : // Track methods
      58             : // ---------------------------------------------------------------------------
      59             : void
      60       34162 : NBRailwayTopologyAnalyzer::Track::addSuccessor(Track* track) {
      61       34162 :     successors.push_back(track);
      62       34162 :     viaSuccessors.push_back(std::make_pair(track, nullptr));
      63       34162 :     minPermissions &= track->edge->getPermissions();
      64       34162 : }
      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             :         }
      81           0 :         return svcSuccessors[svc];
      82             :     }
      83             : }
      84             : 
      85             : 
      86             : const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
      87       17371 : NBRailwayTopologyAnalyzer::Track::getViaSuccessors(SUMOVehicleClass svc, bool /*ignoreTransientPermissions*/) const {
      88       17371 :     if ((minPermissions & svc) != 0) {
      89       17371 :         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          37 : NBRailwayTopologyAnalyzer::repairTopology(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
     115          37 :     const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
     116             :     int addedBidi = 0;
     117          37 :     if (!minimal) {
     118          34 :         addedBidi += extendBidiEdges(ec);
     119          34 :         addedBidi += reverseEdges(ec, sc); // technically not bidi but new edges nevertheless
     120          34 :         addedBidi += addBidiEdgesForBufferStops(ec);
     121          34 :         addedBidi += addBidiEdgesBetweenSwitches(ec);
     122             :     }
     123          37 :     if (lc.getLines().size() > 0) {
     124          29 :         addedBidi += addBidiEdgesForStops(ec, lc, sc, minimal);
     125             :     }
     126          74 :     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          37 :     return addedBidi;
     132             : }
     133             : 
     134             : 
     135             : int
     136           6 : NBRailwayTopologyAnalyzer::makeAllBidi(NBEdgeCont& ec) {
     137           6 :     int numNotCenterEdges = 0;
     138           6 :     int numAddedBidiEdges = 0;
     139          12 :     std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
     140             :     std::vector<NBEdge*> edges;
     141           6 :     if (inputfile == "") {
     142          46 :         for (NBEdge* edge : ec.getAllEdges()) {
     143          41 :             edges.push_back(edge);
     144             :         }
     145             :     } else {
     146             :         std::set<std::string> edgeIDs;
     147           1 :         NBHelpers::loadEdgesFromFile(inputfile, edgeIDs);
     148          26 :         for (const std::string& edgeID : edgeIDs) {
     149          25 :             NBEdge* edge = ec.retrieve(edgeID);
     150          25 :             if (edge != nullptr) {
     151          11 :                 edges.push_back(edge);
     152             :             }
     153             :         }
     154             :     }
     155          58 :     for (NBEdge* edge : edges) {
     156          52 :         if (hasRailway(edge->getPermissions())) {
     157             :             // rebuild connections if given from an earlier network
     158          52 :             edge->invalidateConnections(true);
     159          52 :             if (!edge->isBidiRail()) {
     160          48 :                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     161          48 :                     NBEdge* e2 = addBidiEdge(ec, edge, false);
     162          48 :                     if (e2 != nullptr) {
     163          48 :                         numAddedBidiEdges++;
     164             :                     }
     165             :                 } else {
     166           0 :                     numNotCenterEdges++;
     167             :                 }
     168             :             }
     169             :         }
     170             :     }
     171          12 :     WRITE_MESSAGEF(TL("Added % bidi-edges to ensure that all tracks are usable in both directions."), toString(numAddedBidiEdges));
     172           6 :     if (numNotCenterEdges) {
     173           0 :         WRITE_WARNINGF(TL("Ignore % edges because they have the wrong spreadType"), toString(numNotCenterEdges));
     174             :     }
     175          12 :     return numAddedBidiEdges;
     176             : }
     177             : 
     178             : 
     179             : NBEdge*
     180        1368 : NBRailwayTopologyAnalyzer::addBidiEdge(NBEdgeCont& ec, NBEdge* edge, bool update) {
     181             :     assert(edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER);
     182             :     assert(!edge->isBidiRail());
     183        1368 :     const std::string id2 = (edge->getID()[0] == '-'
     184           3 :                              ? edge->getID().substr(1)
     185        1371 :                              : "-" + edge->getID());
     186        1368 :     if (ec.wasIgnored(id2)) {
     187             :         // we had it before so the warning is already there
     188             :         return nullptr;
     189             :     }
     190        1365 :     if (ec.retrieve(id2) == nullptr) {
     191             :         NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
     192        1365 :                                 edge, edge->getGeometry().reverse());
     193        2730 :         if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     194         806 :             e2->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     195             :         }
     196        1365 :         ec.insert(e2);
     197        1365 :         if (ec.retrieve(id2) == nullptr) {
     198           3 :             WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
     199           1 :             return nullptr;
     200             :         }
     201        1364 :         if (update) {
     202        1316 :             updateTurns(edge);
     203             :             // reconnected added edges
     204        3950 :             for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
     205        2634 :                 if (hasRailway(incoming->getPermissions())) {
     206        2300 :                     incoming->invalidateConnections(true);
     207             :                 }
     208             :             }
     209             :         }
     210        1364 :         return e2;
     211             :     } else {
     212           0 :         WRITE_WARNINGF(TL("Could not add bidi-edge '%'."), id2);
     213           0 :         return nullptr;
     214             :     }
     215             : }
     216             : 
     217             : 
     218             : void
     219       23677 : NBRailwayTopologyAnalyzer::getRailEdges(const NBNode* node,
     220             :                                         EdgeVector& inEdges, EdgeVector& outEdges) {
     221       64537 :     for (NBEdge* e : node->getIncomingEdges()) {
     222       40860 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     223       37340 :             inEdges.push_back(e);
     224             :         }
     225             :     }
     226       64064 :     for (NBEdge* e : node->getOutgoingEdges()) {
     227       40387 :         if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     228       36880 :             outEdges.push_back(e);
     229             :         }
     230             :     }
     231       23677 : }
     232             : 
     233             : 
     234             : std::set<NBNode*>
     235         137 : NBRailwayTopologyAnalyzer::getBrokenRailNodes(NBEdgeCont& ec, bool verbose) {
     236             :     std::set<NBNode*> brokenNodes;
     237         137 :     OutputDevice& device = OutputDevice::getDevice(verbose
     238         303 :                            ? OptionsCont::getOptions().getString("railway.topology.output")
     239             :                            : "/dev/null");
     240             : 
     241         274 :     device.writeXMLHeader("railwayTopology", "");
     242         137 :     std::set<NBNode*> railNodes = getRailNodes(ec, verbose);
     243             :     std::map<std::pair<int, int>, std::set<NBNode*, ComparatorIdLess> > types;
     244             :     std::set<NBEdge*, ComparatorIdLess> bidiEdges;
     245             :     std::set<NBEdge*, ComparatorIdLess> bufferStops;
     246        7665 :     for (NBNode* node : railNodes) {
     247             :         EdgeVector inEdges, outEdges;
     248        7528 :         getRailEdges(node, inEdges, outEdges);
     249       15056 :         types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
     250       18721 :         for (NBEdge* e : outEdges) {
     251       13610 :             if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
     252        4941 :                 NBEdge* primary = e;
     253        4941 :                 NBEdge* secondary = e->getTurnDestination(true);
     254        4941 :                 if (e->getID()[0] == '-') {
     255             :                     std::swap(primary, secondary);
     256        3685 :                 } else if (primary->getID()[0] != '-' && secondary->getID()[0] != '-' && secondary->getID() < primary->getID()) {
     257             :                     std::swap(primary, secondary);
     258             :                 }
     259             :                 if (bidiEdges.count(secondary) == 0) {
     260             :                     // avoid duplicate when both ids start with '-'
     261             :                     bidiEdges.insert(primary);
     262             :                 }
     263             :             }
     264             :         }
     265             :     }
     266             : 
     267         137 :     int numBrokenA = 0;
     268         137 :     int numBrokenB = 0;
     269         137 :     int numBrokenC = 0;
     270         137 :     int numBrokenD = 0;
     271         137 :     int numBufferStops = 0;
     272         137 :     if (verbose && types.size() > 0) {
     273          58 :         WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
     274             :     }
     275         137 :     device.openTag("legend");
     276         274 :     device.openTag("error");
     277             :     device.writeAttr(SUMO_ATTR_ID, "a");
     278         137 :     device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
     279         137 :     device.closeTag();
     280         274 :     device.openTag("error");
     281             :     device.writeAttr(SUMO_ATTR_ID, "b");
     282         137 :     device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
     283         137 :     device.closeTag();
     284         274 :     device.openTag("error");
     285             :     device.writeAttr(SUMO_ATTR_ID, "c");
     286         137 :     device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
     287         137 :     device.closeTag();
     288         274 :     device.openTag("error");
     289             :     device.writeAttr(SUMO_ATTR_ID, "d");
     290         137 :     device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
     291         137 :     device.closeTag();
     292         274 :     device.closeTag();
     293             : 
     294         768 :     for (auto it : types) {
     295         631 :         int numBrokenType = 0;
     296         631 :         device.openTag("railNodeType");
     297         631 :         int in = it.first.first;
     298         631 :         int out = it.first.second;
     299         631 :         device.writeAttr("in", in);
     300        1262 :         device.writeAttr("out", out);
     301        8159 :         for (NBNode* n : it.second) {
     302        7528 :             device.openTag(SUMO_TAG_NODE);
     303        7528 :             device.writeAttr(SUMO_ATTR_ID, n->getID());
     304             :             EdgeVector inRail, outRail;
     305        7528 :             getRailEdges(n, inRail, outRail);
     306             :             // check if there is a mismatch between angle and edge direction
     307             :             // (see above)
     308             : 
     309        7528 :             std::string broken = "";
     310        7528 :             if (in < 2 && hasStraightPair(n, outRail, outRail)) {
     311             :                 broken += "a";
     312         112 :                 numBrokenA++;
     313             :             }
     314        7528 :             if (out < 2 && hasStraightPair(n, inRail, inRail)) {
     315             :                 broken += "b";
     316         173 :                 numBrokenB++;
     317             :             }
     318        7528 :             if (out > 0) {
     319       17752 :                 for (NBEdge* e : inRail) {
     320             :                     EdgeVector tmp;
     321       10706 :                     tmp.push_back(e);
     322       10706 :                     if (allSharp(n, tmp, outRail)) {
     323             :                         broken += "c";
     324         108 :                         numBrokenC++;
     325             :                         break;
     326             :                     }
     327             :                 }
     328             :             }
     329        7528 :             if (in > 0) {
     330       17720 :                 for (NBEdge* e : outRail) {
     331             :                     EdgeVector tmp;
     332       10689 :                     tmp.push_back(e);
     333       10689 :                     if (allSharp(n, inRail, tmp)) {
     334             :                         broken += "d";
     335          79 :                         numBrokenD++;
     336             :                         break;
     337             :                     }
     338             :                 }
     339             :             }
     340             :             // do not mark bidi nodes as broken
     341        7528 :             if (((in == 1 && out == 1) || (in == 2 && out == 2))
     342       13674 :                     && allBidi(inRail) && allBidi(outRail)) {
     343             :                 broken = "";
     344             :             }
     345             : 
     346        7528 :             if (broken.size() > 0) {
     347         574 :                 device.writeAttr("broken", broken);
     348             :                 brokenNodes.insert(n);
     349         287 :                 numBrokenType++;
     350             :             }
     351       15056 :             if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
     352          48 :                 device.writeAttr("buffer_stop", "true");
     353          48 :                 numBufferStops++;
     354             :             }
     355       15056 :             device.closeTag();
     356             :         }
     357         631 :         device.closeTag();
     358         631 :         if (verbose) {
     359         250 :             WRITE_MESSAGE("   " + toString(it.first.first) + "," + toString(it.first.second)
     360             :                           + " count: " + toString(it.second.size()) + " broken: " + toString(numBrokenType));
     361             :         }
     362             : 
     363             :     }
     364         137 :     if (verbose) {
     365          58 :         WRITE_MESSAGE("Found " + toString(brokenNodes.size()) + " broken railway nodes "
     366             :                       + "(A=" + toString(numBrokenA)
     367             :                       + " B=" + toString(numBrokenB)
     368             :                       + " C=" + toString(numBrokenC)
     369             :                       + " D=" + toString(numBrokenD)
     370             :                       + ")");
     371          58 :         WRITE_MESSAGEF(TL("Found % railway nodes marked as buffer_stop"), toString(numBufferStops));
     372             :     }
     373             : 
     374        3816 :     for (NBEdge* e : bidiEdges) {
     375        3679 :         device.openTag("bidiEdge");
     376        3679 :         device.writeAttr(SUMO_ATTR_ID, e->getID());
     377        3679 :         device.writeAttr("bidi", e->getTurnDestination(true)->getID());
     378        7358 :         device.closeTag();
     379             :     }
     380         137 :     if (verbose) {
     381          58 :         WRITE_MESSAGEF(TL("Found % bidirectional rail edges"), toString(bidiEdges.size()));
     382             :     }
     383             : 
     384         137 :     device.close();
     385         137 :     return brokenNodes;
     386             : }
     387             : 
     388             : 
     389             : std::set<NBNode*>
     390         200 : NBRailwayTopologyAnalyzer::getRailNodes(NBEdgeCont& ec, bool verbose) {
     391             :     std::set<NBNode*> railNodes;
     392         200 :     int numRailEdges = 0;
     393       71712 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     394       71512 :         if (hasRailway(it->second->getPermissions())) {
     395       18719 :             numRailEdges++;
     396       18719 :             railNodes.insert(it->second->getFromNode());
     397       18719 :             railNodes.insert(it->second->getToNode());
     398             :         }
     399             :     }
     400         200 :     int numRailSignals = 0;
     401       12786 :     for (const NBNode* const node : railNodes) {
     402       12586 :         if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
     403        2762 :             numRailSignals++;
     404             :         }
     405             :     }
     406         200 :     if (verbose) {
     407          58 :         WRITE_MESSAGEF(TL("Found % railway edges and % railway nodes (% signals)."), toString(numRailEdges), toString(railNodes.size()), toString(numRailSignals));
     408             :     }
     409         200 :     return railNodes;
     410             : }
     411             : 
     412             : 
     413             : bool
     414       51843 : NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
     415       51843 :     const double relAngle = NBHelpers::normRelAngle(e1->getAngleAtNode(node), e2->getAngleAtNode(node));
     416             :     /*
     417             :     std::cout << "  isStraight n=" << node->getID()
     418             :         << " e1=" << e1->getID()
     419             :         << " e2=" << e2->getID()
     420             :         << " a1=" << e1->getAngleAtNode(node)
     421             :         << " a2=" << e2->getAngleAtNode(node)
     422             :         << " rel=" << relAngle
     423             :         << "\n";
     424             :         */
     425       40998 :     if ((e1->getToNode() == node && e2->getFromNode() == node)
     426       55167 :             || (e1->getFromNode() == node && e2->getToNode() == node)) {
     427             :         // edges go in the same direction
     428       45197 :         return fabs(relAngle) < SHARP_THRESHOLD;
     429             :     } else {
     430             :         // edges go in the opposite direction (both incoming or outgoing)
     431        6646 :         return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
     432             :     }
     433             : }
     434             : 
     435             : 
     436             : bool
     437        7091 : NBRailwayTopologyAnalyzer::hasStraightPair(const NBNode* node, const EdgeVector& edges,
     438             :         const EdgeVector& edges2) {
     439             : #ifdef DEBUG_SEQSTOREVERSE
     440             :     //if (node->getID() == DEBUGNODEID2) {
     441             :     //    std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
     442             :     //}
     443             : #endif
     444       13531 :     for (NBEdge* e1 : edges) {
     445       14161 :         for (NBEdge* e2 : edges2) {
     446             :             //if (e1->getID() == "195411601#2" && e2->getID() == "93584120#3") {
     447             :             //    std::cout
     448             :             //        << " DEBUG normRelA=" << NBHelpers::normRelAngle(
     449             :             //                    e1->getAngleAtNode(node),
     450             :             //                    e2->getAngleAtNode(node))
     451             :             //        << "\n";
     452             :             //}
     453        7721 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     454             :                 return true;
     455             :             }
     456             :         }
     457             :     }
     458             :     return false;
     459             : }
     460             : 
     461             : 
     462             : bool
     463         136 : NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
     464         186 :     for (NBEdge* e : in) {
     465         119 :         if (e != candOut && isStraight(node, e, candOut)) {
     466          69 :             if (gDebugFlag1) {
     467           0 :                 std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
     468             :             }
     469             :             return false;
     470             :         }
     471             :     }
     472         195 :     for (NBEdge* e : out) {
     473         134 :         if (e != candOut && !isStraight(node, e, candOut)) {
     474           6 :             if (gDebugFlag1) {
     475           0 :                 std::cout << " isSharp e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
     476             :             }
     477             :             return false;
     478             :         }
     479             :     }
     480             :     return true;
     481             : }
     482             : 
     483             : 
     484             : bool
     485       22750 : NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
     486             :     bool allBidi = true;
     487       28538 :     for (NBEdge* e1 : in) {
     488       36171 :         for (NBEdge* e2 : out) {
     489       30383 :             if (e1 != e2 && isStraight(node, e1, e2)) {
     490             :                 return false;
     491             :             }
     492        9567 :             if (!e1->isBidiRail(true)) {
     493             :                 //std::cout << " allSharp node=" << node->getID() << " e1=" << e1->getID() << " is not bidi\n";
     494             :                 allBidi = false;
     495             :             }
     496             :         }
     497             :     }
     498        1934 :     return !allBidi || countBidiAsSharp;
     499             : }
     500             : 
     501             : 
     502             : bool
     503        9782 : NBRailwayTopologyAnalyzer::allBidi(const EdgeVector& edges) {
     504       23958 :     for (NBEdge* e : edges) {
     505       16686 :         if (!e->isBidiRail()) {
     506             :             return false;
     507             :         }
     508             :     }
     509             :     return true;
     510             : }
     511             : 
     512             : 
     513             : int
     514          37 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec) {
     515          37 :     int added = 0;
     516       13823 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     517       13786 :         NBEdge* e = it->second;
     518       13786 :         if (e->isBidiRail()) {
     519        2000 :             added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
     520        2000 :             added += extendBidiEdges(ec, e->getToNode(), e);
     521             :         }
     522             :     }
     523          37 :     if (added > 0) {
     524          26 :         WRITE_MESSAGEF(TL("Added % bidi-edges as extension of existing bidi edges."), toString(added));
     525             :     }
     526          37 :     return added;
     527             : }
     528             : 
     529             : 
     530             : int
     531        5310 : NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec, NBNode* node, NBEdge* bidiIn) {
     532             :     assert(bidiIn->getToNode() == node);
     533        5310 :     NBEdge* bidiOut = bidiIn->getTurnDestination(true);
     534        5310 :     if (bidiOut == nullptr) {
     535           0 :         WRITE_WARNINGF(TL("Could not find bidi-edge for edge '%'"), bidiIn->getID());
     536           0 :         return 0;
     537             :     }
     538             :     EdgeVector tmpBidiOut;
     539        5310 :     tmpBidiOut.push_back(bidiOut);
     540             :     EdgeVector tmpBidiIn;
     541        5310 :     tmpBidiIn.push_back(bidiIn);
     542             :     int added = 0;
     543             :     EdgeVector inRail, outRail;
     544        5310 :     getRailEdges(node, inRail, outRail);
     545       15149 :     for (NBEdge* cand : outRail) {
     546             :         //std::cout << " extendBidiEdges n=" << node->getID() << " bidiIn=" << bidiIn->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, bidiIn, cand) <<  " allSharp=" << allSharp(node, inRail, tmpBidiOut, true) << "\n";
     547       10392 :         if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
     548         495 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     549       10326 :                 && allSharp(node, inRail, tmpBidiOut, true)) {
     550         442 :             NBEdge* e2 = addBidiEdge(ec, cand);
     551         442 :             if (e2 != nullptr) {
     552         442 :                 added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
     553             :             }
     554             :         }
     555             :     }
     556       15506 :     for (NBEdge* cand : inRail) {
     557             :         //std::cout << " extendBidiEdges n=" << node->getID() << " bidiOut=" << bidiOut->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, cand, bidiOut) << " allSharp=" << allSharp(node, outRail, tmpBidiIn, true) << "\n";
     558       11106 :         if (!cand->isBidiRail() && isStraight(node, cand, bidiOut)
     559         877 :                 && cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
     560       11064 :                 && allSharp(node, outRail, tmpBidiIn, true)) {
     561         820 :             NBEdge* e2 = addBidiEdge(ec, cand);
     562         820 :             if (e2 != nullptr) {
     563         818 :                 added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
     564             :             }
     565             :         }
     566             :     }
     567             :     return added;
     568             : }
     569             : 
     570             : 
     571             : int
     572          34 : NBRailwayTopologyAnalyzer::reverseEdges(NBEdgeCont& ec, NBPTStopCont& sc) {
     573          34 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     574             :     // find reversible edge sequences between broken nodes
     575             :     std::vector<EdgeVector> seqsToReverse;
     576         130 :     for (NBNode* n : brokenNodes) {
     577             :         EdgeVector inRail, outRail;
     578          96 :         getRailEdges(n, inRail, outRail);
     579         216 :         for (NBEdge* start : outRail) {
     580             :             EdgeVector tmp;
     581         120 :             tmp.push_back(start);
     582             :             // only reverse edges where the node would be unbroken afterwards
     583         192 :             if (!allBroken(n, start, inRail, outRail)
     584         120 :                     || (inRail.size() == 1 && outRail.size() == 1)) {
     585             : #ifdef DEBUG_SEQSTOREVERSE
     586             :                 if (n->getID() == DEBUGNODEID) {
     587             :                     std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
     588             :                 }
     589             : #endif
     590             :                 continue;
     591             :             }
     592             :             //std::cout << " get sequences from " << start->getID() << "\n";
     593             :             bool forward = true;
     594             :             EdgeVector seq;
     595         188 :             while (forward) {
     596         140 :                 seq.push_back(start);
     597             :                 //std::cout << " seq=" << toString(seq) << "\n";
     598         140 :                 NBNode* n2 = start->getToNode();
     599             :                 EdgeVector inRail2, outRail2;
     600         140 :                 getRailEdges(n2, inRail2, outRail2);
     601             :                 if (brokenNodes.count(n2) != 0) {
     602             :                     EdgeVector tmp2;
     603          16 :                     tmp2.push_back(start);
     604          16 :                     if (allBroken(n2, start, outRail2, inRail2)) {
     605          13 :                         seqsToReverse.push_back(seq);
     606             :                     } else {
     607             : #ifdef DEBUG_SEQSTOREVERSE
     608             :                         if (n->getID() == DEBUGNODEID) {
     609             :                             std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
     610             :                         }
     611             : #endif
     612             :                     }
     613             :                     forward = false;
     614             :                 } else {
     615         124 :                     if (outRail2.size() == 0) {
     616             :                         // stop at network border
     617             :                         forward = false;
     618             : #ifdef DEBUG_SEQSTOREVERSE
     619             :                         if (n->getID() == DEBUGNODEID) {
     620             :                             std::cout << " abort at n2=" << n2->getID() << " (border)\n";
     621             :                         }
     622             : #endif
     623         106 :                     } else if (outRail2.size() > 1 || inRail2.size() > 1) {
     624             :                         // stop at switch
     625             :                         forward = false;
     626             : #ifdef DEBUG_SEQSTOREVERSE
     627             :                         if (n->getID() == DEBUGNODEID) {
     628             :                             std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
     629             :                         }
     630             : #endif
     631             :                     } else {
     632          92 :                         start = outRail2.front();
     633             :                     }
     634             :                 }
     635             :             }
     636             :         }
     637             :     }
     638             :     // sort by sequence length
     639          34 :     if (seqsToReverse.size() > 0) {
     640          10 :         WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
     641             :     }
     642          34 :     std::sort(seqsToReverse.begin(), seqsToReverse.end(),
     643             :     [](const EdgeVector & a, const EdgeVector & b) {
     644             :         return a.size() < b.size();
     645             :     });
     646          34 :     int numReversed = 0;
     647             :     std::set<NBNode*> affectedEndpoints;
     648             :     std::set<std::string> reversedIDs;
     649             :     std::map<int, int> seqLengths;
     650          47 :     for (EdgeVector& seq : seqsToReverse) {
     651          13 :         NBNode* seqStart = seq.front()->getFromNode();
     652          13 :         NBNode* seqEnd = seq.back()->getToNode();
     653             :         // avoid reversing sequenes on both sides of a broken node
     654             :         if (affectedEndpoints.count(seqStart) == 0
     655             :                 && affectedEndpoints.count(seqEnd) == 0) {
     656             :             affectedEndpoints.insert(seqStart);
     657             :             affectedEndpoints.insert(seqEnd);
     658             :             //WRITE_MESSAGE("  reversed seq=" + toString(seq));
     659          61 :             for (NBEdge* e : seq) {
     660          48 :                 e->reinitNodes(e->getToNode(), e->getFromNode());
     661          48 :                 e->setGeometry(e->getGeometry().reverse());
     662          48 :                 reversedIDs.insert(e->getID());
     663          96 :                 if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
     664           0 :                     e->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     665             :                 }
     666             :             }
     667          13 :             seqLengths[(int)seq.size()]++;
     668          13 :             numReversed++;
     669             :         }
     670             :     }
     671          34 :     if (numReversed > 0) {
     672           5 :         WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
     673         271 :         for (auto& item : sc.getStops()) {
     674         266 :             if (reversedIDs.count(item.second->getEdgeId())) {
     675           4 :                 item.second->findLaneAndComputeBusStopExtent(ec);
     676             :             }
     677             :         }
     678             :     }
     679          68 :     return numReversed;
     680          34 : }
     681             : 
     682             : 
     683             : int
     684          34 : NBRailwayTopologyAnalyzer::addBidiEdgesForBufferStops(NBEdgeCont& ec) {
     685          34 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     686          34 :     std::set<NBNode*> railNodes = getRailNodes(ec);
     687             :     // find buffer stops and ensure that thay are connect to the network in both directions
     688          34 :     int numBufferStops = 0;
     689          34 :     int numAddedBidiTotal = 0;
     690        2386 :     for (NBNode* node : railNodes) {
     691        4704 :         if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
     692          13 :             if (node->getEdges().size() != 1) {
     693           3 :                 WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
     694           1 :                 continue;
     695             :             }
     696             :             // int numAddedBidi = 0;
     697          12 :             numBufferStops++;
     698             :             NBEdge* prev = nullptr;
     699             :             NBEdge* prev2 = nullptr;
     700             :             EdgeVector inRail, outRail;
     701          12 :             getRailEdges(node, inRail, outRail);
     702             :             bool addAway = true; // add new edges away from buffer stop
     703          29 :             while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
     704             :                 NBEdge* e = nullptr;
     705          17 :                 if (prev == nullptr) {
     706             :                     assert(node->getEdges().size() == 1);
     707          12 :                     e = node->getEdges().front();
     708          12 :                     addAway = node == e->getToNode();
     709             :                 } else {
     710           5 :                     if (addAway) {
     711             :                         // XXX if node is broken we need to switch direction
     712             :                         assert(inRail.size() == 2);
     713           4 :                         e = inRail.front() == prev2 ? inRail.back() : inRail.front();
     714             :                     } else {
     715             :                         // XXX if node is broken we need to switch direction
     716             :                         assert(outRail.size() == 2);
     717           1 :                         e = outRail.front() == prev2 ? outRail.back() : outRail.front();
     718             :                     }
     719             :                 }
     720          17 :                 e->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
     721             :                 NBNode* e2From = nullptr;
     722             :                 NBNode* e2To = nullptr;
     723          17 :                 if (addAway) {
     724             :                     e2From = node;
     725             :                     e2To = e->getFromNode();
     726             :                     node = e2To;
     727             :                 } else {
     728             :                     e2From = e->getToNode();
     729             :                     e2To = node;
     730             :                     node = e2From;
     731             :                 }
     732          17 :                 NBEdge* e2 = addBidiEdge(ec, e);
     733          17 :                 if (e2 == nullptr) {
     734             :                     break;
     735             :                 }
     736             :                 prev = e;
     737             :                 prev2 = e2;
     738             :                 // numAddedBidi++;
     739          17 :                 numAddedBidiTotal++;
     740             :                 inRail.clear();
     741             :                 outRail.clear();
     742          17 :                 getRailEdges(node, inRail, outRail);
     743             :             }
     744             :             //if (numAddedBidi > 0) {
     745             :             //    WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
     746             :             //}
     747             :         }
     748             :     }
     749          34 :     if (numAddedBidiTotal > 0) {
     750          12 :         WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
     751             :     }
     752          68 :     return numAddedBidiTotal;
     753             : }
     754             : 
     755             : NBEdge*
     756         118 : NBRailwayTopologyAnalyzer::isBidiSwitch(const NBNode* n) {
     757             :     EdgeVector inRail, outRail;
     758         118 :     getRailEdges(n, inRail, outRail);
     759         118 :     if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
     760          34 :         if (isStraight(n, inRail.front(), outRail.front())) {
     761          26 :             return inRail.front();
     762           8 :         } else if (isStraight(n, inRail.back(), outRail.front())) {
     763           8 :             return inRail.back();
     764             :         }
     765             :     }
     766          84 :     if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
     767          20 :         if (isStraight(n, outRail.front(), inRail.front())) {
     768          12 :             return outRail.front();
     769           8 :         } else if (isStraight(n, outRail.back(), inRail.front())) {
     770           8 :             return outRail.back();
     771             :         }
     772             :     }
     773             :     return nullptr;
     774             : }
     775             : 
     776             : 
     777             : int
     778          34 : NBRailwayTopologyAnalyzer::addBidiEdgesBetweenSwitches(NBEdgeCont& ec) {
     779          34 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
     780             :     std::map<int, int> seqLengths;
     781          34 :     int numAdded = 0;
     782          34 :     int numSeqs = 0;
     783         106 :     for (NBNode* n : brokenNodes) {
     784          72 :         NBEdge* edge = isBidiSwitch(n);
     785          72 :         if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     786             :             std::vector<NBNode*> nodeSeq;
     787             :             EdgeVector edgeSeq;
     788          46 :             NBNode* prev = n;
     789          46 :             nodeSeq.push_back(prev);
     790          46 :             edgeSeq.push_back(edge);
     791             :             bool forward = true;
     792             :             //std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
     793             :             // find a suitable end point for adding bidi edges
     794         164 :             while (forward) {
     795         118 :                 NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
     796             :                 EdgeVector allRail;
     797         118 :                 getRailEdges(next, allRail, allRail);
     798         118 :                 if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
     799          72 :                     prev = next;
     800          72 :                     edge = allRail.front() == edge ? allRail.back() : allRail.front();
     801          72 :                     nodeSeq.push_back(prev);
     802          72 :                     edgeSeq.push_back(edge);
     803             :                 } else {
     804             :                     forward = false;
     805             :                     EdgeVector inRail2, outRail2;
     806          46 :                     getRailEdges(next, inRail2, outRail2);
     807          46 :                     if (isBidiSwitch(next) == edge) {
     808             :                         // suitable switch found as endpoint, add reverse edges
     809             :                         //WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
     810             :                         //        + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
     811          10 :                         for (NBEdge* e : edgeSeq) {
     812           6 :                             addBidiEdge(ec, e);
     813             :                         }
     814           4 :                         seqLengths[(int)edgeSeq.size()]++;
     815           4 :                         numSeqs++;
     816           4 :                         numAdded += (int)edgeSeq.size();
     817             :                     } else {
     818             :                         //std::cout << " sequence ended at junction " << next->getID()
     819             :                         //    << " in=" << inRail2.size()
     820             :                         //    << " out=" << outRail2.size()
     821             :                         //    << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
     822             :                         //    << "\n";
     823             :                     }
     824             : 
     825             :                 }
     826             :             }
     827             : 
     828             :         }
     829             :     }
     830          34 :     if (seqLengths.size() > 0) {
     831           4 :         WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
     832             :     }
     833          68 :     return numAdded;
     834             : }
     835             : 
     836             : 
     837             : std::set<NBPTLine*>
     838          29 : NBRailwayTopologyAnalyzer::findBidiCandidates(NBPTLineCont& lc) {
     839             :     std::set<NBPTLine*>  result;
     840             :     std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
     841         269 :     for (const auto& item : lc.getLines()) {
     842         240 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     843         240 :         if (stops.size() > 1) {
     844         936 :             for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     845             :                 std::shared_ptr<NBPTStop> fromStop = *it;
     846             :                 std::shared_ptr<NBPTStop> toStop = *(it + 1);
     847         722 :                 visited.insert({fromStop, toStop});
     848             :             }
     849             :         }
     850             :     }
     851         269 :     for (const auto& item : lc.getLines()) {
     852         240 :         const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
     853         240 :         if (stops.size() > 1) {
     854         886 :             for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     855             :                 std::shared_ptr<NBPTStop> fromStop = *it;
     856             :                 std::shared_ptr<NBPTStop> toStop = *(it + 1);
     857             :                 std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
     858             :                 if (visited.count(reverseTrip)) {
     859          10 :                     result.insert(item.second);
     860             :                     break;
     861             :                 }
     862         682 :             }
     863             :         }
     864             :     }
     865          29 :     return result;
     866             : }
     867             : 
     868             : int
     869          29 : NBRailwayTopologyAnalyzer::addBidiEdgesForStops(NBEdgeCont& ec, NBPTLineCont& lc, NBPTStopCont& sc, bool minimal) {
     870          58 :     const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
     871             :     // generate bidirectional routing graph
     872             :     std::vector<Track*> tracks;
     873       14599 :     for (NBEdge* edge : ec.getAllEdges()) {
     874       14570 :         tracks.push_back(new Track(edge));
     875             :     }
     876          29 :     const int numEdges = (int)tracks.size();
     877       14599 :     for (NBEdge* edge : ec.getAllEdges()) {
     878       28874 :         tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
     879             :     }
     880             :     // add special tracks for starting end ending in both directions
     881             :     std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
     882       14599 :     for (NBEdge* edge : ec.getAllEdges()) {
     883       14570 :         if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     884        3995 :             Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
     885        3995 :             tracks.push_back(start);
     886        3995 :             Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
     887        3995 :             tracks.push_back(end);
     888        3995 :             stopTracks[edge] = {start, end};
     889             :         }
     890             :     }
     891             :     // set successors based on angle (connections are not yet built)
     892        2735 :     for (NBNode* node : getRailNodes(ec)) {
     893             :         EdgeVector railEdges;
     894        2706 :         getRailEdges(node, railEdges, railEdges);
     895       10695 :         for (NBEdge* e1 : railEdges) {
     896       34552 :             for (NBEdge* e2 : railEdges) {
     897       26563 :                 if (e1 != e2 && isStraight(node, e1, e2)) {
     898       13012 :                     int i = e1->getNumericalID();
     899       13012 :                     int i2 = e2->getNumericalID();
     900       13012 :                     if (e1->getToNode() == node) {
     901        6525 :                         if (e2->getFromNode() == node) {
     902             :                             // case 1) plain forward connection
     903        3921 :                             tracks[i]->addSuccessor(tracks[i2]);
     904             :                             // reverse edge (numerical id incremented by numEdges)
     905        3921 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
     906             :                         } else {
     907             :                             // case 2) both edges pointing towards each other
     908        2604 :                             tracks[i]->addSuccessor(tracks[i2 + numEdges]);
     909        2604 :                             tracks[i2]->addSuccessor(tracks[i + numEdges]);
     910             :                         }
     911             :                     } else {
     912        6487 :                         if (e2->getFromNode() == node) {
     913             :                             // case 3) both edges pointing away from each other
     914        2566 :                             tracks[i + numEdges]->addSuccessor(tracks[i2]);
     915        2566 :                             tracks[i2 + numEdges]->addSuccessor(tracks[i]);
     916             :                         } else {
     917             :                             // already handled via case 1)
     918             :                         }
     919             :                     }
     920             : 
     921             :                 }
     922             :             }
     923             :         }
     924             :     }
     925             :     // define start and end successors
     926        4024 :     for (auto& item : stopTracks) {
     927        3995 :         const int index = item.first->getNumericalID();
     928             :         // start
     929        3995 :         item.second.first->addSuccessor(tracks[index]);
     930        3995 :         item.second.first->addSuccessor(tracks[index + numEdges]);
     931             :         // end
     932        3995 :         tracks[index]->addSuccessor(item.second.second);
     933        3995 :         tracks[index + numEdges]->addSuccessor(item.second.second);
     934             :     }
     935             :     // DEBUG
     936             :     /*
     937             :     for (Track* t : tracks) {
     938             :         std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
     939             :         for (Track* s : t->getSuccessors(SVC_IGNORING)) {
     940             :             std::cout << "   succ=" << s->getID() << "\n";
     941             :         }
     942             :     }
     943             :     */
     944             : 
     945             :     SUMOAbstractRouter<Track, NBVehicle>* const router = new DijkstraRouter<Track, NBVehicle>(
     946          29 :         tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
     947             : 
     948          29 :     int added = 0;
     949          29 :     int numDisconnected = 0;
     950             :     std::set<NBEdge*, ComparatorIdLess> addBidiStops;
     951             :     std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
     952             :     std::set<std::pair<std::string, std::string> > visited;
     953             : 
     954             :     // the isConsistent heuristic may fail in some cases. If we observe that a
     955             :     // specific sequence of stop ids in encoded in both directions, we take this
     956             :     // as a reason to overrule the heuristic
     957          29 :     std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
     958             : 
     959         269 :     for (const auto& item : lc.getLines()) {
     960         240 :         NBPTLine* line = item.second;
     961         240 :         std::vector<std::pair<NBEdge*, std::string> > stops = line->getStopEdges(ec);
     962             :         std::vector<NBEdge*> stopEdges;
     963        1201 :         for (auto it : stops) {
     964         961 :             stopEdges.push_back(it.first);
     965             :         }
     966         240 :         NBEdge* routeStart = line->getRouteStart(ec);
     967         240 :         NBEdge* routeEnd = line->getRouteEnd(ec);
     968         240 :         if (routeStart != nullptr) {
     969         436 :             stops.insert(stops.begin(), {routeStart, routeStart->getID()});
     970             :         }
     971         240 :         if (routeEnd != nullptr) {
     972         426 :             stops.push_back({routeEnd, routeEnd->getID()});
     973             :         }
     974         240 :         if (stops.size() < 2) {
     975           7 :             continue;
     976             :         }
     977         466 :         if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
     978          36 :             WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
     979          12 :             continue;
     980             :         }
     981        1314 :         for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
     982        1093 :             NBEdge* fromEdge = it->first;
     983        1093 :             NBEdge* toEdge = (it + 1)->first;
     984             :             const std::string fromStop = it->second;
     985             :             const std::string toStop = (it + 1)->second;
     986        1093 :             std::pair<std::string, std::string> trip(fromStop, toStop);
     987        1093 :             std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
     988             :             //std::cout << " line=" << line->getLineID() << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << " fromStop=" << fromStop << " toStop=" << toStop << "\n";
     989         453 :             if (visited.count(trip) != 0) {
     990         453 :                 continue;
     991             :             } else {
     992             :                 visited.insert(trip);
     993             :             }
     994         215 :             if (stopTracks.count(fromEdge) == 0
     995             :                     || stopTracks.count(toEdge) == 0) {
     996         215 :                 continue;
     997             :             }
     998         425 :             const bool needBidi = visited.count(reverseTrip) != 0;
     999         425 :             NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
    1000             :             std::vector<const Track*> route;
    1001         425 :             router->compute(stopTracks[fromEdge].first, stopTracks[toEdge].second, &veh, 0, route);
    1002             :             //if (fromEdge->getID() == "356053025#1" && toEdge->getID() == "23256161") {
    1003             :             //    std::cout << "DEBUG: route=" << toString(route) << "\n";
    1004             :             //}
    1005         425 :             if (route.size() > 0) {
    1006             :                 assert(route.size() > 2);
    1007        3223 :                 for (int i = 1; i < (int)route.size() - 1; ++i) {
    1008        2798 :                     if (route[i]->getNumericalID() >= numEdges || needBidi) {
    1009         100 :                         NBEdge* edge = route[i]->edge;
    1010             :                         if (addBidiEdges.count(edge) == 0) {
    1011          97 :                             bool isStop = i == 1 || i == (int)route.size() - 2;
    1012          97 :                             if (!edge->isBidiRail(true)) {
    1013          63 :                                 if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
    1014             :                                     addBidiEdges.insert(edge);
    1015          63 :                                     if (isStop) {
    1016             :                                         addBidiStops.insert(edge);
    1017             :                                     }
    1018             :                                 } else {
    1019           0 :                                     if (isStop) {
    1020           0 :                                         WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
    1021             :                                     }
    1022             :                                 }
    1023          34 :                             } else if (isStop && needBidi) {
    1024          48 :                                 std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
    1025          24 :                                 if (fs) {
    1026          42 :                                     std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(fs, ec);
    1027          21 :                                     if (fromReverse) {
    1028          42 :                                         sc.insert(fromReverse);
    1029          21 :                                         fs->setBidiStop(fromReverse);
    1030             :                                     }
    1031             :                                 }
    1032          48 :                                 std::shared_ptr<NBPTStop> ts = sc.get(toStop);
    1033          24 :                                 if (ts) {
    1034          38 :                                     std::shared_ptr<NBPTStop> toReverse = sc.getReverseStop(ts, ec);
    1035          19 :                                     if (toReverse) {
    1036          38 :                                         sc.insert(toReverse);
    1037          19 :                                         ts->setBidiStop(toReverse);
    1038             :                                     }
    1039             :                                 }
    1040             :                             }
    1041             :                         }
    1042             :                     }
    1043             :                 }
    1044             :             } else {
    1045           0 :                 WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
    1046           0 :                 numDisconnected++;
    1047             :             }
    1048        1093 :         }
    1049         240 :     }
    1050          92 :     for (NBEdge* edge : addBidiEdges) {
    1051          63 :         if (!edge->isBidiRail()) {
    1052          29 :             NBEdge* e2 = addBidiEdge(ec, edge);
    1053             :             //std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
    1054          29 :             if (e2 != nullptr) {
    1055          29 :                 added++;
    1056          29 :                 if (!minimal) {
    1057          19 :                     added += extendBidiEdges(ec, edge->getToNode(), edge);
    1058          19 :                     added += extendBidiEdges(ec, edge->getFromNode(), e2);
    1059             :                 }
    1060             :             }
    1061             :         }
    1062             :     }
    1063             : 
    1064          29 :     if (addBidiEdges.size() > 0 || numDisconnected > 0) {
    1065          34 :         WRITE_MESSAGE("Added " + toString(addBidiStops.size()) + " bidi-edges for public transport stops and a total of "
    1066             :                       + toString(added) + " bidi-edges to ensure connectivity of stops ("
    1067             :                       + toString(numDisconnected) + " stops remain disconnected)");
    1068             :     }
    1069             : 
    1070             :     // clean up
    1071       37159 :     for (Track* t : tracks) {
    1072       37130 :         delete t;
    1073             :     }
    1074          29 :     delete router;
    1075          58 :     return (int)addBidiEdges.size();
    1076             : }
    1077             : 
    1078             : 
    1079             : int
    1080           6 : NBRailwayTopologyAnalyzer::addBidiEdgesForStraightConnectivity(NBEdgeCont& ec, bool geometryLike) {
    1081           6 :     int added = 0;
    1082           6 :     std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
    1083         107 :     for (const auto& e : ec) {
    1084         101 :         if (!hasRailway(e.second->getPermissions())) {
    1085          72 :             continue;
    1086             :         }
    1087         101 :         NBNode* const from = e.second->getFromNode();
    1088             :         NBNode* const to = e.second->getToNode();
    1089          59 :         if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
    1090          59 :             continue;
    1091             :         }
    1092          42 :         if (e.second->isBidiRail()) {
    1093          13 :             continue;
    1094             :         }
    1095             :         EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
    1096          29 :         getRailEdges(from, inRailFrom, outRailFrom);
    1097          29 :         getRailEdges(to, inRailTo, outRailTo);
    1098             :         // check whether there is a straight edge pointing away from this one at the from-node
    1099             :         // and there is no straight incoming edge at the from-node
    1100             :         bool haveStraight = false;
    1101             :         bool haveStraightReverse = false;
    1102          29 :         if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
    1103          25 :             for (const NBEdge* fromStraightCand : outRailFrom) {
    1104          17 :                 if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
    1105             :                     haveStraightReverse = true;
    1106             :                     //std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
    1107             :                     break;
    1108             :                 }
    1109             :             }
    1110          12 :             if (haveStraightReverse) {
    1111           5 :                 for (const NBEdge* fromStraightCand : inRailFrom) {
    1112           4 :                     if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
    1113             :                         haveStraight = true;
    1114             :                         //std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
    1115             :                         break;
    1116             :                     }
    1117             :                 }
    1118             :             }
    1119             :         }
    1120          29 :         if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
    1121             :             // check whether there is a straight edge pointing towards this one at the to-node
    1122             :             // and there is no straight outoing edge at the to-node
    1123             :             haveStraight = false;
    1124             :             haveStraightReverse = false;
    1125          26 :             for (const NBEdge* toStraightCand : inRailTo) {
    1126          22 :                 if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
    1127             :                     haveStraightReverse = true;
    1128             :                     //std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
    1129             :                     break;
    1130             :                 }
    1131             :             }
    1132          13 :             if (haveStraightReverse) {
    1133          13 :                 for (const NBEdge* toStraightCand : outRailTo) {
    1134           8 :                     if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
    1135             :                         haveStraight = true;
    1136             :                         //std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
    1137             :                         break;
    1138             :                     }
    1139             :                 }
    1140             :             }
    1141             :         }
    1142             :         //std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
    1143          29 :         if (haveStraightReverse && !haveStraight) {
    1144           6 :             NBEdge* e2 = addBidiEdge(ec, e.second);
    1145             :             //std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
    1146           6 :             if (e2 != nullptr) {
    1147           6 :                 added++;
    1148           6 :                 added += extendBidiEdges(ec, to, e.second);
    1149           6 :                 added += extendBidiEdges(ec, from, e2);
    1150             :             }
    1151             :         }
    1152             :     }
    1153           6 :     if (added > 0) {
    1154           4 :         if (geometryLike) {
    1155           4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
    1156             :         } else {
    1157           4 :             WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
    1158             :         }
    1159             :     }
    1160           6 :     return added;
    1161             : }
    1162             : 
    1163             : 
    1164             : void
    1165        1316 : NBRailwayTopologyAnalyzer::updateTurns(NBEdge* edge) {
    1166        1316 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getFromNode(), false);
    1167        1316 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getToNode(), false);
    1168        1316 : }
    1169             : 
    1170             : 
    1171             : double
    1172       17371 : NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
    1173       17371 :     return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
    1174             : }
    1175             : 
    1176             : 
    1177             : void
    1178           4 : NBRailwayTopologyAnalyzer::extendDirectionPriority(NBEdgeCont& ec, bool fromUniDir) {
    1179             :     // if fromUniDir=true, assign priority value for each railway edge:
    1180             :     // 4: edge is unidirectional
    1181             :     // 3: edge is in main direction of bidirectional track
    1182             :     // 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
    1183             :     // 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
    1184             :     // 0: edge is part of bidirectional track in reverse of main direction
    1185             :     //
    1186             :     // otherwise:
    1187             :     // assign priority value for each railway edge with priority -1 (undefined):
    1188             :     // x: edges with priority >= 0 keep their priority
    1189             :     // x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
    1190             :     // x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
    1191             :     // x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
    1192             :     // x-4 : edge is reverse direction of an x-1 edge
    1193             : 
    1194             :     std::set<NBEdge*, ComparatorIdLess> bidi;
    1195             :     EdgeSet uni;
    1196         174 :     for (NBEdge* edge : ec.getAllEdges()) {
    1197         170 :         if (hasRailway(edge->getPermissions())) {
    1198         170 :             if (fromUniDir) {
    1199          46 :                 if (!edge->isBidiRail()) {
    1200          14 :                     edge->setPriority(4);
    1201             :                     uni.insert(edge);
    1202             :                 } else {
    1203             :                     bidi.insert(edge);
    1204             :                 }
    1205             :             } else {
    1206         124 :                 if (edge->getPriority() >= 0) {
    1207             :                     uni.insert(edge);
    1208             :                 } else {
    1209             :                     bidi.insert(edge);
    1210             :                 }
    1211             :             }
    1212             :         }
    1213             :     }
    1214             : 
    1215           4 :     if (uni.size() == 0) {
    1216           0 :         if (bidi.size() != 0) {
    1217           0 :             WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
    1218             :         }
    1219             :         return;
    1220             :     }
    1221             :     EdgeSet seen;
    1222             :     EdgeSet check = uni;
    1223             :     EdgeSet forward;
    1224         120 :     while (!check.empty()) {
    1225         116 :         NBEdge* edge = *check.begin();
    1226             :         check.erase(edge);
    1227          48 :         if (seen.count(edge) != 0) {
    1228          48 :             continue;
    1229             :         }
    1230             :         seen.insert(edge);
    1231          68 :         NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
    1232          68 :         if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
    1233             :             forward.insert(straightOut);
    1234             :             check.insert(straightOut);
    1235             :         }
    1236          68 :         NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
    1237          68 :         if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
    1238             :             forward.insert(straightIn);
    1239             :             check.insert(straightIn);
    1240             :         }
    1241             : #ifdef DEBUG_DIRECTION_PRIORITY
    1242             :         std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
    1243             :                   << " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
    1244             :                   << " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
    1245             :                   << "\n";
    1246             : #endif
    1247             :     }
    1248             : 
    1249         130 :     for (NBEdge* edge : bidi) {
    1250         126 :         NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
    1251             :         int prio;
    1252             :         int bidiPrio;
    1253             :         if (forward.count(edge) != 0) {
    1254             :             if (forward.count(bidiEdge) == 0) {
    1255             :                 prio = 3;
    1256             :                 bidiPrio = 0;
    1257             :             } else {
    1258             :                 // both forward
    1259             :                 prio = 2;
    1260             :                 bidiPrio = 2;
    1261             :             }
    1262             :         } else {
    1263             :             if (forward.count(bidiEdge) != 0) {
    1264             :                 prio = 0;
    1265             :                 bidiPrio = 3;
    1266             :             } else {
    1267             :                 // neither forward
    1268             :                 prio = 1;
    1269             :                 bidiPrio = 1;
    1270             :             }
    1271             :         }
    1272         126 :         if (bidiEdge == nullptr) {
    1273           6 :             WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
    1274             :         }
    1275         126 :         if (edge->getPriority() >= 0) {
    1276             :             bidiPrio = 0;
    1277             :         }
    1278         126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
    1279             :             prio = 0;
    1280             :         }
    1281         126 :         if (edge->getPriority() < 0) {
    1282             :             edge->setPriority(prio);
    1283             :         }
    1284         126 :         if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
    1285             :             bidiEdge->setPriority(bidiPrio);
    1286             :         }
    1287             :     }
    1288             :     std::map<int, int> numPrios;
    1289         130 :     for (NBEdge* edge : bidi) {
    1290         126 :         numPrios[edge->getPriority()]++;
    1291             :     }
    1292           4 :     if (fromUniDir) {
    1293           2 :         WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1294             :     } else {
    1295           6 :         WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
    1296             :     }
    1297             : }
    1298             : 
    1299             : // ---------------------------------------------------------------------------
    1300             : // NBRailwaySignalGuesser methods
    1301             : // ---------------------------------------------------------------------------
    1302             : 
    1303             : int
    1304        1837 : NBRailwaySignalGuesser::guessRailSignals(NBEdgeCont& ec, NBPTStopCont& sc) {
    1305        1837 :     const OptionsCont& oc = OptionsCont::getOptions();
    1306             :     int addedSignals = 0;
    1307        3674 :     if (oc.exists("railway.signal.guess.by-stops")) {
    1308        3492 :         if (oc.getBool("railway.signal.guess.by-stops")) {
    1309           0 :             const double minLength = oc.getFloat("osm.stop-output.length.train");
    1310           0 :             addedSignals += guessByStops(ec, sc, minLength);
    1311             :         }
    1312             :     }
    1313        1837 :     return addedSignals;
    1314             : }
    1315             : 
    1316             : 
    1317             : int
    1318           0 : NBRailwaySignalGuesser::guessByStops(NBEdgeCont& ec, NBPTStopCont& sc, double minLength) {
    1319             :     int addedSignals = 0;
    1320           0 :     for (auto& item : sc.getStops()) {
    1321           0 :         const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
    1322           0 :         if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
    1323             :             NBNode* to = stopEdge->getToNode();
    1324           0 :             if (to->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
    1325           0 :                 to->reinit(to->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1326           0 :                 addedSignals++;
    1327             :             }
    1328             :             NBNode* from = stopEdge->getFromNode();
    1329           0 :             if (stopEdge->getLoadedLength() >= minLength) {
    1330             :                 /// XXX should split edge if it is too long
    1331           0 :                 if (from->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
    1332           0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1333           0 :                     addedSignals++;
    1334             :                 }
    1335             :             } else {
    1336           0 :                 double searchDist = minLength - stopEdge->getLoadedLength();
    1337           0 :                 while (searchDist > 0 && from->geometryLike()) {
    1338           0 :                     for (const NBEdge* in : from->getIncomingEdges()) {
    1339           0 :                         if (in->getFromNode() != stopEdge->getToNode()) {
    1340             :                             // found edge that isn't a bidi predecessor
    1341             :                             stopEdge = in;
    1342             :                             break;
    1343             :                         }
    1344             :                     }
    1345           0 :                     if (stopEdge->getFromNode() == from) {
    1346             :                         // bidi edge without predecessor
    1347             :                         break;
    1348             :                     } else {
    1349             :                         from = stopEdge->getFromNode();
    1350             :                     }
    1351           0 :                     searchDist -= stopEdge->getLoadedLength();
    1352             :                 }
    1353           0 :                 if (searchDist <= 0 && from->getType() != SumoXMLNodeType::RAIL_SIGNAL) {
    1354           0 :                     from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
    1355           0 :                     addedSignals++;
    1356             :                 }
    1357             :             }
    1358             :         }
    1359             :     }
    1360           0 :     WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
    1361           0 :     return addedSignals;
    1362             : }
    1363             : 
    1364             : 
    1365             : 
    1366             : /****************************************************************************/

Generated by: LCOV version 1.14