LCOV - code coverage report
Current view: top level - src/netbuild - NBEdge.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 2002 2156 92.9 %
Date: 2024-05-07 15:28:01 Functions: 179 193 92.7 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-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    NBEdge.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Jakob Erdmann
      17             : /// @author  Sascha Krieg
      18             : /// @author  Michael Behrisch
      19             : /// @author  Laura Bieker
      20             : /// @author  Leonhard Luecken
      21             : /// @date    Tue, 20 Nov 2001
      22             : ///
      23             : // Methods for the representation of a single edge
      24             : /****************************************************************************/
      25             : #include <config.h>
      26             : 
      27             : #include <vector>
      28             : #include <string>
      29             : #include <algorithm>
      30             : #include <cmath>
      31             : #include <iomanip>
      32             : #include <utils/common/MsgHandler.h>
      33             : #include <utils/common/StringTokenizer.h>
      34             : #include <utils/common/StringUtils.h>
      35             : #include <utils/common/ToString.h>
      36             : #include <utils/common/UtilExceptions.h>
      37             : #include <utils/common/StdDefs.h>
      38             : #include <utils/geom/GeomHelper.h>
      39             : #include <utils/options/OptionsCont.h>
      40             : #include "NBEdgeCont.h"
      41             : #include "NBNode.h"
      42             : #include "NBNodeCont.h"
      43             : #include "NBContHelper.h"
      44             : #include "NBHelpers.h"
      45             : #include "NBTrafficLightDefinition.h"
      46             : #include "NBOwnTLDef.h"
      47             : #include "NBTypeCont.h"
      48             : #include "NBEdge.h"
      49             : 
      50             : //#define ADDITIONAL_WARNINGS
      51             : //#define DEBUG_CONNECTION_GUESSING
      52             : //#define DEBUG_ANGLES
      53             : //#define DEBUG_NODE_BORDER
      54             : //#define DEBUG_REPLACECONNECTION
      55             : //#define DEBUG_JUNCTIONPRIO
      56             : //#define DEBUG_TURNSIGNS
      57             : //#define DEBUG_CUT_LANES
      58             : #define DEBUGID ""
      59             : #define DEBUGCOND (getID() == DEBUGID)
      60             : //#define DEBUGCOND (StringUtils::startsWith(getID(), DEBUGID))
      61             : //#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
      62             : #define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
      63             : //#define DEBUGCOND (true)
      64             : 
      65             : // ===========================================================================
      66             : // static members
      67             : // ===========================================================================
      68             : const double NBEdge::UNSPECIFIED_WIDTH = -1;
      69             : const double NBEdge::UNSPECIFIED_OFFSET = 0;
      70             : const double NBEdge::UNSPECIFIED_SPEED = -1;
      71             : const double NBEdge::UNSPECIFIED_FRICTION = 1.;
      72             : const double NBEdge::UNSPECIFIED_CONTPOS = -1;
      73             : const double NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE = -1;
      74             : 
      75             : const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
      76             : const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
      77             : const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
      78             : const int NBEdge::UNSPECIFIED_INTERNAL_LANE_INDEX = -1;
      79             : const bool NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED = false;
      80             : 
      81             : double NBEdge::myDefaultConnectionLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
      82             : 
      83             : NBEdge NBEdge::DummyEdge;
      84             : 
      85             : ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
      86             : 
      87             : // ===========================================================================
      88             : // method definitions
      89             : // ===========================================================================
      90             : std::string
      91      804075 : NBEdge::Connection::getInternalLaneID() const {
      92     1608150 :     return id + "_" + toString(internalLaneIndex);
      93             : }
      94             : 
      95             : 
      96             : std::string
      97         382 : NBEdge::Connection::getDescription(const NBEdge* parent) const {
      98         764 :     return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
      99        1146 :             + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
     100             : }
     101             : 
     102             : 
     103      860030 : NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
     104      860030 :     fromLane(fromLane_),
     105      860030 :     toEdge(toEdge_),
     106      860030 :     toLane(toLane_),
     107      860030 :     mayDefinitelyPass(mayDefinitelyPass_),
     108      860030 :     customLength(myDefaultConnectionLength),
     109     1719978 :     id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
     110      860030 : }
     111             : 
     112             : 
     113      196119 : NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
     114      196119 :     speed(e->getSpeed()),
     115      196119 :     friction(e->getFriction()),
     116      196119 :     permissions(SVCAll),
     117      196119 :     preferred(0),
     118      196119 :     changeLeft(SVCAll),
     119      196119 :     changeRight(SVCAll),
     120      196119 :     endOffset(e->getEndOffset()),
     121      196119 :     laneStopOffset(e->getEdgeStopOffset()),
     122      196119 :     width(e->getLaneWidth()),
     123      196119 :     accelRamp(false),
     124      392238 :     connectionsDone(false) {
     125      196119 :     if (origID_ != "") {
     126       60826 :         setParameter(SUMO_PARAM_ORIGID, origID_);
     127             :     }
     128      196119 : }
     129             : 
     130             : 
     131             : /* -------------------------------------------------------------------------
     132             :  * NBEdge::ToEdgeConnectionsAdder-methods
     133             :  * ----------------------------------------------------------------------- */
     134             : void
     135      489235 : NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
     136             :     // check
     137             :     assert((int)myTransitions.size() > virtEdge);
     138             :     // get the approached edge
     139      489235 :     NBEdge* succEdge = myTransitions[virtEdge];
     140             :     std::vector<int> lanes;
     141             : 
     142             :     // check whether the currently regarded, approached edge has already
     143             :     //  a connection starting at the edge which is currently being build
     144             :     std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
     145      489235 :     if (i != myConnections.end()) {
     146             :         // if there were already lanes assigned, get them
     147      375122 :         lanes = (*i).second;
     148             :     }
     149             : 
     150             :     // check whether the current lane was already used to connect the currently
     151             :     //  regarded approached edge
     152      489235 :     std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
     153      489235 :     if (j == lanes.end()) {
     154             :         // if not, add it to the list
     155      127221 :         lanes.push_back(lane);
     156             :     }
     157             :     // set information about connecting lanes
     158      489235 :     myConnections[succEdge] = lanes;
     159      489235 : }
     160             : 
     161             : 
     162             : 
     163             : /* -------------------------------------------------------------------------
     164             :  * NBEdge::MainDirections-methods
     165             :  * ----------------------------------------------------------------------- */
     166       58238 : NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
     167             :     NBContHelper::edge_similar_direction_sorter sorter(parent);
     168       58238 :     const NBEdge* straight = nullptr;
     169      172430 :     for (const NBEdge* const out : outgoing) {
     170      114192 :         const SVCPermissions outPerms = out->getPermissions();
     171      114412 :         for (const int l : availableLanes) {
     172      114270 :             if ((parent->myLanes[l].permissions & outPerms) != 0) {
     173      114050 :                 if (straight == nullptr || sorter(out, straight)) {
     174       85949 :                     straight = out;
     175             :                 }
     176             :                 break;
     177             :             }
     178             :         }
     179             :     }
     180       58238 :     if (straight == nullptr) {
     181        5053 :         return;
     182             :     }
     183       58172 :     myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
     184             : 
     185             :     // check whether the right turn has a higher priority
     186             :     assert(outgoing.size() > 0);
     187       58172 :     const LinkDirection straightestDir = to->getDirection(parent, straight);
     188             : #ifdef DEBUG_CONNECTION_GUESSING
     189             :     if (DEBUGCOND2(parent)) {
     190             :         std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
     191             :     }
     192             : #endif
     193       58172 :     if (NBNode::isTrafficLight(to->getType()) &&
     194        5347 :             (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
     195        4987 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     196        4987 :         return;
     197             :     }
     198       53185 :     if (outgoing[0]->getJunctionPriority(to) == 1) {
     199       28219 :         myDirs.push_back(MainDirections::Direction::RIGHTMOST);
     200             :     }
     201             :     // check whether the left turn has a higher priority
     202       53185 :     if (outgoing.back()->getJunctionPriority(to) == 1) {
     203             :         // ok, the left turn belongs to the higher priorised edges on the junction
     204             :         //  let's check, whether it has also a higher priority (lane number/speed)
     205             :         //  than the current
     206       28534 :         if (outgoing.back()->getPriority() > straight->getPriority() ||
     207             :                 outgoing.back()->getNumLanes() > straight->getNumLanes()) {
     208        1952 :             myDirs.push_back(MainDirections::Direction::LEFTMOST);
     209             :         }
     210             :     }
     211             :     // check whether the forward direction has a higher priority
     212             :     // check whether it has a higher priority and is going straight
     213       53185 :     if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
     214       25797 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     215             :     }
     216             : }
     217             : 
     218             : 
     219       58238 : NBEdge::MainDirections::~MainDirections() {}
     220             : 
     221             : 
     222             : bool
     223       58172 : NBEdge::MainDirections::empty() const {
     224       58172 :     return myDirs.empty();
     225             : }
     226             : 
     227             : 
     228             : bool
     229      217374 : NBEdge::MainDirections::includes(Direction d) const {
     230      217374 :     return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
     231             : }
     232             : 
     233             : 
     234             : /* -------------------------------------------------------------------------
     235             :  * NBEdge::connections_relative_edgelane_sorter-methods
     236             :  * ----------------------------------------------------------------------- */
     237             : int
     238      212945 : NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
     239      212945 :     if (c1.toEdge != c2.toEdge) {
     240      165724 :         return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
     241             :     }
     242       47221 :     return c1.toLane < c2.toLane;
     243             : }
     244             : 
     245             : 
     246             : /* -------------------------------------------------------------------------
     247             :  * NBEdge-methods
     248             :  * ----------------------------------------------------------------------- */
     249       20302 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
     250             :                std::string type, double speed, double friction, int nolanes,
     251             :                int priority, double laneWidth, double endOffset,
     252       20302 :                LaneSpreadFunction spread, const std::string& streetName) :
     253       40604 :     Named(StringUtils::convertUmlaute(id)),
     254       20302 :     myStep(EdgeBuildingStep::INIT),
     255       20302 :     myType(StringUtils::convertUmlaute(type)),
     256       20302 :     myFrom(from), myTo(to),
     257       20302 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     258       20302 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     259       20302 :     myDistance(0),
     260       20302 :     myTurnDestination(nullptr),
     261       20302 :     myPossibleTurnDestination(nullptr),
     262       20302 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     263       20302 :     myLaneSpreadFunction(spread), myEndOffset(endOffset),
     264       20302 :     myLaneWidth(laneWidth),
     265       20302 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     266       20302 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     267       20302 :     myStreetName(streetName),
     268       20302 :     mySignalPosition(Position::INVALID),
     269       20302 :     mySignalNode(nullptr),
     270       20302 :     myIsOffRamp(false),
     271       20302 :     myIsBidi(false),
     272       60906 :     myIndex(-1) {
     273       20302 :     init(nolanes, false, "");
     274       20302 : }
     275             : 
     276             : 
     277      128300 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
     278             :                std::string type, double speed, double friction, int nolanes,
     279             :                int priority, double laneWidth, double endOffset,
     280             :                PositionVector geom,
     281             :                LaneSpreadFunction spread,
     282             :                const std::string& streetName,
     283             :                const std::string& origID,
     284      128300 :                bool tryIgnoreNodePositions) :
     285      256600 :     Named(StringUtils::convertUmlaute(id)),
     286      128300 :     myStep(EdgeBuildingStep::INIT),
     287      128301 :     myType(StringUtils::convertUmlaute(type)),
     288      128300 :     myFrom(from), myTo(to),
     289      128300 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     290      128300 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     291      128300 :     myDistance(0),
     292      128300 :     myTurnDestination(nullptr),
     293      128300 :     myPossibleTurnDestination(nullptr),
     294      128300 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     295      128300 :     myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
     296      128300 :     myLaneWidth(laneWidth),
     297      128300 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     298      128300 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     299      128300 :     myStreetName(streetName),
     300      128300 :     mySignalPosition(Position::INVALID),
     301      128300 :     mySignalNode(nullptr),
     302      128300 :     myIsOffRamp(false),
     303      128300 :     myIsBidi(false),
     304      384900 :     myIndex(-1) {
     305      128300 :     init(nolanes, tryIgnoreNodePositions, origID);
     306      128307 : }
     307             : 
     308             : 
     309        2625 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
     310        5250 :     Named(StringUtils::convertUmlaute(id)),
     311        2625 :     myStep(EdgeBuildingStep::INIT),
     312        2625 :     myType(tpl->getTypeID()),
     313        2625 :     myFrom(from), myTo(to),
     314        2625 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     315        2625 :     myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
     316        2625 :     myFriction(tpl->getFriction()),
     317        2625 :     myDistance(0),
     318        2625 :     myTurnDestination(nullptr),
     319        2625 :     myPossibleTurnDestination(nullptr),
     320        2625 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     321        2625 :     myGeom(geom),
     322        2625 :     myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
     323        2625 :     myEndOffset(tpl->getEndOffset()),
     324        2625 :     myEdgeStopOffset(tpl->getEdgeStopOffset()),
     325        2625 :     myLaneWidth(tpl->getLaneWidth()),
     326        2625 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     327        2625 :     myAmInTLS(false),
     328        2625 :     myAmMacroscopicConnector(false),
     329        2625 :     myStreetName(tpl->getStreetName()),
     330        2625 :     mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
     331        2625 :     mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
     332        2625 :     myIsOffRamp(false),
     333        2625 :     myIsBidi(false),
     334       13125 :     myIndex(-1) {
     335        3990 :     init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
     336        6419 :     for (int i = 0; i < getNumLanes(); i++) {
     337        3794 :         const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
     338        3794 :         setSpeed(i, tpl->getLaneSpeed(tplIndex));
     339        3794 :         setFriction(i, tpl->getLaneFriction(tplIndex));
     340        3794 :         setPermissions(tpl->getPermissions(tplIndex), i);
     341        3794 :         setLaneWidth(i, tpl->myLanes[tplIndex].width);
     342        3794 :         setLaneType(i, tpl->myLanes[tplIndex].type);
     343        3794 :         myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
     344        3794 :         if (to == tpl->myTo) {
     345        1305 :             setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
     346        1305 :             setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
     347             :         }
     348             :     }
     349        2625 :     if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
     350           3 :         myLoadedLength = tpl->myLoadedLength;
     351             :     }
     352        2625 :     updateParameters(tpl->getParametersMap());
     353        2625 : }
     354             : 
     355             : 
     356        2382 : NBEdge::NBEdge() :
     357             :     Named("DUMMY"),
     358        2382 :     myStep(EdgeBuildingStep::INIT),
     359        2382 :     myFrom(nullptr), myTo(nullptr),
     360        2382 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     361        2382 :     myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
     362        2382 :     myDistance(0),
     363        2382 :     myTurnDestination(nullptr),
     364        2382 :     myPossibleTurnDestination(nullptr),
     365        2382 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     366        2382 :     myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
     367        2382 :     myEndOffset(0),
     368        2382 :     myEdgeStopOffset(StopOffset()),
     369        2382 :     myLaneWidth(0),
     370        2382 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     371        2382 :     myAmInTLS(false),
     372        2382 :     myAmMacroscopicConnector(false),
     373        2382 :     mySignalPosition(Position::INVALID),
     374        7146 :     mySignalNode(nullptr) {
     375        2382 : }
     376             : 
     377             : 
     378             : void
     379        1878 : NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
     380             :                double speed, double friction, int nolanes, int priority,
     381             :                PositionVector geom, double laneWidth, double endOffset,
     382             :                const std::string& streetName,
     383             :                LaneSpreadFunction spread,
     384             :                bool tryIgnoreNodePositions) {
     385        1878 :     if (myFrom != from) {
     386          10 :         myFrom->removeEdge(this, false);
     387             :     }
     388        1878 :     if (myTo != to) {
     389          11 :         myTo->removeEdge(this, false);
     390             :     }
     391        3756 :     myType = StringUtils::convertUmlaute(type);
     392        1878 :     myFrom = from;
     393        1878 :     myTo = to;
     394        1878 :     myPriority = priority;
     395             :     //?myTurnDestination(0),
     396             :     //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
     397             :     myGeom = geom;
     398        1878 :     myLaneSpreadFunction = spread;
     399        1878 :     myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
     400        1878 :     myStreetName = streetName;
     401             :     //?, myAmTurningWithAngle(0), myAmTurningOf(0),
     402             :     //?myAmInTLS(false), myAmMacroscopicConnector(false)
     403             : 
     404             :     // preserve lane-specific settings (geometry must be recomputed)
     405             :     // if new lanes are added they copy the values from the leftmost lane (if specified)
     406        1878 :     const std::vector<Lane> oldLanes = myLanes;
     407        3756 :     init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
     408        3844 :     for (int i = 0; i < (int)nolanes; ++i) {
     409        1966 :         PositionVector newShape = myLanes[i].shape;
     410        1966 :         myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
     411             :         myLanes[i].shape = newShape;
     412        1966 :     }
     413             :     // however, if the new edge defaults are explicityly given, they override the old settings
     414        1878 :     if (endOffset != UNSPECIFIED_OFFSET) {
     415           1 :         setEndOffset(-1, endOffset);
     416             :     }
     417        1878 :     if (laneWidth != UNSPECIFIED_WIDTH) {
     418           2 :         setLaneWidth(-1, laneWidth);
     419             :     }
     420        1878 :     if (speed != UNSPECIFIED_SPEED) {
     421          28 :         setSpeed(-1, speed);
     422             :     }
     423        1878 :     if (friction != UNSPECIFIED_FRICTION) {
     424           0 :         setFriction(-1, friction);
     425             :     }
     426        1878 : }
     427             : 
     428             : 
     429             : void
     430       10203 : NBEdge::reinitNodes(NBNode* from, NBNode* to) {
     431             :     // connections may still be valid
     432       10203 :     if (from == nullptr || to == nullptr) {
     433           0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     434             :     }
     435       10203 :     if (myFrom != from) {
     436        5186 :         myFrom->removeEdge(this, false);
     437             :     }
     438       10203 :     if (myTo != to) {
     439        5026 :         myTo->removeEdge(this, false);
     440             :     }
     441             :     // remove first from both nodes and then add to the new nodes
     442             :     // (otherwise reversing does not work)
     443       10203 :     if (myFrom != from) {
     444        5186 :         myFrom = from;
     445        5186 :         myFrom->addOutgoingEdge(this);
     446             :     }
     447       10203 :     if (myTo != to) {
     448        5026 :         myTo = to;
     449        5026 :         myTo->addIncomingEdge(this);
     450             :     }
     451       10203 :     computeAngle();
     452       10203 : }
     453             : 
     454             : 
     455             : void
     456      153105 : NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
     457      153105 :     if (noLanes == 0) {
     458           0 :         throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
     459             :     }
     460      153105 :     if (myFrom == nullptr || myTo == nullptr) {
     461           0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     462             :     }
     463      153105 :     if (!SUMOXMLDefinitions::isValidNetID(myID)) {
     464           4 :         throw ProcessError(TLF("Invalid edge id '%'.", myID));
     465             :     }
     466             :     // revisit geometry
     467             :     //  should have at least two points at the end...
     468             :     //  and in dome cases, the node positions must be added
     469             :     // attempt symmetrical removal for forward and backward direction
     470             :     // (very important for bidiRail)
     471      153104 :     if (myFrom->getID() < myTo->getID()) {
     472       78894 :         PositionVector reverse = myGeom.reverse();
     473       78894 :         reverse.removeDoublePoints(POSITION_EPS, true);
     474      157788 :         myGeom = reverse.reverse();
     475       78894 :     } else {
     476       74210 :         myGeom.removeDoublePoints(POSITION_EPS, true);
     477             :     }
     478             : 
     479      153104 :     if (!tryIgnoreNodePositions || myGeom.size() < 2) {
     480       47856 :         if (myGeom.size() == 0) {
     481       47488 :             myGeom.push_back(myFrom->getPosition());
     482       47488 :             myGeom.push_back(myTo->getPosition());
     483             :         } else {
     484         368 :             myGeom.push_back_noDoublePos(myTo->getPosition());
     485         368 :             myGeom.push_front_noDoublePos(myFrom->getPosition());
     486             :         }
     487             :     }
     488      153104 :     if (myGeom.size() < 2) {
     489           0 :         myGeom.clear();
     490           0 :         myGeom.push_back(myFrom->getPosition());
     491           0 :         myGeom.push_back(myTo->getPosition());
     492             :     }
     493      153104 :     if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
     494          12 :         WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
     495           4 :         int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
     496           4 :         myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
     497             :     }
     498             :     //
     499      153104 :     myFrom->addOutgoingEdge(this);
     500      153104 :     myTo->addIncomingEdge(this);
     501             :     // prepare container
     502             :     assert(myGeom.size() >= 2);
     503      153104 :     myLength = myGeom.length();
     504      153104 :     if ((int)myLanes.size() > noLanes) {
     505             :         // remove connections starting at the removed lanes
     506           6 :         for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
     507           3 :             removeFromConnections(nullptr, lane, -1);
     508             :         }
     509             :         // remove connections targeting the removed lanes
     510           3 :         const EdgeVector& incoming = myFrom->getIncomingEdges();
     511           6 :         for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
     512           6 :             for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
     513           3 :                 (*i)->removeFromConnections(this, -1, lane);
     514             :             }
     515             :         }
     516             :     }
     517      153104 :     myLanes.clear();
     518      343496 :     for (int i = 0; i < noLanes; i++) {
     519      380784 :         myLanes.push_back(Lane(this, origID));
     520             :     }
     521      153104 :     computeLaneShapes();
     522      153104 :     computeAngle();
     523             : 
     524             : #ifdef DEBUG_CONNECTION_GUESSING
     525             :     if (DEBUGCOND) {
     526             :         std::cout << "init edge=" << getID() << "\n";
     527             :         for (Connection& c : myConnections) {
     528             :             std::cout << "  conn " << c.getDescription(this) << "\n";
     529             :         }
     530             :         for (Connection& c : myConnectionsToDelete) {
     531             :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
     532             :         }
     533             :     }
     534             : #endif
     535      153104 : }
     536             : 
     537             : 
     538      765533 : NBEdge::~NBEdge() {}
     539             : 
     540             : 
     541             : // -----------  Applying offset
     542             : void
     543       89863 : NBEdge::reshiftPosition(double xoff, double yoff) {
     544       89863 :     myGeom.add(xoff, yoff, 0);
     545      203294 :     for (Lane& lane : myLanes) {
     546      113431 :         lane.customShape.add(xoff, yoff, 0);
     547             :     }
     548       89863 :     computeLaneShapes(); // old shapes are dubious if computed with large coordinates
     549       97637 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
     550        7774 :         (*i).customShape.add(xoff, yoff, 0);
     551             :     }
     552             :     if (mySignalPosition != Position::INVALID) {
     553             :         mySignalPosition.add(xoff, yoff);
     554             :     }
     555       89863 :     myFromBorder.add(xoff, yoff, 0);
     556       89863 :     myToBorder.add(xoff, yoff, 0);
     557       89863 :     computeEdgeShape();
     558       89863 :     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
     559       89863 : }
     560             : 
     561             : 
     562             : void
     563        1144 : NBEdge::mirrorX() {
     564        1144 :     myGeom.mirrorX();
     565        3029 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     566        1885 :         myLanes[i].shape.mirrorX();
     567        1885 :         myLanes[i].customShape.mirrorX();
     568             :     }
     569        2496 :     for (Connection& c : myConnections) {
     570        1352 :         c.shape.mirrorX();
     571        1352 :         c.viaShape.mirrorX();
     572        1352 :         c.customShape.mirrorX();
     573             :     }
     574             :     if (mySignalPosition != Position::INVALID) {
     575          24 :         mySignalPosition.sety(-mySignalPosition.y());
     576             :     }
     577        1144 :     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
     578        1144 : }
     579             : 
     580             : 
     581             : // ----------- Edge geometry access and computation
     582             : const PositionVector
     583        3442 : NBEdge::getInnerGeometry() const {
     584        3442 :     return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
     585             : }
     586             : 
     587             : 
     588             : bool
     589      129037 : NBEdge::hasDefaultGeometry() const {
     590      129037 :     return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
     591             : }
     592             : 
     593             : 
     594             : bool
     595       77877 : NBEdge::hasDefaultGeometryEndpoints() const {
     596      142292 :     return myGeom.front().almostSame(myFrom->getPosition(), 0.01)  &&
     597       64415 :            myGeom.back().almostSame(myTo->getPosition(), 0.01);
     598             : }
     599             : 
     600             : 
     601             : bool
     602      247454 : NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
     603             :     // do not extend past the node position
     604      247454 :     if (node == myFrom) {
     605             :         return myGeom.front() == node->getPosition();
     606             :     } else {
     607             :         assert(node == myTo);
     608             :         return myGeom.back() == node->getPosition();
     609             :     }
     610             : }
     611             : 
     612             : Position
     613        5128 : NBEdge::getEndpointAtNode(const NBNode* node) const {
     614        5128 :     return node == myFrom ? myGeom.front() : myGeom.back();
     615             : }
     616             : 
     617             : void
     618        4016 : NBEdge::setGeometry(const PositionVector& s, bool inner) {
     619        4016 :     Position begin = myGeom.front(); // may differ from node position
     620        4016 :     Position end = myGeom.back(); // may differ from node position
     621             :     myGeom = s;
     622        4016 :     if (inner) {
     623           0 :         myGeom.insert(myGeom.begin(), begin);
     624           0 :         myGeom.push_back(end);
     625             :     }
     626             :     // ensure non-zero length (see ::init)
     627        4016 :     if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
     628           6 :         WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
     629           2 :         int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
     630           2 :         myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
     631             :     }
     632        4016 :     computeLaneShapes();
     633        4016 :     computeAngle();
     634        4016 :     myLength = myGeom.length();
     635        4016 : }
     636             : 
     637             : 
     638             : void
     639           0 : NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
     640             :     //std::cout << "extendGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " extent=" << maxExtent << " geom=" << myGeom;
     641           0 :     if (node == myFrom) {
     642           0 :         myGeom.extrapolate(maxExtent, true);
     643           0 :         double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
     644             :         //std::cout << " geom2=" << myGeom << " offset=" << offset;
     645           0 :         if (offset != GeomHelper::INVALID_OFFSET) {
     646           0 :             myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
     647             :         }
     648             :     } else {
     649             :         assert(node == myTo);
     650           0 :         myGeom.extrapolate(maxExtent, false, true);
     651           0 :         double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
     652             :         //std::cout << " geom2=" << myGeom << " offset=" << offset;
     653           0 :         if (offset != GeomHelper::INVALID_OFFSET) {
     654           0 :             myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
     655             :         }
     656             :     }
     657             :     //std::cout << " geom3=" << myGeom << "\n";
     658           0 : }
     659             : 
     660             : 
     661             : void
     662           2 : NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
     663             :     //std::cout << "shortenGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " reduction=" << reduction << " geom=" << myGeom;
     664           2 :     reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
     665           2 :     if (node == myFrom) {
     666           2 :         myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
     667             :     } else {
     668           2 :         myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
     669             :     }
     670           2 :     computeLaneShapes();
     671             :     //std::cout << " geom2=" << myGeom << "\n";
     672           2 : }
     673             : 
     674             : 
     675             : void
     676      327954 : NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
     677      327954 :     PositionVector border;
     678      327954 :     if (rectangularCut) {
     679             :         const double extend = 100;
     680        4754 :         border = myGeom.getOrthogonal(p, extend, node == myTo);
     681             :     } else {
     682      325577 :         border.push_back(p);
     683      325577 :         border.push_back(p2);
     684             :     }
     685      327954 :     if (border.size() == 2) {
     686      327954 :         border.extrapolate2D(getTotalWidth());
     687      327954 :         if (node == myFrom) {
     688             :             myFromBorder = border;
     689             :         } else {
     690             :             assert(node == myTo);
     691             :             myToBorder = border;
     692             :         }
     693             :     }
     694             : #ifdef DEBUG_NODE_BORDER
     695             :     gDebugFlag1 = DEBUGCOND;
     696             :     if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
     697             :                                  << " rect=" << rectangularCut
     698             :                                  << " p=" << p << " p2=" << p2
     699             :                                  << " border=" << border
     700             :                                  << " myGeom=" << myGeom
     701             :                                  << "\n";
     702             : 
     703             : #endif
     704      327954 : }
     705             : 
     706             : 
     707             : const PositionVector&
     708         280 : NBEdge::getNodeBorder(const NBNode* node) const {
     709         280 :     if (node == myFrom) {
     710         140 :         return myFromBorder;
     711             :     } else {
     712             :         assert(node == myTo);
     713         140 :         return myToBorder;
     714             :     }
     715             : }
     716             : 
     717             : 
     718             : void
     719       41264 : NBEdge::resetNodeBorder(const NBNode* node) {
     720       41264 :     if (node == myFrom) {
     721             :         myFromBorder.clear();
     722             :     } else {
     723             :         assert(node == myTo);
     724             :         myToBorder.clear();
     725             :     }
     726       41264 : }
     727             : 
     728             : 
     729             : bool
     730     6356271 : NBEdge::isBidiRail(bool ignoreSpread) const {
     731     6356271 :     return (isRailway(getPermissions())
     732      183994 :             && (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
     733      181910 :             && myPossibleTurnDestination != nullptr
     734      126875 :             && myPossibleTurnDestination->myPossibleTurnDestination == this
     735      116140 :             && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
     736      115998 :             && isRailway(myPossibleTurnDestination->getPermissions())
     737     6540265 :             && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
     738             : }
     739             : 
     740             : 
     741             : bool
     742     5905579 : NBEdge::isBidiEdge(bool checkPotential) const {
     743     5905579 :     return myPossibleTurnDestination != nullptr
     744     5526189 :            && myPossibleTurnDestination->myPossibleTurnDestination == this
     745     5251544 :            && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
     746        2570 :            && myPossibleTurnDestination->getToNode() == getFromNode()
     747        2570 :            && myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
     748             :            // geometry check a) full overlap geometry
     749     5908149 :            && ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
     750        1902 :                 && (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
     751           0 :                     || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
     752             :                // b) TWLT (Two-Way-Left-Turn-lane)
     753     5907198 :                || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
     754     5905579 :               );
     755             : 
     756             : }
     757             : 
     758             : 
     759             : bool
     760        3106 : NBEdge::isRailDeadEnd() const {
     761        3106 :     if (!isRailway(getPermissions())) {
     762             :         return false;
     763             :     }
     764        4938 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
     765        9296 :         if (isRailway(out->getPermissions()) &&
     766        4512 :                 out != getTurnDestination(true)) {
     767             :             return true;
     768             :         }
     769             :     }
     770             :     return true;
     771             : }
     772             : 
     773             : 
     774             : PositionVector
     775      417791 : NBEdge::cutAtIntersection(const PositionVector& old) const {
     776             :     PositionVector shape = old;
     777      835582 :     shape = startShapeAt(shape, myFrom, myFromBorder);
     778             : #ifdef DEBUG_CUT_LANES
     779             :     if (DEBUGCOND) {
     780             :         std::cout << getID() << " cutFrom=" << shape << "\n";
     781             :     }
     782             : #endif
     783      417791 :     if (shape.size() < 2) {
     784             :         // only keep the last snippet
     785         584 :         const double oldLength = old.length();
     786        1168 :         shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
     787             : #ifdef DEBUG_CUT_LANES
     788             :         if (DEBUGCOND) {
     789             :             std::cout << getID() << " cutFromFallback=" << shape << "\n";
     790             :         }
     791             : #endif
     792             :     }
     793      835582 :     shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
     794             : #ifdef DEBUG_CUT_LANES
     795             :     if (DEBUGCOND) {
     796             :         std::cout << getID() << " cutTo=" << shape << "\n";
     797             :     }
     798             : #endif
     799             :     // sanity checks
     800      417791 :     if (shape.length() < POSITION_EPS) {
     801         587 :         if (old.length() < 2 * POSITION_EPS) {
     802             :             shape = old;
     803             :         } else {
     804         582 :             const double midpoint = old.length() / 2;
     805             :             // EPS*2 because otherwhise shape has only a single point
     806        1164 :             shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
     807             :             assert(shape.size() >= 2);
     808             :             assert(shape.length() > 0);
     809             : #ifdef DEBUG_CUT_LANES
     810             :             if (DEBUGCOND) {
     811             :                 std::cout << getID() << " fallBackShort=" << shape << "\n";
     812             :             }
     813             : #endif
     814             :         }
     815             :     } else {
     816             :         // @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
     817             :         // in this case the result shape should shortened
     818      417204 :         if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
     819             :             // eliminate intermediate points
     820       46527 :             PositionVector tmp;
     821       46527 :             tmp.push_back(shape[0]);
     822       46527 :             tmp.push_back(shape[-1]);
     823             :             shape = tmp;
     824       46527 :             if (tmp.length() < POSITION_EPS) {
     825             :                 // fall back to original shape
     826          92 :                 if (old.length() < 2 * POSITION_EPS) {
     827             :                     shape = old;
     828             :                 } else {
     829          92 :                     const double midpoint = old.length() / 2;
     830             :                     // EPS*2 because otherwhise shape has only a single point
     831         184 :                     shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
     832             :                     assert(shape.size() >= 2);
     833             :                     assert(shape.length() > 0);
     834             :                 }
     835             : #ifdef DEBUG_CUT_LANES
     836             :                 if (DEBUGCOND) {
     837             :                     std::cout << getID() << " fallBackReversed=" << shape << "\n";
     838             :                 }
     839             : #endif
     840             :             } else {
     841       46435 :                 const double midpoint = shape.length() / 2;
     842             :                 // cut to size and reverse
     843       92870 :                 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
     844       46435 :                 if (shape.length() < POSITION_EPS) {
     845             :                     assert(false);
     846             :                     // the shape has a sharp turn near the midpoint
     847             :                 }
     848       92870 :                 shape = shape.reverse();
     849             : #ifdef DEBUG_CUT_LANES
     850             :                 if (DEBUGCOND) {
     851             :                     std::cout << getID() << " fallBackReversed2=" << shape << " mid=" << midpoint << "\n";
     852             :                 }
     853             : #endif
     854             :             }
     855             :             // make short edge flat (length <= 2 * POSITION_EPS)
     856       46527 :             const double z = (shape[0].z() + shape[1].z()) / 2;
     857       46527 :             shape[0].setz(z);
     858       46527 :             shape[1].setz(z);
     859       46527 :         }
     860             :     }
     861      417791 :     return shape;
     862           0 : }
     863             : 
     864             : 
     865             : void
     866      276508 : NBEdge::computeEdgeShape(double smoothElevationThreshold) {
     867      276508 :     if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
     868        1851 :         PositionVector cut = cutAtIntersection(myGeom);
     869             :         // cutting and patching z-coordinate may cause steep grades which should be smoothed
     870        1851 :         if (!myFrom->geometryLike()) {
     871        1741 :             cut[0].setz(myFrom->getPosition().z());
     872        1741 :             const double d = cut[0].distanceTo2D(cut[1]);
     873        1741 :             const double dZ = fabs(cut[0].z() - cut[1].z());
     874        1741 :             if (dZ / smoothElevationThreshold > d) {
     875         513 :                 cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
     876             :             }
     877             :         }
     878        1851 :         if (!myTo->geometryLike()) {
     879        1741 :             cut[-1].setz(myTo->getPosition().z());
     880        1741 :             const double d = cut[-1].distanceTo2D(cut[-2]);
     881        1741 :             const double dZ = fabs(cut[-1].z() - cut[-2].z());
     882        1741 :             if (dZ / smoothElevationThreshold > d) {
     883         508 :                 cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
     884             :             }
     885             :         }
     886        1851 :         cut[0] = myGeom[0];
     887        1851 :         cut[-1] = myGeom[-1];
     888        1851 :         if (cut != myGeom) {
     889             :             myGeom = cut;
     890          20 :             computeLaneShapes();
     891             :         }
     892        1851 :     }
     893      629121 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     894      705226 :         myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
     895             :     }
     896             :     // recompute edge's length as the average of lane lengths
     897             :     double avgLength = 0;
     898      629121 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     899      352613 :         avgLength += myLanes[i].shape.length();
     900             :     }
     901      276508 :     myLength = avgLength / (double) myLanes.size();
     902      276508 :     computeAngle(); // update angles using the finalized node and lane shapes
     903      276508 : }
     904             : 
     905             : 
     906             : PositionVector
     907      835862 : NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
     908      835862 :     if (nodeShape.size() == 0) {
     909      211974 :         nodeShape = startNode->getShape();
     910      211974 :         nodeShape.closePolygon();
     911             :     }
     912             :     PositionVector lb = laneShape;
     913      835862 :     lb.extrapolate2D(100.0);
     914      835862 :     if (nodeShape.intersects(laneShape)) {
     915             :         // shape intersects directly
     916      606524 :         std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
     917             :         assert(pbv.size() > 0);
     918             :         // ensure that the subpart has at least two points
     919      606524 :         double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
     920      606524 :         if (pb < 0) {
     921             :             return laneShape;
     922             :         }
     923      606500 :         PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
     924             :         //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
     925      606500 :         const double delta = ns[0].z() - laneShape[0].z();
     926             :         //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
     927      606500 :         if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
     928             :             // make "real" intersections and small intersections flat
     929             :             //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
     930        2837 :             ns[0].setz(startNode->getPosition().z());
     931             :         }
     932             :         assert(ns.size() >= 2);
     933             :         return ns;
     934      835838 :     } else if (nodeShape.intersects(lb)) {
     935             :         // extension of first segment intersects
     936       96220 :         std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
     937             :         assert(pbv.size() > 0);
     938             :         double pb = VectorHelper<double>::maxValue(pbv);
     939             :         assert(pb >= 0);
     940       96220 :         PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
     941       96220 :         Position np = lb.positionAtOffset2D(pb);
     942       96220 :         const double delta = np.z() - laneShape[0].z();
     943             :         //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
     944       96220 :         if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
     945             :             // avoid z-overshoot when extrapolating
     946             :             //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
     947             :             np.setz(startNode->getPosition().z());
     948             :         }
     949       96220 :         result.push_front_noDoublePos(np);
     950             :         return result;
     951             :         //if (result.size() >= 2) {
     952             :         //    return result;
     953             :         //} else {
     954             :         //    WRITE_WARNING(error + " (resulting shape is too short)");
     955             :         //    return laneShape;
     956             :         //}
     957       96220 :     } else {
     958             :         // could not find proper intersection. Probably the edge is very short
     959             :         // and lies within nodeShape
     960             :         // @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
     961             :         return laneShape;
     962             :     }
     963      835862 : }
     964             : 
     965             : 
     966             : const PositionVector&
     967     4166634 : NBEdge::getLaneShape(int i) const {
     968     4166634 :     return myLanes[i].shape;
     969             : }
     970             : 
     971             : 
     972             : void
     973        1432 : NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
     974        1432 :     myLaneSpreadFunction = spread;
     975        1432 : }
     976             : 
     977             : 
     978             : LaneSpreadFunction
     979      314906 : NBEdge::getLaneSpreadFunction() const {
     980      314906 :     return myLaneSpreadFunction;
     981             : }
     982             : 
     983             : 
     984             : void
     985         702 : NBEdge::addGeometryPoint(int index, const Position& p) {
     986         702 :     if (index >= 0) {
     987         351 :         myGeom.insert(myGeom.begin() + index, p);
     988             :     } else {
     989         351 :         myGeom.insert(myGeom.end() + index, p);
     990             :     }
     991         702 : }
     992             : 
     993             : 
     994             : void
     995         462 : NBEdge::reduceGeometry(const double minDist) {
     996             :     // attempt symmetrical removal for forward and backward direction
     997             :     // (very important for bidiRail)
     998         462 :     if (myFrom->getID() < myTo->getID()) {
     999         232 :         PositionVector reverse = myGeom.reverse();
    1000         232 :         reverse.removeDoublePoints(minDist, true, 0, 0, true);
    1001         464 :         myGeom = reverse.reverse();
    1002         628 :         for (Lane& lane : myLanes) {
    1003         792 :             reverse = lane.customShape.reverse();
    1004         396 :             reverse.removeDoublePoints(minDist, true, 0, 0, true);
    1005         792 :             lane.customShape = reverse.reverse();
    1006             :         }
    1007         232 :     } else {
    1008         230 :         myGeom.removeDoublePoints(minDist, true, 0, 0, true);
    1009         578 :         for (Lane& lane : myLanes) {
    1010         348 :             lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
    1011             :         }
    1012             :     }
    1013         462 : }
    1014             : 
    1015             : 
    1016             : void
    1017      101024 : NBEdge::checkGeometry(const double maxAngle, const double minRadius, bool fix, bool silent) {
    1018      101024 :     if (myGeom.size() < 3) {
    1019       52761 :         return;
    1020             :     }
    1021             :     //std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
    1022             :     std::vector<double> angles; // absolute segment angles
    1023             :     //std::cout << "  absolute angles:";
    1024      279129 :     for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
    1025      230784 :         angles.push_back(myGeom.angleAt2D(i));
    1026             :         //std::cout << " " << angles.back();
    1027             :     }
    1028             :     //std::cout << "\n  relative angles: ";
    1029      230415 :     for (int i = 0; i < (int)angles.size() - 1; ++i) {
    1030      182152 :         const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
    1031             :         //std::cout << relAngle << " ";
    1032      182152 :         if (maxAngle > 0 && relAngle > maxAngle && !silent) {
    1033         542 :             WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
    1034             :         }
    1035      182152 :         if (relAngle < DEG2RAD(1)) {
    1036       52771 :             continue;
    1037             :         }
    1038      129381 :         if (i == 0 || i == (int)angles.size() - 2) {
    1039             :             const bool start = i == 0;
    1040       54757 :             const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
    1041       54757 :             const double r = tan(0.5 * (M_PI - relAngle)) * dist;
    1042             :             //std::cout << (start ? "  start" : "  end") << " length=" << dist << " radius=" << r << "  ";
    1043       54757 :             if (minRadius > 0 && r < minRadius) {
    1044         930 :                 if (fix) {
    1045         246 :                     WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
    1046             :                                    toString(r), start ? TL("start") : "end", getID());
    1047          82 :                     myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
    1048          82 :                     checkGeometry(maxAngle, minRadius, fix, silent);
    1049          82 :                     return;
    1050         848 :                 } else if (!silent) {
    1051        2758 :                     WRITE_WARNINGF(TL("Found sharp turn with radius % at the % of edge '%'."),
    1052             :                                    toString(r), start ? "start" : "end", getID());
    1053             :                 }
    1054             :             }
    1055             :         }
    1056             :     }
    1057             :     //std::cout << "\n";
    1058             : }
    1059             : 
    1060             : 
    1061             : // ----------- Setting and getting connections
    1062             : bool
    1063       89019 : NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
    1064       89019 :     if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
    1065             :         return true;
    1066             :     }
    1067             :     // check whether the node was merged and now a connection between
    1068             :     //  not matching edges is tried to be added
    1069             :     //  This happens f.e. within the ptv VISSIM-example "Beijing"
    1070       89019 :     if (dest != nullptr && myTo != dest->myFrom) {
    1071             :         return false;
    1072             :     }
    1073       89019 :     if (dest == nullptr) {
    1074          82 :         invalidateConnections();
    1075         164 :         myConnections.push_back(Connection(-1, dest, -1));
    1076       88937 :     } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
    1077      149954 :         myConnections.push_back(Connection(-1, dest, -1));
    1078       74977 :         myConnections.back().permissions = permissions;
    1079             :     }
    1080       89019 :     if (overrideRemoval) {
    1081             :         // override earlier delete decision
    1082        1474 :         for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
    1083         597 :             if (it->toEdge == dest) {
    1084           2 :                 it = myConnectionsToDelete.erase(it);
    1085             :             } else {
    1086             :                 it++;
    1087             :             }
    1088             :         }
    1089             :     }
    1090       89019 :     if (myStep < EdgeBuildingStep::EDGE2EDGES) {
    1091       41749 :         myStep = EdgeBuildingStep::EDGE2EDGES;
    1092             :     }
    1093             :     return true;
    1094             : }
    1095             : 
    1096             : 
    1097             : bool
    1098       88109 : NBEdge::addLane2LaneConnection(int from, NBEdge* dest,
    1099             :                                int toLane, Lane2LaneInfoType type,
    1100             :                                bool mayUseSameDestination,
    1101             :                                bool mayDefinitelyPass,
    1102             :                                KeepClear keepClear,
    1103             :                                double contPos,
    1104             :                                double visibility,
    1105             :                                double speed,
    1106             :                                double friction,
    1107             :                                double length,
    1108             :                                const PositionVector& customShape,
    1109             :                                bool uncontrolled,
    1110             :                                SVCPermissions permissions,
    1111             :                                bool indirectLeft,
    1112             :                                const std::string& edgeType,
    1113             :                                SVCPermissions changeLeft,
    1114             :                                SVCPermissions changeRight,
    1115             :                                bool postProcess) {
    1116       88109 :     if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
    1117             :         return true;
    1118             :     }
    1119             :     // check whether the node was merged and now a connection between
    1120             :     //  not matching edges is tried to be added
    1121             :     //  This happens f.e. within the ptv VISSIM-example "Beijing"
    1122       88109 :     if (myTo != dest->myFrom) {
    1123             :         return false;
    1124             :     }
    1125       87489 :     if (!addEdge2EdgeConnection(dest)) {
    1126             :         return false;
    1127             :     }
    1128       87489 :     return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
    1129       87489 :                          customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
    1130             : }
    1131             : 
    1132             : 
    1133             : bool
    1134        1742 : NBEdge::addLane2LaneConnections(int fromLane,
    1135             :                                 NBEdge* dest, int toLane,
    1136             :                                 int no, Lane2LaneInfoType type,
    1137             :                                 bool invalidatePrevious,
    1138             :                                 bool mayDefinitelyPass) {
    1139        1742 :     if (invalidatePrevious) {
    1140         930 :         invalidateConnections(true);
    1141             :     }
    1142             :     bool ok = true;
    1143        4005 :     for (int i = 0; i < no && ok; i++) {
    1144        4526 :         ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
    1145             :     }
    1146        1742 :     return ok;
    1147             : }
    1148             : 
    1149             : 
    1150             : bool
    1151      270461 : NBEdge::setConnection(int lane, NBEdge* destEdge,
    1152             :                       int destLane, Lane2LaneInfoType type,
    1153             :                       bool mayUseSameDestination,
    1154             :                       bool mayDefinitelyPass,
    1155             :                       KeepClear keepClear,
    1156             :                       double contPos,
    1157             :                       double visibility,
    1158             :                       double speed,
    1159             :                       double friction,
    1160             :                       double length,
    1161             :                       const PositionVector& customShape,
    1162             :                       bool uncontrolled,
    1163             :                       SVCPermissions permissions,
    1164             :                       bool indirectLeft,
    1165             :                       const std::string& edgeType,
    1166             :                       SVCPermissions changeLeft,
    1167             :                       SVCPermissions changeRight,
    1168             :                       bool postProcess) {
    1169      270461 :     if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
    1170             :         return false;
    1171             :     }
    1172             :     // some kind of a misbehaviour which may occure when the junction's outgoing
    1173             :     //  edge priorities were not properly computed, what may happen due to
    1174             :     //  an incomplete or not proper input
    1175             :     // what happens is that under some circumstances a single lane may set to
    1176             :     //  be approached more than once by the one of our lanes.
    1177             :     //  This must not be!
    1178             :     // we test whether it is the case and do nothing if so - the connection
    1179             :     //  will be refused
    1180             :     //
    1181      270461 :     if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
    1182             :         return false;
    1183             :     }
    1184      256792 :     if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
    1185             :         return true;
    1186             :     }
    1187      256750 :     if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
    1188             :         // problem might be corrigible in post-processing
    1189          38 :         WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
    1190          19 :         return false;
    1191             :     }
    1192      826990 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1193      570259 :         if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
    1194      195920 :             if (permissions == SVC_UNSPECIFIED) {
    1195             :                 // @note: in case we were to add multiple connections from the
    1196             :                 // same lane the second one wouldn't get the special permissions!
    1197      195867 :                 permissions = (*i).permissions;
    1198             :             }
    1199      195920 :             i = myConnections.erase(i);
    1200             :         } else {
    1201             :             ++i;
    1202             :         }
    1203             :     }
    1204      513462 :     myConnections.push_back(Connection(lane, destEdge, destLane));
    1205      256731 :     if (mayDefinitelyPass) {
    1206          27 :         myConnections.back().mayDefinitelyPass = true;
    1207             :     }
    1208      256731 :     myConnections.back().keepClear = keepClear;
    1209      256731 :     myConnections.back().contPos = contPos;
    1210      256731 :     myConnections.back().visibility = visibility;
    1211      256731 :     myConnections.back().permissions = permissions;
    1212      256731 :     myConnections.back().indirectLeft = indirectLeft;
    1213      256731 :     myConnections.back().edgeType = edgeType;
    1214      256731 :     myConnections.back().changeLeft = changeLeft;
    1215      256731 :     myConnections.back().changeRight = changeRight;
    1216      256731 :     myConnections.back().speed = speed;
    1217      256731 :     myConnections.back().friction = friction;
    1218      256731 :     myConnections.back().customLength = length;
    1219             :     myConnections.back().customShape = customShape;
    1220      256731 :     myConnections.back().uncontrolled = uncontrolled;
    1221      256731 :     if (type == Lane2LaneInfoType::USER) {
    1222        4014 :         myStep = EdgeBuildingStep::LANES2LANES_USER;
    1223             :     } else {
    1224             :         // check whether we have to take another look at it later
    1225      252717 :         if (type == Lane2LaneInfoType::COMPUTED) {
    1226             :             // yes, the connection was set using an algorithm which requires a recheck
    1227      129426 :             myStep = EdgeBuildingStep::LANES2LANES_RECHECK;
    1228             :         } else {
    1229             :             // ok, let's only not recheck it if we did no add something that has to be rechecked
    1230      123291 :             if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
    1231       86670 :                 myStep = EdgeBuildingStep::LANES2LANES_DONE;
    1232             :             }
    1233             :         }
    1234             :     }
    1235      256731 :     if (postProcess) {
    1236             :         // override earlier delete decision
    1237          18 :         for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
    1238           0 :             if ((it->fromLane < 0 || it->fromLane == lane)
    1239           1 :                     && (it->toEdge == nullptr || it->toEdge == destEdge)
    1240           2 :                     && (it->toLane < 0 || it->toLane == destLane)) {
    1241           1 :                 it = myConnectionsToDelete.erase(it);
    1242             :             } else {
    1243             :                 it++;
    1244             :             }
    1245             :         }
    1246             :     }
    1247             :     return true;
    1248             : }
    1249             : 
    1250             : 
    1251             : std::vector<NBEdge::Connection>
    1252     3893441 : NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
    1253             :     std::vector<NBEdge::Connection> ret;
    1254    17867398 :     for (const Connection& c : myConnections) {
    1255    13973957 :         if ((lane < 0 || c.fromLane == lane)
    1256     9053641 :                 && (to == nullptr || to == c.toEdge)
    1257     9033071 :                 && (toLane < 0 || toLane == c.toLane)) {
    1258     9031525 :             ret.push_back(c);
    1259             :         }
    1260             :     }
    1261     3893441 :     return ret;
    1262           0 : }
    1263             : 
    1264             : 
    1265             : const NBEdge::Connection&
    1266      500396 : NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
    1267     1042233 :     for (const Connection& c : myConnections) {
    1268     1042233 :         if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
    1269      500396 :             return c;
    1270             :         }
    1271             :     }
    1272           0 :     throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
    1273           0 :                        + " to " + to->getID() + "_" + toString(toLane) + " not found");
    1274             : }
    1275             : 
    1276             : 
    1277             : NBEdge::Connection&
    1278           6 : NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
    1279          14 :     for (Connection& c : myConnections) {
    1280          14 :         if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
    1281           6 :             return c;
    1282             :         }
    1283             :     }
    1284           0 :     throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
    1285           0 :                        + " to " + to->getID() + "_" + toString(toLane) + " not found");
    1286             : }
    1287             : 
    1288             : 
    1289             : bool
    1290      320016 : NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
    1291      320016 :     return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
    1292             : }
    1293             : 
    1294             : 
    1295             : bool
    1296     1124673 : NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
    1297     1124673 :     if (!ignoreTurnaround && (e == myTurnDestination)) {
    1298             :         return true;
    1299             :     }
    1300             :     return
    1301     1114589 :         find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
    1302             :         !=
    1303             :         myConnections.end();
    1304             : 
    1305             : }
    1306             : 
    1307             : 
    1308             : const EdgeVector*
    1309       88416 : NBEdge::getConnectedSorted() {
    1310             :     // check whether connections exist and if not, use edges from the node
    1311             :     EdgeVector outgoing;
    1312       88416 :     if (myConnections.size() == 0) {
    1313       31119 :         outgoing = myTo->getOutgoingEdges();
    1314             :     } else {
    1315      171648 :         for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1316      114351 :             if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
    1317      113777 :                 outgoing.push_back((*i).toEdge);
    1318             :             }
    1319             :         }
    1320             :     }
    1321      100658 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    1322       12242 :         if (it->fromLane < 0 && it->toLane < 0) {
    1323             :             // found an edge that shall not be connected
    1324       12242 :             EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
    1325       12242 :             if (forbidden != outgoing.end()) {
    1326             :                 outgoing.erase(forbidden);
    1327             :             }
    1328             :         }
    1329             :     }
    1330             :     // allocate the sorted container
    1331       88416 :     int size = (int) outgoing.size();
    1332       88416 :     EdgeVector* edges = new EdgeVector();
    1333       88416 :     edges->reserve(size);
    1334      241178 :     for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
    1335      152762 :         NBEdge* outedge = *i;
    1336      152762 :         if (outedge != nullptr && outedge != myTurnDestination) {
    1337      140937 :             edges->push_back(outedge);
    1338             :         }
    1339             :     }
    1340       88416 :     std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
    1341       88416 :     return edges;
    1342             : }
    1343             : 
    1344             : 
    1345             : EdgeVector
    1346      159785 : NBEdge::getConnectedEdges() const {
    1347             :     EdgeVector ret;
    1348      539930 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1349      380145 :         if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
    1350      224090 :             ret.push_back((*i).toEdge);
    1351             :         }
    1352             :     }
    1353      159785 :     return ret;
    1354             : }
    1355             : 
    1356             : 
    1357             : EdgeVector
    1358        8562 : NBEdge::getIncomingEdges() const {
    1359             :     EdgeVector ret;
    1360        8562 :     const EdgeVector& candidates = myFrom->getIncomingEdges();
    1361       29673 :     for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
    1362       21111 :         if ((*i)->isConnectedTo(this)) {
    1363       19392 :             ret.push_back(*i);
    1364             :         }
    1365             :     }
    1366        8562 :     return ret;
    1367             : }
    1368             : 
    1369             : 
    1370             : std::vector<int>
    1371      641055 : NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
    1372             :     std::vector<int> ret;
    1373      641055 :     if (currentOutgoing != myTurnDestination) {
    1374     1917356 :         for (const Connection& c : myConnections) {
    1375     1331429 :             if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
    1376      514362 :                 ret.push_back(c.fromLane);
    1377             :             }
    1378             :         }
    1379             :     }
    1380      641055 :     return ret;
    1381             : }
    1382             : 
    1383             : 
    1384             : void
    1385      128821 : NBEdge::sortOutgoingConnectionsByAngle() {
    1386      128821 :     sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
    1387      128821 : }
    1388             : 
    1389             : 
    1390             : void
    1391      201906 : NBEdge::sortOutgoingConnectionsByIndex() {
    1392      201906 :     sort(myConnections.begin(), myConnections.end(), connections_sorter);
    1393      201906 : }
    1394             : 
    1395             : 
    1396             : void
    1397           0 : NBEdge::remapConnections(const EdgeVector& incoming) {
    1398           0 :     EdgeVector connected = getConnectedEdges();
    1399           0 :     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
    1400           0 :         NBEdge* inc = *i;
    1401             :         // We have to do this
    1402           0 :         inc->myStep = EdgeBuildingStep::EDGE2EDGES;
    1403             :         // add all connections
    1404           0 :         for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
    1405           0 :             inc->addEdge2EdgeConnection(*j);
    1406             :         }
    1407           0 :         inc->removeFromConnections(this);
    1408             :     }
    1409           0 : }
    1410             : 
    1411             : 
    1412             : void
    1413       76619 : NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
    1414             :                               const bool keepPossibleTurns) {
    1415             :     // remove from "myConnections"
    1416       76619 :     const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
    1417       76619 :     const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
    1418      105106 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1419             :         Connection& c = *i;
    1420       28487 :         if ((toEdge == nullptr || c.toEdge == toEdge)
    1421        1205 :                 && (fromLane < 0 || c.fromLane == fromLane)
    1422        1042 :                 && (toLane < 0 || c.toLane == toLane)) {
    1423         905 :             if (myTo->isTLControlled()) {
    1424             :                 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
    1425         380 :                 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
    1426         190 :                     (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
    1427             :                 }
    1428             :             }
    1429         905 :             i = myConnections.erase(i);
    1430             :             tryLater = false;
    1431         905 :         } else {
    1432       27582 :             if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
    1433         128 :                 if (myTo->isTLControlled()) {
    1434             :                     std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
    1435          10 :                     for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
    1436          83 :                         for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
    1437             :                             NBConnection& tc = *tlcon;
    1438          78 :                             if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
    1439          12 :                                 tc.shiftLaneIndex(this, -1);
    1440             :                             }
    1441             :                         }
    1442             :                     }
    1443             :                 }
    1444             :                 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
    1445         128 :                 c.fromLane--;
    1446             :             }
    1447       27582 :             if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
    1448             :                 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
    1449         101 :                 c.toLane--;
    1450             :             }
    1451             :             ++i;
    1452             :         }
    1453             :     }
    1454             :     // check whether it was the turn destination
    1455       76619 :     if (myTurnDestination == toEdge && fromLane < 0) {
    1456        4337 :         myTurnDestination = nullptr;
    1457             :     }
    1458       76619 :     if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
    1459        3498 :         myPossibleTurnDestination = nullptr;
    1460             :     }
    1461       76619 :     if (tryLater) {
    1462       24722 :         myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
    1463             : #ifdef DEBUG_CONNECTION_GUESSING
    1464             :         if (DEBUGCOND) {
    1465             :             std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
    1466             :             for (Connection& c : myConnections) {
    1467             :                 std::cout << "  conn " << c.getDescription(this) << "\n";
    1468             :             }
    1469             :             for (Connection& c : myConnectionsToDelete) {
    1470             :                 std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    1471             :             }
    1472             :         }
    1473             : #endif
    1474             :     }
    1475       76619 : }
    1476             : 
    1477             : 
    1478             : bool
    1479          10 : NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
    1480             :     // iterate over connections
    1481          12 :     for (auto i = myConnections.begin(); i !=  myConnections.end(); i++) {
    1482          12 :         if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
    1483             :             // remove connection
    1484          10 :             myConnections.erase(i);
    1485             :             return true;
    1486             :         }
    1487             :     }
    1488             :     // assert(false);
    1489             :     return false;
    1490             : }
    1491             : 
    1492             : 
    1493             : void
    1494        4235 : NBEdge::invalidateConnections(bool reallowSetting) {
    1495        4235 :     myTurnDestination = nullptr;
    1496             :     myConnections.clear();
    1497        4235 :     if (reallowSetting) {
    1498        4141 :         myStep = EdgeBuildingStep::INIT;
    1499             :     } else {
    1500          94 :         myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
    1501             :     }
    1502        4235 : }
    1503             : 
    1504             : 
    1505             : void
    1506         952 : NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
    1507             :     // replace in "_connectedEdges"
    1508        1666 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1509         714 :         if ((*i).toEdge == which) {
    1510         495 :             (*i).toEdge = by;
    1511         495 :             (*i).toLane += laneOff;
    1512             :         }
    1513             :     }
    1514             :     // check whether it was the turn destination
    1515         952 :     if (myTurnDestination == which) {
    1516          21 :         myTurnDestination = by;
    1517             :     }
    1518         952 : }
    1519             : 
    1520             : void
    1521       68064 : NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
    1522             :     std::map<int, int> laneMap;
    1523             :     int minLane = -1;
    1524             :     int maxLane = -1;
    1525             :     // get lanes used to approach the edge to remap
    1526             :     bool wasConnected = false;
    1527       74994 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1528        6930 :         if ((*i).toEdge != which) {
    1529        6229 :             continue;
    1530             :         }
    1531             :         wasConnected = true;
    1532         701 :         if ((*i).fromLane != -1) {
    1533             :             int fromLane = (*i).fromLane;
    1534         671 :             laneMap[(*i).toLane] = fromLane;
    1535         671 :             if (minLane == -1 || minLane > fromLane) {
    1536             :                 minLane = fromLane;
    1537             :             }
    1538         671 :             if (maxLane == -1 || maxLane < fromLane) {
    1539             :                 maxLane = fromLane;
    1540             :             }
    1541             :         }
    1542             :     }
    1543       68064 :     if (!wasConnected) {
    1544             :         return;
    1545             :     }
    1546             :     // add new connections
    1547         495 :     std::vector<NBEdge::Connection> conns = origConns;
    1548         495 :     EdgeVector origTargets = getSuccessors();
    1549        2545 :     for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
    1550        2050 :         if ((*i).toEdge == which || (*i).toEdge == this
    1551             :                 // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
    1552        4046 :                 || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
    1553             : #ifdef DEBUG_REPLACECONNECTION
    1554             :             if (DEBUGCOND) {
    1555             :                 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
    1556             :                           << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
    1557             :             }
    1558             : #endif
    1559         863 :             continue;
    1560             :         }
    1561        1190 :         if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
    1562             :             // do not set lane-level connections
    1563           3 :             replaceInConnections(which, (*i).toEdge, 0);
    1564           3 :             continue;
    1565             :         }
    1566        1187 :         int fromLane = (*i).fromLane;
    1567             :         int toUse = -1;
    1568        1187 :         if (laneMap.find(fromLane) == laneMap.end()) {
    1569         278 :             if (fromLane >= 0 && fromLane <= minLane) {
    1570             :                 toUse = minLane;
    1571             :                 // patch laneMap to avoid crossed-over connections
    1572         441 :                 for (auto& item : laneMap) {
    1573         241 :                     if (item.first < fromLane) {
    1574          35 :                         item.second = MIN2(item.second, minLane);
    1575             :                     }
    1576             :                 }
    1577             :             }
    1578         278 :             if (fromLane >= 0 && fromLane >= maxLane) {
    1579             :                 toUse = maxLane;
    1580             :                 // patch laneMap to avoid crossed-over connections
    1581         320 :                 for (auto& item : laneMap) {
    1582         175 :                     if (item.first > fromLane) {
    1583          62 :                         item.second = MAX2(item.second, maxLane);
    1584             :                     }
    1585             :                 }
    1586             :             }
    1587             :         } else {
    1588         909 :             toUse = laneMap[fromLane];
    1589             :         }
    1590        1187 :         if (toUse == -1) {
    1591             :             toUse = 0;
    1592             :         }
    1593             : #ifdef DEBUG_REPLACECONNECTION
    1594             :         if (DEBUGCOND) {
    1595             :             std::cout  << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
    1596             :                        << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
    1597             :                        << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
    1598             :         }
    1599             : #endif
    1600        2374 :         setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
    1601        1187 :                       i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
    1602             :     }
    1603             :     // remove the remapped edge from connections
    1604         495 :     removeFromConnections(which);
    1605         495 : }
    1606             : 
    1607             : 
    1608             : void
    1609         630 : NBEdge::copyConnectionsFrom(NBEdge* src) {
    1610         630 :     myStep = src->myStep;
    1611         630 :     myConnections = src->myConnections;
    1612         630 : }
    1613             : 
    1614             : 
    1615             : bool
    1616        1264 : NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
    1617             :     // only allow using newFromLane if at least 1 vClass is permitted to use
    1618             :     // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
    1619        1264 :     const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
    1620        1264 :     return (common > 0 && common != SVC_PEDESTRIAN);
    1621             : }
    1622             : 
    1623             : 
    1624             : void
    1625         560 : NBEdge::moveConnectionToLeft(int lane) {
    1626             :     int index = 0;
    1627        3428 :     for (int i = 0; i < (int)myConnections.size(); ++i) {
    1628        2868 :         if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
    1629             :             index = i;
    1630             :         }
    1631             :     }
    1632             :     std::vector<Connection>::iterator i = myConnections.begin() + index;
    1633         560 :     Connection c = *i;
    1634         560 :     myConnections.erase(i);
    1635         560 :     setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
    1636         560 : }
    1637             : 
    1638             : 
    1639             : void
    1640         108 : NBEdge::moveConnectionToRight(int lane) {
    1641         383 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1642         383 :         if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
    1643         108 :             Connection c = *i;
    1644         108 :             i = myConnections.erase(i);
    1645         108 :             setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
    1646             :             return;
    1647         108 :         }
    1648             :     }
    1649             : }
    1650             : 
    1651             : 
    1652             : double
    1653       66662 : NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
    1654       66662 :     const OptionsCont& oc = OptionsCont::getOptions();
    1655       66662 :     const int numPoints = oc.getInt("junctions.internal-link-detail");
    1656       66662 :     const bool joinTurns = oc.getBool("junctions.join-turns");
    1657       66662 :     const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
    1658       66662 :     const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
    1659       66662 :     const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
    1660       66662 :     const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
    1661       66662 :     const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
    1662       66662 :     const bool higherSpeed = oc.getBool("junctions.higher-speed");
    1663       66662 :     const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
    1664       66662 :     const bool fromRail = isRailway(getPermissions());
    1665       66662 :     std::string innerID = ":" + n.getID();
    1666             :     NBEdge* toEdge = nullptr;
    1667       66662 :     int edgeIndex = linkIndex;
    1668             :     int internalLaneIndex = 0;
    1669             :     int numLanes = 0; // number of lanes that share the same edge
    1670             :     double lengthSum = 0; // total shape length of all lanes that share the same edge
    1671             :     int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
    1672             :     bool averageLength = true;
    1673             :     double maxCross = 0.;
    1674      187977 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1675             :         Connection& con = *i;
    1676      121315 :         con.haveVia = false; // reset first since this may be called multiple times
    1677      121315 :         if (con.toEdge == nullptr) {
    1678           0 :             continue;
    1679             :         }
    1680      121315 :         LinkDirection dir = n.getDirection(this, con.toEdge);
    1681      121315 :         const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
    1682      121315 :         const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
    1683             :         // put turning internal lanes on separate edges
    1684      121315 :         if (con.toEdge != toEdge) {
    1685             :             // skip indices to keep some correspondence between edge ids and link indices:
    1686             :             // internalEdgeIndex + internalLaneIndex = linkIndex
    1687      109665 :             edgeIndex = linkIndex;
    1688             :             toEdge = con.toEdge;
    1689             :             internalLaneIndex = 0;
    1690      109665 :             maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
    1691             :             numLanes = 0;
    1692             :             lengthSum = 0;
    1693             :         }
    1694      121315 :         averageLength = !isTurn || joinTurns; // legacy behavior
    1695      121315 :         SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
    1696      121315 :         const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
    1697      121315 :         PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
    1698             :         std::vector<int> foeInternalLinks;
    1699             : 
    1700      121315 :         if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
    1701         260 :             WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
    1702             :         }
    1703             : 
    1704             :         // crossingPosition, list of foe link indices
    1705             :         std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
    1706             :         std::set<std::string> tmpFoeIncomingLanes;
    1707      121315 :         if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
    1708       75901 :             int index = 0;
    1709             :             std::vector<PositionVector> otherShapes;
    1710       75901 :             const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
    1711             :             const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
    1712      333943 :             for (const NBEdge* i2 : n.getIncomingEdges()) {
    1713     1178313 :                 for (const Connection& k2 : i2->getConnections()) {
    1714      920271 :                     if (k2.toEdge == nullptr) {
    1715           0 :                         continue;
    1716             :                     }
    1717             :                     // vehicles are typically less wide than the lane
    1718             :                     // they drive on but but bicycle lanes should be kept clear for their whole width
    1719      920271 :                     double width2 = k2.toEdge->getLaneWidth(k2.toLane);
    1720      920271 :                     if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
    1721      905428 :                         width2 *= 0.5;
    1722             :                     }
    1723      920271 :                     const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
    1724      920271 :                     LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
    1725      920271 :                     bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
    1726      920271 :                     const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
    1727       16262 :                     bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
    1728             :                     int shapeFlag = 0;
    1729      920271 :                     SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
    1730             :                     // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
    1731             :                     if (con.customShape.size() == 0
    1732      919675 :                             && k2.customShape.size() == 0
    1733      919372 :                             && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane  && avoidIntersectCandidate))
    1734      924420 :                             && ((i2->getPermissions(k2.fromLane) & warn) != 0
    1735        3410 :                                 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
    1736             :                         // recompute with different curve parameters (unless
    1737             :                         // the other connection is "unimportant"
    1738        3283 :                         shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
    1739             :                         PositionVector origShape = shape;
    1740        6566 :                         shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
    1741        3283 :                         oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
    1742        3283 :                         if (oppositeLeftIntersect
    1743        2121 :                                 && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
    1744             :                             shape = origShape;
    1745             :                         } else {
    1746             :                             // recompute previously computed crossing positions
    1747        3234 :                             if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
    1748        1769 :                                     || avoidedIntersectingLeftOriginLane < con.fromLane) {
    1749        3507 :                                 for (const PositionVector& otherShape : otherShapes) {
    1750        1988 :                                     const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1751        1988 :                                     const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
    1752        3976 :                                                                            "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1753        1988 :                                     if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1754             :                                         assert(minDV >= 0);
    1755        1840 :                                         if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1756         321 :                                             crossingPositions.first = minDV;
    1757             :                                         }
    1758             :                                     }
    1759             :                                 }
    1760             :                             }
    1761             :                             // make sure connections further to the left do not get a wider angle
    1762        3234 :                             avoidedIntersectingLeftOriginLane = con.fromLane;
    1763             :                         }
    1764        3283 :                     }
    1765      920271 :                     const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
    1766             :                     //std::cout << "n=" << n.getID() << " e1=" << getID() << " prio=" << getJunctionPriority(&n) << " e2=" << i2->getID() << " prio2=" << i2->getJunctionPriority(&n) << " both=" << bothPrio << " bothLeftIntersect=" << bothLeftIntersect(n, shape, dir, i2, k2, numPoints, width2) << " needsCont=" << needsCont << "\n";
    1767             :                     // the following special case might get obsolete once we have solved #9745
    1768      920271 :                     const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
    1769             :                     // compute the crossing point
    1770      920271 :                     if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
    1771       39366 :                         crossingPositions.second.push_back(index);
    1772       39366 :                         const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
    1773       39366 :                         otherShapes.push_back(otherShape);
    1774       39366 :                         const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1775       39366 :                         const double minDV = firstIntersection(shape, otherShape, width1, width2,
    1776       78732 :                                                                "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1777       39366 :                         if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1778             :                             assert(minDV >= 0);
    1779       33432 :                             if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1780       32771 :                                 crossingPositions.first = minDV;
    1781             :                             }
    1782             :                         }
    1783       39366 :                     }
    1784      920271 :                     const bool rightTurnConflict = NBNode::rightTurnConflict(
    1785      920271 :                                                        this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
    1786      920271 :                     const bool indirectTurnConflit = con.indirectLeft && this == i2 && dir2 == LinkDirection::STRAIGHT;
    1787      920271 :                     const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
    1788      920271 :                     const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
    1789      920271 :                     const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
    1790             :                     // compute foe internal lanes
    1791      920271 :                     if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
    1792      263818 :                         foeInternalLinks.push_back(index);
    1793             :                     }
    1794             :                     // only warn once per pair of intersecting turns
    1795        3005 :                     if (oppositeLeftIntersect && getID() > i2->getID()
    1796        1502 :                             && (getPermissions(con.fromLane) & warn) != 0
    1797        1196 :                             && (con.toEdge->getPermissions(con.toLane) & warn) != 0
    1798        1139 :                             && (i2->getPermissions(k2.fromLane) & warn) != 0
    1799        1053 :                             && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
    1800             :                             // do not warn for unregulated nodes
    1801      921303 :                             && n.getType() != SumoXMLNodeType::NOJUNCTION
    1802             :                        ) {
    1803         538 :                         WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
    1804             :                                        n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
    1805             :                     }
    1806             :                     // compute foe incoming lanes
    1807      920271 :                     const bool signalised = hasSignalisedConnectionTo(con.toEdge);
    1808     1682023 :                     if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
    1809      921046 :                             && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
    1810      282094 :                         tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
    1811             :                     }
    1812      920271 :                     if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
    1813             :                         //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
    1814             :                         // break symmetry using edge id
    1815             :                         // only store link index and resolve actual lane id later (might be multi-lane internal edge)
    1816         386 :                         tmpFoeIncomingLanes.insert(":" + toString(index));
    1817             :                     }
    1818      920271 :                     index++;
    1819             :                 }
    1820             :             }
    1821             :             // foe pedestrian crossings
    1822       75901 :             std::vector<NBNode::Crossing*> crossings = n.getCrossings();
    1823       91610 :             for (auto c : crossings) {
    1824             :                 const NBNode::Crossing& crossing = *c;
    1825       43380 :                 for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
    1826       27671 :                     const NBEdge* edge = *it_e;
    1827             :                     // compute foe internal lanes
    1828       27671 :                     if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
    1829        7110 :                         foeInternalLinks.push_back(index);
    1830        7110 :                         if (con.toEdge == edge &&
    1831        3552 :                                 ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
    1832             :                             // build internal junctions (not for left turns at uncontrolled intersections)
    1833             :                             PositionVector crossingShape = crossing.shape;
    1834        1553 :                             crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
    1835        1553 :                             const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
    1836        1553 :                             if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
    1837             :                                 assert(minDV >= 0);
    1838        1450 :                                 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1839         840 :                                     crossingPositions.first = minDV;
    1840             :                                 }
    1841             :                             }
    1842        1553 :                         }
    1843             :                     }
    1844             :                 }
    1845       15709 :                 index++;
    1846             :             }
    1847             : 
    1848       75901 :             if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
    1849             :                 // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
    1850             :                 // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
    1851         300 :                 crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
    1852             :             }
    1853       75901 :         }
    1854      121315 :         if (con.contPos != UNSPECIFIED_CONTPOS) {
    1855             :             // apply custom internal junction position
    1856          28 :             if (con.contPos <= 0 || con.contPos >= shape.length()) {
    1857             :                 // disable internal junction
    1858           1 :                 crossingPositions.first = -1;
    1859             :             } else {
    1860             :                 // set custom position
    1861          27 :                 crossingPositions.first = con.contPos;
    1862             :             }
    1863             :         }
    1864             : 
    1865             :         // @todo compute the maximum speed allowed based on angular velocity
    1866             :         //  see !!! for an explanation (with a_lat_mean ~0.3)
    1867             :         /*
    1868             :         double vmax = (double) 0.3 * (double) 9.80778 *
    1869             :                         getLaneShape(con.fromLane).back().distanceTo(
    1870             :                             con.toEdge->getLaneShape(con.toLane).front())
    1871             :                         / (double) 2.0 / (double) M_PI;
    1872             :         vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
    1873             :         */
    1874      121315 :         if (con.speed == UNSPECIFIED_SPEED) {
    1875      121130 :             if (higherSpeed) {
    1876          44 :                 con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
    1877             :             } else {
    1878      121106 :                 con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
    1879             :             }
    1880      121130 :             if (limitTurnSpeed > 0) {
    1881             :                 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
    1882      227196 :                 const double angleRaw = fabs(GeomHelper::angleDiff(
    1883      113598 :                                                  getLaneShape(con.fromLane).angleAt2D(-2),
    1884      113598 :                                                  con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
    1885      222688 :                 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
    1886      113598 :                 const double length = shape.length2D();
    1887             :                 // do not trust the radius of tiny junctions
    1888             :                 // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
    1889      113598 :                 if (angle > 0 && length > 1) {
    1890             :                     // permit higher turning speed on wide lanes
    1891       74636 :                     const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
    1892       74636 :                     const double limit = sqrt(limitTurnSpeed * radius);
    1893       74636 :                     const double reduction = con.vmax - limit;
    1894             :                     // always treat connctions at roundabout as turns when warning
    1895       74636 :                     const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
    1896             :                     const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
    1897       74374 :                     if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
    1898       74421 :                             || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
    1899         537 :                         std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
    1900         376 :                         if (atRoundabout) {
    1901             :                             dirType = "roundabout";
    1902             :                         }
    1903        1290 :                         WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
    1904             :                                        dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
    1905             :                     }
    1906      138575 :                     con.vmax = MIN2(con.vmax, limit);
    1907             :                     // value is saved in <net> attribute. Must be set again when importing from .con.xml
    1908             :                     // con.speed = con.vmax;
    1909             :                 }
    1910             :                 assert(con.vmax > 0);
    1911             :                 //if (getID() == "-1017000.0.00") {
    1912             :                 //    std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
    1913             :                 //        << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
    1914             :                 //}
    1915        7532 :             } else if (fromRail && dir == LinkDirection::TURN) {
    1916           0 :                 con.vmax = 0.01;
    1917             :             }
    1918             :         } else {
    1919         185 :             con.vmax = con.speed;
    1920             :         }
    1921      121315 :         if (con.friction == UNSPECIFIED_FRICTION) {
    1922      121315 :             con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
    1923             :         }
    1924             :         //
    1925             :         assert(shape.size() >= 2);
    1926             :         // get internal splits if any
    1927      242630 :         con.id = innerID + "_" + toString(edgeIndex);
    1928      121315 :         const double shapeLength = shape.length();
    1929             :         double firstLength = shapeLength;
    1930      121315 :         if (crossingPositions.first >= 0 && crossingPositions.first < shapeLength) {
    1931       22311 :             std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
    1932             :             con.shape = split.first;
    1933       22311 :             con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
    1934       22311 :             con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
    1935       44622 :             con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
    1936       22311 :             ++splitIndex;
    1937             :             con.viaShape = split.second;
    1938       22311 :             con.haveVia = true;
    1939       22311 :             firstLength = con.shape.length();
    1940       22311 :         } else {
    1941             :             con.shape = shape;
    1942             :         }
    1943      121315 :         con.internalLaneIndex = internalLaneIndex;
    1944      121315 :         ++internalLaneIndex;
    1945      121315 :         ++linkIndex;
    1946             :         ++numLanes;
    1947      121315 :         if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
    1948             :             // split length proportionally
    1949          28 :             lengthSum += firstLength / shapeLength * con.customLength;
    1950             :         } else {
    1951      121287 :             lengthSum += firstLength;
    1952             :         }
    1953      121315 :     }
    1954      133324 :     return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
    1955             : }
    1956             : 
    1957             : 
    1958             : double
    1959      176327 : NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
    1960             :     // assign average length to all lanes of the same internal edge if averageLength is set
    1961             :     // the lengthSum only covers the part up to the first internal junction
    1962             :     // TODO This code assumes that either all connections in question have a via or none
    1963             :     double maxCross = 0.;
    1964             :     assert(i - myConnections.begin() >= numLanes);
    1965      297642 :     for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
    1966             :         //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
    1967             :         Connection& c = (*(i - prevIndex));
    1968      121315 :         const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
    1969      121315 :         c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
    1970      121315 :         if (c.haveVia) {
    1971       44622 :             c.viaLength = MAX2(minLength, c.viaShape.length());
    1972             :         }
    1973      121315 :         if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
    1974          28 :             if (c.haveVia) {
    1975             :                 // split length proportionally
    1976           4 :                 const double a = c.viaLength / (c.shape.length() + c.viaLength);
    1977           6 :                 c.viaLength = MAX2(minLength, a * c.customLength);
    1978             :             }
    1979          28 :             if (!averageLength) {
    1980          19 :                 c.length = MAX2(minLength, c.customLength - c.viaLength);
    1981             :             }
    1982             :         }
    1983      121315 :         if (c.haveVia) {
    1984             :             // we need to be able to leave from the internal junction by accelerating from 0
    1985       22311 :             maxCross = MAX2(maxCross, sqrt(2. * c.viaLength)); // t = sqrt(2*s/a) and we assume 'a' is at least 1 (default value for tram in SUMOVTypeParameter)
    1986             :         }
    1987             :         // we need to be able to cross the junction in one go but not if we have an indirect left turn
    1988      121315 :         if (c.indirectLeft) {
    1989          15 :             maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    1990             :         } else {
    1991      173987 :             maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    1992             :         }
    1993             :     }
    1994      176327 :     return maxCross;
    1995             : }
    1996             : 
    1997             : 
    1998             : double
    1999       88300 : NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
    2000             :     double intersect = std::numeric_limits<double>::max();
    2001       88300 :     if (v2.length() < POSITION_EPS) {
    2002             :         return intersect;
    2003             :     }
    2004             :     try {
    2005             :         PositionVector v1Right = v1;
    2006       88262 :         v1Right.move2side(width1);
    2007             : 
    2008             :         PositionVector v1Left = v1;
    2009       88262 :         v1Left.move2side(-width1);
    2010             : 
    2011             :         PositionVector v2Right = v2;
    2012       88262 :         v2Right.move2side(width2);
    2013             : 
    2014             :         PositionVector v2Left = v2;
    2015       88262 :         v2Left.move2side(-width2);
    2016             : 
    2017             :         // intersect all border combinations
    2018             :         bool skip = secondIntersection;
    2019      116081 :         for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
    2020       27819 :             if (skip) {
    2021             :                 skip = false;
    2022           8 :                 continue;
    2023             :             }
    2024             :             intersect = MIN2(intersect, cand);
    2025             :         }
    2026             :         skip = secondIntersection;
    2027      131224 :         for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
    2028       42962 :             if (skip) {
    2029             :                 skip = false;
    2030          11 :                 continue;
    2031             :             }
    2032             :             intersect = MIN2(intersect, cand);
    2033             :         }
    2034             :         skip = secondIntersection;
    2035      138685 :         for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
    2036       50423 :             if (skip) {
    2037             :                 skip = false;
    2038           9 :                 continue;
    2039             :             }
    2040             :             intersect = MIN2(intersect, cand);
    2041             :         }
    2042             :         skip = secondIntersection;
    2043      132248 :         for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
    2044       43986 :             if (skip) {
    2045             :                 skip = false;
    2046          11 :                 continue;
    2047             :             }
    2048             :             intersect = MIN2(intersect, cand);
    2049             :         }
    2050       88262 :     } catch (InvalidArgument&) {
    2051           0 :         if (error != "") {
    2052           0 :             WRITE_WARNING(error);
    2053             :         }
    2054           0 :     }
    2055             :     //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
    2056             :     //std::cout << "  intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
    2057             :     //std::cout << "  intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
    2058             :     return intersect;
    2059             : }
    2060             : 
    2061             : 
    2062             : bool
    2063      660364 : NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
    2064      660364 :     if (otherFrom == this) {
    2065             :         // not an opposite pair
    2066             :         return false;
    2067             :     }
    2068      399104 :     return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
    2069             : }
    2070             : 
    2071             : bool
    2072       19545 : NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
    2073             :                          double width1, double width2, int shapeFlag) const {
    2074       19545 :     const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
    2075       19545 :     const double minDV = firstIntersection(shape, otherShape, width1, width2);
    2076       39090 :     return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
    2077       19545 : }
    2078             : 
    2079             : 
    2080             : // -----------
    2081             : int
    2082    12805149 : NBEdge::getJunctionPriority(const NBNode* const node) const {
    2083    12805149 :     if (node == myFrom) {
    2084      348288 :         return myFromJunctionPriority;
    2085             :     } else {
    2086    12456861 :         return myToJunctionPriority;
    2087             :     }
    2088             : }
    2089             : 
    2090             : 
    2091             : void
    2092      366847 : NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
    2093      366847 :     if (node == myFrom) {
    2094      181074 :         myFromJunctionPriority = prio;
    2095             : #ifdef DEBUG_JUNCTIONPRIO
    2096             :         setParameter("fromPrio", toString(prio));
    2097             : #endif
    2098             :     } else {
    2099      185773 :         myToJunctionPriority = prio;
    2100             : #ifdef DEBUG_JUNCTIONPRIO
    2101             :         setParameter("toPrio", toString(prio));
    2102             : #endif
    2103             :     }
    2104      366847 : }
    2105             : 
    2106             : 
    2107             : double
    2108    21260208 : NBEdge::getAngleAtNode(const NBNode* const atNode) const {
    2109    21260208 :     if (atNode == myFrom) {
    2110    10445589 :         return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
    2111             :     }
    2112             :     assert(atNode == myTo);
    2113    10814619 :     return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2114             : }
    2115             : 
    2116             : 
    2117             : double
    2118     4158636 : NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
    2119             :     double res;
    2120     4158636 :     if (atNode == myFrom) {
    2121     2072042 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
    2122             :     } else {
    2123             :         assert(atNode == myTo);
    2124     2086594 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2125             :     }
    2126     4158636 :     if (res < 0) {
    2127     3016771 :         res += 360;
    2128             :     }
    2129     4158636 :     return res;
    2130             : }
    2131             : 
    2132             : 
    2133             : double
    2134     6604974 : NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
    2135     6604974 :     if (atNode == myFrom) {
    2136     3112908 :         double res = myStartAngle - 180;
    2137     3112908 :         if (res < 0) {
    2138     1336169 :             res += 360;
    2139             :         }
    2140     3112908 :         return res;
    2141             :     } else {
    2142             :         assert(atNode == myTo);
    2143     3492066 :         return myEndAngle;
    2144             :     }
    2145             : }
    2146             : 
    2147             : 
    2148             : void
    2149      760681 : NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
    2150      760681 :     if (!onlyPossible) {
    2151      711485 :         myTurnDestination = e;
    2152             :     }
    2153      760681 :     myPossibleTurnDestination = e;
    2154      760681 : }
    2155             : 
    2156             : 
    2157             : double
    2158       11070 : NBEdge::getLaneSpeed(int lane) const {
    2159       11070 :     return myLanes[lane].speed;
    2160             : }
    2161             : 
    2162             : 
    2163             : double
    2164        3794 : NBEdge::getLaneFriction(int lane) const {
    2165        3794 :     return myLanes[lane].friction;
    2166             : }
    2167             : 
    2168             : 
    2169             : void
    2170          76 : NBEdge::resetLaneShapes() {
    2171          76 :     computeLaneShapes();
    2172          76 : }
    2173             : 
    2174             : 
    2175             : void
    2176           6 : NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
    2177          24 :     for (Lane& lane : myLanes) {
    2178          18 :         if (lane.changeLeft != SVCAll) {
    2179           6 :             lane.changeLeft = ignoring;
    2180             :         }
    2181          18 :         if (lane.changeRight != SVCAll) {
    2182           6 :             lane.changeRight = ignoring;
    2183             :         }
    2184             :     }
    2185          15 :     for (Connection& con : myConnections) {
    2186           9 :         if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
    2187           0 :             con.changeLeft = ignoring;
    2188             :         }
    2189           9 :         if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
    2190           0 :             con.changeRight = ignoring;
    2191             :         }
    2192             :     }
    2193           6 : }
    2194             : 
    2195             : 
    2196             : void
    2197      435736 : NBEdge::computeLaneShapes() {
    2198             :     // vissim needs this
    2199      435736 :     if (myFrom == myTo) {
    2200           0 :         return;
    2201             :     }
    2202             :     // compute lane offset, first
    2203      435736 :     std::vector<double> offsets(myLanes.size(), 0.);
    2204             :     double offset = 0;
    2205      557176 :     for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
    2206      121440 :         offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
    2207      121440 :         offsets[i] = offset;
    2208             :     }
    2209      435736 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    2210             :         double width = 0;
    2211      471234 :         for (int i = 0; i < (int)myLanes.size(); ++i) {
    2212      266177 :             width += getLaneWidth(i);
    2213             :         }
    2214      205057 :         offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
    2215             :     } else {
    2216      230679 :         double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
    2217      230679 :         offset = laneWidth / 2.;
    2218             :     }
    2219      435736 :     if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
    2220         428 :         for (NBEdge* e : myTo->getOutgoingEdges()) {
    2221         375 :             if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
    2222         203 :                 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
    2223         203 :                 break;
    2224             :             }
    2225             :         }
    2226             :     }
    2227             : 
    2228      992912 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2229      557176 :         offsets[i] += offset;
    2230             :     }
    2231             : 
    2232             :     // build the shape of each lane
    2233      992912 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2234      557176 :         if (myLanes[i].customShape.size() != 0) {
    2235             :             myLanes[i].shape = myLanes[i].customShape;
    2236          18 :             continue;
    2237             :         }
    2238             :         try {
    2239     1114316 :             myLanes[i].shape = computeLaneShape(i, offsets[i]);
    2240           0 :         } catch (InvalidArgument& e) {
    2241           0 :             WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
    2242             :             myLanes[i].shape = myGeom;
    2243           0 :         }
    2244             :     }
    2245             : }
    2246             : 
    2247             : 
    2248             : PositionVector
    2249      557158 : NBEdge::computeLaneShape(int lane, double offset) const {
    2250             :     PositionVector shape = myGeom;
    2251             :     try {
    2252      557158 :         shape.move2side(offset);
    2253           0 :     } catch (InvalidArgument& e) {
    2254           0 :         WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
    2255           0 :     }
    2256      557158 :     return shape;
    2257           0 : }
    2258             : 
    2259             : 
    2260             : void
    2261      544192 : NBEdge::computeAngle() {
    2262             :     // taking the angle at the first might be unstable, thus we take the angle
    2263             :     // at a certain distance. (To compare two edges, additional geometry
    2264             :     // segments are considered to resolve ambiguities)
    2265      544192 :     const bool hasFromShape = myFrom->getShape().size() > 0;
    2266      544192 :     const bool hasToShape = myTo->getShape().size() > 0;
    2267      544192 :     Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
    2268      544192 :     Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
    2269             :     PositionVector shape = myGeom;
    2270      544192 :     if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
    2271      283779 :         if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
    2272      136456 :             shape = myLanes[getNumLanes() - 1].shape ;
    2273             :         } else {
    2274      147323 :             shape = myLanes[getNumLanes() / 2].shape;
    2275      147323 :             if (getNumLanes() % 2 == 0) {
    2276             :                 // there is no center lane. shift to get the center
    2277       17824 :                 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
    2278             :             }
    2279             :         }
    2280             :     }
    2281             : 
    2282             :     // if the junction shape is suspicious we cannot trust the angle to the centroid
    2283      544192 :     const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
    2284      235387 :                                      || myFrom->getShape().around(shape[-1])
    2285      234326 :                                      || !(myFrom->getShape().around(fromCenter)));
    2286      544192 :     const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
    2287      227903 :                                    || myTo->getShape().around(shape[0])
    2288      227009 :                                    || !(myTo->getShape().around(toCenter)));
    2289             : 
    2290      544192 :     const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
    2291      544192 :     const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
    2292      544192 :     const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
    2293             : 
    2294      544192 :     myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
    2295      544192 :     const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
    2296      544192 :     const double myStartAngle3 = getAngleAtNode(myFrom);
    2297      544192 :     myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
    2298      544192 :     const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
    2299      544192 :     const double myEndAngle3 = getAngleAtNode(myTo);
    2300             : 
    2301             : #ifdef DEBUG_ANGLES
    2302             :     if (DEBUGCOND) {
    2303             :         if (suspiciousFromShape) {
    2304             :             std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
    2305             :                       << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
    2306             :                       << " fromCenter=" << fromCenter
    2307             :                       << " fromPos=" << myFrom->getPosition()
    2308             :                       << " refStart=" << referencePosStart
    2309             :                       << "\n";
    2310             :         }
    2311             :         if (suspiciousToShape) {
    2312             :             std::cout << "suspiciousToShape len=" << shape.length() << "  endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
    2313             :                       << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
    2314             :                       << " toCenter=" << toCenter
    2315             :                       << " toPos=" << myTo->getPosition()
    2316             :                       << " refEnd=" << referencePosEnd
    2317             :                       << "\n";
    2318             :         }
    2319             :     }
    2320             : #endif
    2321             : 
    2322      544192 :     if (suspiciousFromShape && shape.length() > 1) {
    2323       21800 :         myStartAngle = myStartAngle2;
    2324       62741 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
    2325             :                // don't trust footpath angles
    2326      528818 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2327        4210 :         myStartAngle = myStartAngle3;
    2328        4210 :         if (myStartAngle < 0) {
    2329        2079 :             myStartAngle += 360;
    2330             :         }
    2331             :     }
    2332             : 
    2333      544192 :     if (suspiciousToShape && shape.length() > 1) {
    2334       30367 :         myEndAngle = myEndAngle2;
    2335       35539 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
    2336             :                // don't trust footpath angles
    2337      521705 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2338        4674 :         myEndAngle = myEndAngle3;
    2339        4674 :         if (myEndAngle < 0) {
    2340        2278 :             myEndAngle += 360;
    2341             :         }
    2342             :     }
    2343             : 
    2344      544192 :     myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
    2345             : #ifdef DEBUG_ANGLES
    2346             :     if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
    2347             :                                  << " fromCenter=" << fromCenter << " toCenter=" << toCenter
    2348             :                                  << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
    2349             :                                  << " hasFromShape=" << hasFromShape
    2350             :                                  << " hasToShape=" << hasToShape
    2351             :                                  << " numLanes=" << getNumLanes()
    2352             :                                  << " shapeLane=" << getNumLanes() / 2
    2353             :                                  << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
    2354             : #endif
    2355      544192 : }
    2356             : 
    2357             : 
    2358             : double
    2359      234622 : NBEdge::getShapeStartAngle() const {
    2360      234622 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2361      234622 :     const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
    2362      234622 :     return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
    2363             : }
    2364             : 
    2365             : 
    2366             : double
    2367      115515 : NBEdge::getShapeEndAngle() const {
    2368      115515 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2369      115515 :     const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
    2370      115515 :     return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
    2371             : }
    2372             : 
    2373             : 
    2374             : bool
    2375           0 : NBEdge::hasPermissions() const {
    2376           0 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2377           0 :         if ((*i).permissions != SVCAll) {
    2378             :             return true;
    2379             :         }
    2380             :     }
    2381             :     return false;
    2382             : }
    2383             : 
    2384             : 
    2385             : bool
    2386        5885 : NBEdge::hasLaneSpecificPermissions() const {
    2387             :     std::vector<Lane>::const_iterator i = myLanes.begin();
    2388        5885 :     SVCPermissions firstLanePermissions = i->permissions;
    2389             :     i++;
    2390        7851 :     for (; i != myLanes.end(); ++i) {
    2391        3405 :         if (i->permissions != firstLanePermissions) {
    2392             :             return true;
    2393             :         }
    2394             :     }
    2395             :     return false;
    2396             : }
    2397             : 
    2398             : 
    2399             : bool
    2400        5628 : NBEdge::hasLaneSpecificSpeed() const {
    2401       16386 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2402       10773 :         if (i->speed != getSpeed()) {
    2403             :             return true;
    2404             :         }
    2405             :     }
    2406             :     return false;
    2407             : }
    2408             : 
    2409             : bool
    2410        2350 : NBEdge::hasLaneSpecificFriction() const {
    2411        6185 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2412        3839 :         if (i->friction != myLanes.begin()->friction) {
    2413             :             return true;
    2414             :         }
    2415             :     }
    2416             :     return false;
    2417             : }
    2418             : 
    2419             : bool
    2420       41180 : NBEdge::hasLaneSpecificWidth() const {
    2421       93377 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2422       53924 :         if (i->width != myLanes.begin()->width) {
    2423             :             return true;
    2424             :         }
    2425             :     }
    2426             :     return false;
    2427             : }
    2428             : 
    2429             : 
    2430             : bool
    2431        2086 : NBEdge::hasLaneSpecificType() const {
    2432        4978 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2433        2894 :         if (i->type != myLanes.begin()->type) {
    2434             :             return true;
    2435             :         }
    2436             :     }
    2437             :     return false;
    2438             : }
    2439             : 
    2440             : 
    2441             : bool
    2442       40446 : NBEdge::hasLaneSpecificEndOffset() const {
    2443       93886 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2444       53457 :         if (i->endOffset != myLanes.begin()->endOffset) {
    2445             :             return true;
    2446             :         }
    2447             :     }
    2448             :     return false;
    2449             : }
    2450             : 
    2451             : 
    2452             : bool
    2453       42772 : NBEdge::hasLaneSpecificStopOffsets() const {
    2454      100004 :     for (const auto& lane : myLanes) {
    2455       57237 :         if (lane.laneStopOffset.isDefined()) {
    2456           5 :             if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
    2457             :                 return true;
    2458             :             }
    2459             :         }
    2460             :     }
    2461             :     return false;
    2462             : }
    2463             : 
    2464             : 
    2465             : bool
    2466        2078 : NBEdge::hasAccelLane() const {
    2467        4947 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2468        2870 :         if (i->accelRamp) {
    2469             :             return true;
    2470             :         }
    2471             :     }
    2472             :     return false;
    2473             : }
    2474             : 
    2475             : 
    2476             : bool
    2477        2077 : NBEdge::hasCustomLaneShape() const {
    2478        4936 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2479        2864 :         if (i->customShape.size() > 0) {
    2480             :             return true;
    2481             :         }
    2482             :     }
    2483             :     return false;
    2484             : }
    2485             : 
    2486             : 
    2487             : bool
    2488        2072 : NBEdge::hasLaneParams() const {
    2489        4925 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2490        2856 :         if (i->getParametersMap().size() > 0) {
    2491             :             return true;
    2492             :         }
    2493             :     }
    2494             :     return false;
    2495             : }
    2496             : 
    2497             : bool
    2498        2069 : NBEdge::prohibitsChanging() const {
    2499        4904 :     for (const Lane& lane : myLanes) {
    2500        2842 :         if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
    2501             :             return true;
    2502             :         }
    2503             :     }
    2504             :     return false;
    2505             : }
    2506             : 
    2507             : bool
    2508        2350 : NBEdge::needsLaneSpecificOutput() const {
    2509        2350 :     return (hasLaneSpecificPermissions()
    2510        2093 :             || hasLaneSpecificSpeed()
    2511        2090 :             || hasLaneSpecificWidth()
    2512        2086 :             || hasLaneSpecificType()
    2513        2084 :             || hasLaneSpecificEndOffset()
    2514        2078 :             || hasLaneSpecificStopOffsets()
    2515        2078 :             || hasAccelLane()
    2516        2077 :             || hasCustomLaneShape()
    2517        2072 :             || hasLaneParams()
    2518        2069 :             || prohibitsChanging()
    2519        4412 :             || (!myLanes.empty() && myLanes.back().oppositeID != ""));
    2520             : }
    2521             : 
    2522             : 
    2523             : 
    2524             : bool
    2525      128127 : NBEdge::computeEdge2Edges(bool noLeftMovers) {
    2526             : #ifdef DEBUG_CONNECTION_GUESSING
    2527             :     if (DEBUGCOND) {
    2528             :         std::cout << "computeEdge2Edges  edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
    2529             :         for (Connection& c : myConnections) {
    2530             :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2531             :         }
    2532             :         for (Connection& c : myConnectionsToDelete) {
    2533             :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2534             :         }
    2535             :     }
    2536             : #endif
    2537             :     // return if this relationship has been build in previous steps or
    2538             :     //  during the import
    2539      128127 :     if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
    2540             :         return true;
    2541             :     }
    2542       87700 :     const bool fromRail = isRailway(getPermissions());
    2543      302788 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
    2544      215088 :         if (noLeftMovers && myTo->isLeftMover(this, out)) {
    2545        1140 :             continue;
    2546             :         }
    2547             :         // avoid sharp railway turns
    2548      213948 :         if (fromRail && isRailway(out->getPermissions())) {
    2549       12137 :             const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
    2550       12137 :             if (angle > 150) {
    2551        4673 :                 continue;
    2552        7464 :             } else if (angle > 90) {
    2553             :                 // possibly the junction is large enough to achieve a plausible radius:
    2554         173 :                 const PositionVector& fromShape = myLanes.front().shape;
    2555         173 :                 const PositionVector& toShape = out->getLanes().front().shape;
    2556         173 :                 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
    2557         173 :                 const double radius = shape.length2D() / DEG2RAD(angle);
    2558         173 :                 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
    2559             :                 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
    2560         173 :                 if (radius < minRadius) {
    2561             :                     continue;
    2562             :                 }
    2563         173 :             }
    2564             :         }
    2565      209107 :         if (out == myTurnDestination) {
    2566             :             // will be added by appendTurnaround
    2567       53507 :             continue;
    2568             :         }
    2569      155600 :         if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
    2570             :             // no common permissions
    2571       43761 :             continue;
    2572             :         }
    2573      223678 :         myConnections.push_back(Connection(-1, out, -1));
    2574             :     }
    2575       87700 :     myStep = EdgeBuildingStep::EDGE2EDGES;
    2576       87700 :     return true;
    2577             : }
    2578             : 
    2579             : 
    2580             : bool
    2581      128127 : NBEdge::computeLanes2Edges() {
    2582             : #ifdef DEBUG_CONNECTION_GUESSING
    2583             :     if (DEBUGCOND) {
    2584             :         std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
    2585             :         for (Connection& c : myConnections) {
    2586             :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2587             :         }
    2588             :         for (Connection& c : myConnectionsToDelete) {
    2589             :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2590             :         }
    2591             :     }
    2592             : #endif
    2593             :     // return if this relationship has been build in previous steps or
    2594             :     //  during the import
    2595      128127 :     if (myStep >= EdgeBuildingStep::LANES2EDGES) {
    2596             :         return true;
    2597             :     }
    2598             :     assert(myStep == EdgeBuildingStep::EDGE2EDGES);
    2599             :     // get list of possible outgoing edges sorted by direction clockwise
    2600             :     //  the edge in the backward direction (turnaround) is not in the list
    2601       88416 :     const EdgeVector* edges = getConnectedSorted();
    2602       88416 :     if (myConnections.size() != 0 && edges->size() == 0) {
    2603             :         // dead end per definition!?
    2604             :         myConnections.clear();
    2605             :     } else {
    2606             :         // divide the lanes on reachable edges
    2607       88300 :         divideOnEdges(edges);
    2608             :     }
    2609      176832 :     delete edges;
    2610       88416 :     myStep = EdgeBuildingStep::LANES2EDGES;
    2611       88416 :     return true;
    2612             : }
    2613             : 
    2614             : 
    2615             : std::vector<LinkDirection>
    2616         358 : NBEdge::decodeTurnSigns(int turnSigns, int shift) {
    2617             :     std::vector<LinkDirection> result;
    2618        3222 :     for (int i = 0; i < 8; i++) {
    2619             :         // see LinkDirection in SUMOXMLDefinitions.h
    2620        2864 :         if ((turnSigns & (1 << (i + shift))) != 0) {
    2621         295 :             result.push_back((LinkDirection)(1 << i));
    2622             :         }
    2623             :     }
    2624         358 :     return result;
    2625             : }
    2626             : 
    2627             : void
    2628         252 : NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
    2629         252 :     if (dirs.size() > 0) {
    2630          71 :         if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
    2631           2 :             perm &= ~spec;
    2632             :         } else {
    2633          69 :             perm |= spec;
    2634             :         }
    2635             :     }
    2636         252 : }
    2637             : 
    2638             : bool
    2639          31 : NBEdge::applyTurnSigns() {
    2640             : #ifdef DEBUG_TURNSIGNS
    2641             :     std::cout << "applyTurnSigns edge=" << getID() << "\n";
    2642             : #endif
    2643             :     // build a map of target edges and lanes
    2644             :     std::vector<const NBEdge*> targets;
    2645             :     std::map<const NBEdge*, std::vector<int> > toLaneMap;
    2646         126 :     for (const Connection& c : myConnections) {
    2647          95 :         if (myLanes[c.fromLane].turnSigns != 0) {
    2648          95 :             if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
    2649          78 :                 targets.push_back(c.toEdge);
    2650             :             }
    2651          95 :             toLaneMap[c.toEdge].push_back(c.toLane);
    2652             :         }
    2653             :     }
    2654             :     // might be unsorted due to bike lane connections
    2655         109 :     for (auto& item : toLaneMap) {
    2656          78 :         std::sort(item.second.begin(), item.second.end());
    2657             :     }
    2658             : 
    2659             :     // check number of distinct signed directions and count the number of signs for each direction
    2660             :     std::map<LinkDirection, int> signCons;
    2661             :     int allDirs = 0;
    2662         103 :     for (const Lane& lane : myLanes) {
    2663          72 :         allDirs |= lane.turnSigns;
    2664         162 :         for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
    2665          90 :             signCons[dir]++;
    2666             :         }
    2667             :     }
    2668          31 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
    2669          31 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
    2670          31 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
    2671             : 
    2672          31 :     if ((allDirs & (int)LinkDirection::NODIR) != 0) {
    2673           3 :         targets.push_back(nullptr); // dead end
    2674             :     }
    2675             : 
    2676             :     SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
    2677             :     // build a mapping from sign directions to targets
    2678          31 :     std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
    2679             :     std::map<LinkDirection, const NBEdge*> dirMap;
    2680             : #ifdef DEBUG_TURNSIGNS
    2681             :     std::cout << "  numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
    2682             : #endif
    2683          31 :     if (signedDirs.size() > targets.size()) {
    2684          18 :         WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
    2685           6 :         return false;
    2686          25 :     } else if (signedDirs.size() < targets.size()) {
    2687             :         // we need to drop some targets (i.e. turn-around)
    2688             :         // use sumo-directions as a guide
    2689             :         std::vector<LinkDirection> sumoDirs;
    2690          67 :         for (const NBEdge* to : targets) {
    2691          53 :             sumoDirs.push_back(myTo->getDirection(this, to));
    2692             :         }
    2693             :         // remove targets to the left
    2694             :         bool checkMore = true;
    2695          29 :         while (signedDirs.size() < targets.size() && checkMore) {
    2696             :             checkMore = false;
    2697             :             //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
    2698          15 :             if (sumoDirs.back() != signedDirs.back()) {
    2699             :                 targets.pop_back();
    2700             :                 sumoDirs.pop_back();
    2701             :                 checkMore = true;
    2702             :             }
    2703             :         }
    2704             :         // remove targets to the right
    2705             :         checkMore = true;
    2706          16 :         while (signedDirs.size() < targets.size() && checkMore) {
    2707             :             checkMore = false;
    2708           2 :             if (sumoDirs.front() != signedDirs.front()) {
    2709             :                 targets.erase(targets.begin());
    2710             :                 sumoDirs.erase(sumoDirs.begin());
    2711             :                 checkMore = true;
    2712             :             }
    2713             :         }
    2714             :         // remove targets by permissions
    2715             :         int i = 0;
    2716          19 :         while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
    2717           5 :             if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
    2718             :                 targets.erase(targets.begin() + i);
    2719             :                 sumoDirs.erase(sumoDirs.begin() + i);
    2720             :             } else {
    2721           5 :                 i++;
    2722             :             }
    2723             :         }
    2724          14 :         if (signedDirs.size() != targets.size()) {
    2725           6 :             WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
    2726             :             return false;
    2727             :         }
    2728             :     }
    2729             :     // directions and connections are both sorted from right to left
    2730          80 :     for (int i = 0; i < (int)signedDirs.size(); i++) {
    2731          57 :         dirMap[signedDirs[i]] = targets[i];
    2732             :     }
    2733             :     // check whether we have enough target lanes for a each signed direction
    2734          76 :     for (auto item : signCons) {
    2735          55 :         const LinkDirection dir = item.first;
    2736          55 :         if (dir == LinkDirection::NODIR) {
    2737           2 :             continue;
    2738             :         }
    2739          53 :         const NBEdge* to = dirMap[dir];
    2740          53 :         int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
    2741          53 :         if (candidates == 0) {
    2742           4 :             WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
    2743           2 :             return false;
    2744             :         }
    2745          52 :         std::vector<int>& knownTargets = toLaneMap[to];
    2746          52 :         if ((int)knownTargets.size() < item.second) {
    2747           4 :             if (candidates < item.second) {
    2748           4 :                 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
    2749             :                                getID(), item.second, toString(dir), to->getID(), candidates);
    2750           1 :                 return false;
    2751             :             }
    2752             :             int i;
    2753             :             int iInc;
    2754             :             int iEnd;
    2755           3 :             if (dir > LinkDirection::STRAIGHT) {
    2756             :                 // set more targets on the left
    2757           1 :                 i = to->getNumLanes() - 1;
    2758             :                 iInc = -1;
    2759             :                 iEnd = -1;
    2760             :             } else {
    2761             :                 // set more targets on the right
    2762           2 :                 i = 0;
    2763             :                 iInc = 1;
    2764           2 :                 iEnd = to->getNumLanes();
    2765             :             }
    2766           8 :             while ((int)knownTargets.size() < item.second && i != iEnd) {
    2767           5 :                 if ((to->getPermissions(i) & defaultPermissions) != 0) {
    2768           5 :                     if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
    2769           3 :                         knownTargets.push_back(i);
    2770             :                     }
    2771             :                 }
    2772           5 :                 i += iInc;
    2773             :             }
    2774           3 :             if ((int)knownTargets.size() != item.second) {
    2775           0 :                 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
    2776           0 :                 return false;
    2777             :             }
    2778           3 :             std::sort(knownTargets.begin(), knownTargets.end());
    2779             :         }
    2780             :     }
    2781             :     std::map<const NBEdge*, int> toLaneIndex;
    2782          72 :     for (int i = 0; i < getNumLanes(); i++) {
    2783          51 :         const int turnSigns = myLanes[i].turnSigns;
    2784             :         // no turnSigns are given for bicycle lanes and sidewalks
    2785          51 :         if (turnSigns != 0) {
    2786             :             // clear existing connections
    2787         225 :             for (auto it = myConnections.begin(); it != myConnections.end();) {
    2788         174 :                 if (it->fromLane == i) {
    2789          70 :                     it = myConnections.erase(it);
    2790             :                 } else {
    2791             :                     it++;
    2792             :                 }
    2793             :             }
    2794             :             // add new connections
    2795          51 :             int allSigns = (turnSigns
    2796          51 :                             | turnSigns >> TURN_SIGN_SHIFT_BUS
    2797          51 :                             | turnSigns >> TURN_SIGN_SHIFT_TAXI
    2798          51 :                             | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
    2799          51 :             std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
    2800          51 :             std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
    2801          51 :             std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
    2802          51 :             std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
    2803             :             //std::cout << "  allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
    2804         114 :             for (LinkDirection dir : decodeTurnSigns(allSigns)) {
    2805          63 :                 SVCPermissions perm = 0;
    2806          63 :                 updateTurnPermissions(perm, dir, SVCAll, all);
    2807          63 :                 updateTurnPermissions(perm, dir, SVC_BUS, bus);
    2808          63 :                 updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
    2809          63 :                 updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
    2810          63 :                 if (perm == SVCAll) {
    2811          61 :                     perm = SVC_UNSPECIFIED;
    2812             :                 }
    2813             :                 //std::cout << "   lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
    2814          63 :                 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
    2815          63 :                 if (to != nullptr) {
    2816             :                     if (toLaneIndex.count(to) == 0) {
    2817             :                         // initialize to rightmost feasible lane
    2818          50 :                         SVCPermissions fromP = getPermissions(i);
    2819          50 :                         if ((fromP & SVC_PASSENGER) != 0) {
    2820             :                             // if the source permits passenger traffic, the target should too
    2821             :                             fromP = SVC_PASSENGER;
    2822             :                         }
    2823          50 :                         int toLane = toLaneMap[to][0];
    2824          52 :                         while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
    2825             :                             toLane++;
    2826             :                             /*
    2827             :                             if (toLane == to->getNumLanes()) {
    2828             :                                 SOFT_ASSERT(false);
    2829             :                             #ifdef DEBUG_TURNSIGNS
    2830             :                                 std::cout << "  could not find passenger lane for target=" << to->getID() << "\n";
    2831             :                             #endif
    2832             :                                 return false;
    2833             :                             }
    2834             :                             */
    2835             :                         }
    2836             : #ifdef DEBUG_TURNSIGNS
    2837             :                         std::cout << "  target=" << to->getID() << " initial toLane=" << toLane << "\n";
    2838             : #endif
    2839          50 :                         toLaneIndex[to] = toLane;
    2840             :                     }
    2841          61 :                     setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
    2842             :                                   false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
    2843             :                                   UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
    2844             :                                   myDefaultConnectionLength, PositionVector::EMPTY,
    2845             :                                   UNSPECIFIED_CONNECTION_UNCONTROLLED,
    2846             :                                   perm);
    2847          61 :                     if (toLaneIndex[to] < to->getNumLanes() - 1) {
    2848          13 :                         toLaneIndex[to]++;
    2849             :                     }
    2850             :                 }
    2851             :             }
    2852             :         }
    2853             :     }
    2854          21 :     sortOutgoingConnectionsByAngle();
    2855          21 :     sortOutgoingConnectionsByIndex();
    2856             :     return true;
    2857             : }
    2858             : 
    2859             : 
    2860             : bool
    2861      128122 : NBEdge::recheckLanes() {
    2862             : #ifdef DEBUG_CONNECTION_GUESSING
    2863             :     if (DEBUGCOND) {
    2864             :         std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
    2865             :         for (Connection& c : myConnections) {
    2866             :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2867             :         }
    2868             :         for (Connection& c : myConnectionsToDelete) {
    2869             :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2870             :         }
    2871             :     }
    2872             : #endif
    2873             :     // check delayed removals
    2874      140378 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    2875       12256 :         removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
    2876             :     }
    2877      128122 :     std::vector<int> connNumbersPerLane(myLanes.size(), 0);
    2878      381379 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    2879      253257 :         if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
    2880           1 :             i = myConnections.erase(i);
    2881             :         } else {
    2882             :             if ((*i).fromLane >= 0) {
    2883      253256 :                 ++connNumbersPerLane[(*i).fromLane];
    2884             :             }
    2885             :             ++i;
    2886             :         }
    2887             :     }
    2888      128122 :     if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
    2889             : #ifdef DEBUG_TURNSIGNS
    2890             :         if (myLanes.back().turnSigns != 0) {
    2891             :             std::cout << getID() << " hasTurnSigns\n";
    2892             :             if (myTurnSignTarget != myTo->getID()) {
    2893             :                 std::cout << "   tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
    2894             :             }
    2895             :         }
    2896             : #endif
    2897       75104 :         if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
    2898             :             // check #1:
    2899             :             // If there is a lane with no connections and any neighbour lane has
    2900             :             //  more than one connections, try to move one of them.
    2901             :             // This check is only done for edges which connections were assigned
    2902             :             //  using the standard algorithm.
    2903      170895 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    2904       95812 :                 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
    2905             :                     // dead-end lane found
    2906             :                     bool hasDeadEnd = true;
    2907             :                     // find lane with two connections or more to the right of the current lane
    2908        4078 :                     for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
    2909        2414 :                         if (getPermissions(i) != getPermissions(i2)) {
    2910             :                             break;
    2911             :                         }
    2912        1703 :                         if (connNumbersPerLane[i2] > 1) {
    2913         378 :                             connNumbersPerLane[i2]--;
    2914         938 :                             for (int i3 = i2; i3 != i; i3++) {
    2915         560 :                                 moveConnectionToLeft(i3);
    2916         560 :                                 sortOutgoingConnectionsByAngle();
    2917         560 :                                 sortOutgoingConnectionsByIndex();
    2918             :                             }
    2919             :                             hasDeadEnd = false;
    2920             :                         }
    2921             :                     }
    2922        2375 :                     if (hasDeadEnd) {
    2923             :                         // find lane with two connections or more to the left of the current lane
    2924        3091 :                         for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
    2925        1211 :                             if (getPermissions(i) != getPermissions(i2)) {
    2926             :                                 break;
    2927             :                             }
    2928        1094 :                             if (connNumbersPerLane[i2] > 1) {
    2929          79 :                                 connNumbersPerLane[i2]--;
    2930         187 :                                 for (int i3 = i2; i3 != i; i3--) {
    2931         108 :                                     moveConnectionToRight(i3);
    2932         108 :                                     sortOutgoingConnectionsByAngle();
    2933         108 :                                     sortOutgoingConnectionsByIndex();
    2934             :                                 }
    2935             :                                 hasDeadEnd = false;
    2936             :                             }
    2937             :                         }
    2938             :                     }
    2939        2375 :                     if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
    2940             :                         int passengerLanes = 0;
    2941             :                         int passengerTargetLanes = 0;
    2942        1165 :                         for (const Lane& lane : myLanes) {
    2943         896 :                             if ((lane.permissions & SVC_PASSENGER) != 0) {
    2944         743 :                                 passengerLanes++;
    2945             :                             }
    2946             :                         }
    2947        1041 :                         for (const NBEdge* out : myTo->getOutgoingEdges()) {
    2948         772 :                             if (!isTurningDirectionAt(out)) {
    2949        1514 :                                 for (const Lane& lane : out->getLanes()) {
    2950         981 :                                     if ((lane.permissions & SVC_PASSENGER) != 0) {
    2951         832 :                                         passengerTargetLanes++;
    2952             :                                     }
    2953             :                                 }
    2954             :                             }
    2955             :                         }
    2956         269 :                         if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
    2957             :                             // no need for dead-ends
    2958          72 :                             if (i > 0) {
    2959             :                                 // check if a connection to the right has a usable target to the left of its target
    2960          50 :                                 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
    2961          50 :                                 if (rightCons.size() > 0) {
    2962             :                                     const Connection& rc = rightCons.back();
    2963          40 :                                     NBEdge* to = rc.toEdge;
    2964          40 :                                     int toLane = rc.toLane + 1;
    2965             :                                     if (toLane < to->getNumLanes()
    2966           0 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    2967          40 :                                             && !hasConnectionTo(to, toLane)) {
    2968           0 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    2969             :                                         hasDeadEnd = false;
    2970           0 :                                         sortOutgoingConnectionsByAngle();
    2971           0 :                                         sortOutgoingConnectionsByIndex();
    2972             :                                     }
    2973             :                                     if (hasDeadEnd) {
    2974             :                                         // check if a connection to the right has a usable target to the right of its target
    2975          40 :                                         toLane = rc.toLane - 1;
    2976             :                                         if (toLane >= 0
    2977           7 :                                                 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
    2978           7 :                                                 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    2979          45 :                                                 && !hasConnectionTo(to, toLane)) {
    2980             :                                             // shift the right lane connection target right and connect the dead lane to the old target
    2981           1 :                                             getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
    2982           1 :                                             setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
    2983             :                                             hasDeadEnd = false;
    2984           1 :                                             sortOutgoingConnectionsByAngle();
    2985           1 :                                             sortOutgoingConnectionsByIndex();
    2986             :                                         }
    2987             :                                     }
    2988             :                                 }
    2989          50 :                             }
    2990          72 :                             if (hasDeadEnd && i < getNumLanes() - 1) {
    2991             :                                 // check if a connection to the left has a usable target to the right of its target
    2992          58 :                                 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
    2993          58 :                                 if (leftCons.size() > 0) {
    2994          48 :                                     NBEdge* to = leftCons.front().toEdge;
    2995          48 :                                     int toLane = leftCons.front().toLane - 1;
    2996             :                                     if (toLane >= 0
    2997           4 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    2998          52 :                                             && !hasConnectionTo(to, toLane)) {
    2999           4 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    3000             :                                         hasDeadEnd = false;
    3001           4 :                                         sortOutgoingConnectionsByAngle();
    3002           4 :                                         sortOutgoingConnectionsByIndex();
    3003             :                                     }
    3004             :                                 }
    3005          58 :                             }
    3006             : #ifdef ADDITIONAL_WARNINGS
    3007             :                             if (hasDeadEnd) {
    3008             :                                 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
    3009             :                             }
    3010             : #endif
    3011             :                         }
    3012             :                     }
    3013             :                 }
    3014             :             }
    3015             :             // check restrictions
    3016      240591 :             for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3017             :                 Connection& c = *i;
    3018      165508 :                 const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
    3019      165508 :                 if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
    3020             :                     // these are computed in NBNode::buildWalkingAreas
    3021         652 :                     i = myConnections.erase(i);
    3022      164856 :                 } else if (common == 0) {
    3023             :                     // no common permissions.
    3024             :                     // try to find a suitable target lane to the right
    3025          90 :                     const int origToLane = c.toLane;
    3026          90 :                     c.toLane = -1; // ignore this connection when calling hasConnectionTo
    3027             :                     int toLane = origToLane;
    3028          90 :                     while (toLane > 0
    3029          63 :                             && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3030         172 :                             && !hasConnectionTo(c.toEdge, toLane)
    3031             :                           ) {
    3032          41 :                         toLane--;
    3033             :                     }
    3034          90 :                     if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3035          90 :                             && !hasConnectionTo(c.toEdge, toLane)) {
    3036           4 :                         c.toLane = toLane;
    3037             :                         ++i;
    3038             :                     } else {
    3039             :                         // try to find a suitable target lane to the left
    3040             :                         toLane = origToLane;
    3041         160 :                         while (toLane < (int)c.toEdge->getNumLanes() - 1
    3042         100 :                                 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3043         234 :                                 && !hasConnectionTo(c.toEdge, toLane)
    3044             :                               ) {
    3045          74 :                             toLane++;
    3046             :                         }
    3047          86 :                         if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3048          86 :                                 && !hasConnectionTo(c.toEdge, toLane)) {
    3049           0 :                             c.toLane = toLane;
    3050             :                             ++i;
    3051             :                         } else {
    3052             :                             // no alternative target found
    3053          86 :                             i = myConnections.erase(i);
    3054             :                         }
    3055             :                     }
    3056      175009 :                 } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
    3057      174835 :                            && isTurningDirectionAt(c.toEdge))  {
    3058             :                     // do not allow sharp rail turns
    3059        3001 :                     i = myConnections.erase(i);
    3060             :                 } else {
    3061             :                     ++i;
    3062             :                 }
    3063             :             }
    3064             :         }
    3065             :     }
    3066             :     // check involuntary dead end at "real" junctions
    3067      128122 :     if (getPermissions() != SVC_PEDESTRIAN) {
    3068      107126 :         if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    3069         227 :             WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
    3070             :         }
    3071      107126 :         const EdgeVector& incoming = myFrom->getIncomingEdges();
    3072      107126 :         if (incoming.size() > 1) {
    3073      180038 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    3074      101042 :                 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
    3075             :                     bool connected = false;
    3076      128178 :                     for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
    3077      128034 :                         if ((*in)->hasConnectionTo(this, i)) {
    3078             :                             connected = true;
    3079             :                             break;
    3080             :                         }
    3081             :                     }
    3082       96019 :                     if (!connected) {
    3083         438 :                         WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
    3084             :                     }
    3085             :                 }
    3086             :             }
    3087             :         }
    3088             :     }
    3089             :     // avoid deadend due to change prohibitions
    3090      128122 :     if (getNumLanes() > 1 && myConnections.size() > 0) {
    3091       79985 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    3092       56490 :             Lane& lane = myLanes[i];
    3093       49046 :             if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
    3094          51 :                                                 && getSuccessors(SVC_PASSENGER).size() > 1))
    3095       56505 :                     && getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
    3096        2587 :                 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
    3097        2587 :                 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
    3098        2587 :                 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
    3099           2 :                     lane.changeLeft = SVC_UNSPECIFIED;
    3100           4 :                     WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
    3101        2585 :                 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
    3102           1 :                     lane.changeRight = SVC_UNSPECIFIED;
    3103           2 :                     WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
    3104             :                 }
    3105             :             }
    3106             :         }
    3107             :     }
    3108             : #ifdef ADDITIONAL_WARNINGS
    3109             :     // check for connections with bad access permissions
    3110             :     for (const Connection& c : myConnections) {
    3111             :         SVCPermissions fromP = getPermissions(c.fromLane);
    3112             :         SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
    3113             :         if ((fromP & SVC_PASSENGER) != 0
    3114             :                 && toP == SVC_BICYCLE) {
    3115             :             bool hasAlternative = false;
    3116             :             for (const Connection& c2 : myConnections) {
    3117             :                 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
    3118             :                         && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
    3119             :                     hasAlternative = true;
    3120             :                 }
    3121             :             }
    3122             :             if (!hasAlternative) {
    3123             :                 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
    3124             :             }
    3125             :         }
    3126             :     }
    3127             : 
    3128             : #endif
    3129             : #ifdef DEBUG_CONNECTION_GUESSING
    3130             :     if (DEBUGCOND) {
    3131             :         std::cout << "recheckLanes (final) edge=" << getID() << "\n";
    3132             :         for (Connection& c : myConnections) {
    3133             :             std::cout << "  conn " << c.getDescription(this) << "\n";
    3134             :         }
    3135             :     }
    3136             : #endif
    3137      128122 :     return true;
    3138             : }
    3139             : 
    3140             : 
    3141             : void
    3142       88300 : NBEdge::divideOnEdges(const EdgeVector* outgoing) {
    3143       88300 :     if (outgoing->size() == 0) {
    3144             :         // we have to do this, because the turnaround may have been added before
    3145             :         myConnections.clear();
    3146       15203 :         return;
    3147             :     }
    3148             : 
    3149             : #ifdef DEBUG_CONNECTION_GUESSING
    3150             :     if (DEBUGCOND) {
    3151             :         std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
    3152             :     }
    3153             : #endif
    3154             : 
    3155             :     // build connections for miv lanes
    3156             :     std::vector<int> availableLanes;
    3157      165393 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3158       92296 :         if ((getPermissions(i) & SVC_PASSENGER) != 0) {
    3159       56662 :             availableLanes.push_back(i);
    3160             :         }
    3161             :     }
    3162       73097 :     if (availableLanes.size() > 0) {
    3163       42455 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3164             :     }
    3165             :     // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
    3166             :     availableLanes.clear();
    3167      165393 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3168       92296 :         const SVCPermissions perms = getPermissions(i);
    3169       92296 :         if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
    3170       78973 :             continue;
    3171             :         }
    3172       13323 :         availableLanes.push_back(i);
    3173             :     }
    3174       73097 :     if (availableLanes.size() > 0) {
    3175       13220 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3176             :     }
    3177             :     // build connections for busses from lanes that were excluded in the previous step
    3178             :     availableLanes.clear();
    3179      165393 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3180       92296 :         const SVCPermissions perms = getPermissions(i);
    3181       92296 :         if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
    3182       91981 :             continue;
    3183             :         }
    3184         315 :         availableLanes.push_back(i);
    3185             :     }
    3186       73097 :     if (availableLanes.size() > 0) {
    3187         312 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3188             :     }
    3189             :     // build connections for bicycles (possibly combined with pedestrians)
    3190             :     availableLanes.clear();
    3191      165393 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3192       92296 :         const SVCPermissions perms = getPermissions(i);
    3193       92296 :         if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
    3194       90020 :             continue;
    3195             :         }
    3196        2276 :         availableLanes.push_back(i);
    3197             :     }
    3198       73097 :     if (availableLanes.size() > 0) {
    3199        2251 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3200             :     }
    3201             :     // clean up unassigned fromLanes
    3202             :     bool explicitTurnaround = false;
    3203       73097 :     SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
    3204      311540 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3205      238443 :         if ((*i).fromLane == -1) {
    3206      113046 :             if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
    3207             :                 explicitTurnaround = true;
    3208         172 :                 turnaroundPermissions = (*i).permissions;
    3209             :             }
    3210      113046 :             if ((*i).permissions != SVC_UNSPECIFIED) {
    3211        2409 :                 for (Connection& c : myConnections) {
    3212        2096 :                     if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
    3213             :                         // carry over loaded edge2edge permissions
    3214         562 :                         c.permissions = (*i).permissions;
    3215             :                     }
    3216             :                 }
    3217             :             }
    3218      113046 :             i = myConnections.erase(i);
    3219             :         } else {
    3220             :             ++i;
    3221             :         }
    3222             :     }
    3223       73097 :     if (explicitTurnaround) {
    3224         344 :         myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
    3225         172 :         myConnections.back().permissions = turnaroundPermissions;
    3226             :     }
    3227       73097 :     sortOutgoingConnectionsByIndex();
    3228             : }
    3229             : 
    3230             : 
    3231             : void
    3232       58238 : NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3233       58238 :     const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
    3234       58238 :     if (priorities.empty()) {
    3235             :         return;
    3236             :     }
    3237             : #ifdef DEBUG_CONNECTION_GUESSING
    3238             :     if (DEBUGCOND) {
    3239             :         std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
    3240             :     }
    3241             : #endif
    3242             :     // compute the resulting number of lanes that should be used to reach the following edge
    3243       58172 :     const int numOutgoing = (int)outgoing->size();
    3244             :     std::vector<int> resultingLanesFactor;
    3245       58172 :     resultingLanesFactor.reserve(numOutgoing);
    3246             :     int minResulting = std::numeric_limits<int>::max();
    3247      172285 :     for (int i = 0; i < numOutgoing; i++) {
    3248             :         // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
    3249      114113 :         const int res = priorities[i] * (int)availableLanes.size();
    3250      114113 :         resultingLanesFactor.push_back(res);
    3251      114113 :         if (minResulting > res && res > 0) {
    3252             :             // prevent minResulting from becoming 0
    3253             :             minResulting = res;
    3254             :         }
    3255             :     }
    3256             :     // compute the number of virtual edges
    3257             :     //  a virtual edge is used as a replacement for a real edge from now on
    3258             :     //  it shall allow to divide the existing lanes on this structure without
    3259             :     //  regarding the structure of outgoing edges
    3260             :     int numVirtual = 0;
    3261             :     // compute the transition from virtual to real edges
    3262             :     EdgeVector transition;
    3263       58172 :     transition.reserve(numOutgoing);
    3264      172285 :     for (int i = 0; i < numOutgoing; i++) {
    3265             :         // tmpNum will be the number of connections from this edge to the next edge
    3266             :         assert(i < (int)resultingLanesFactor.size());
    3267      114113 :         const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
    3268      114113 :         numVirtual += tmpNum;
    3269      598218 :         for (int j = 0; j < tmpNum; j++) {
    3270      484105 :             transition.push_back((*outgoing)[i]);
    3271             :         }
    3272             :     }
    3273             : #ifdef DEBUG_CONNECTION_GUESSING
    3274             :     if (DEBUGCOND) {
    3275             :         std::cout << "   minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
    3276             :     }
    3277             : #endif
    3278             : 
    3279             :     // assign lanes to edges
    3280             :     //  (conversion from virtual to real edges is done)
    3281             :     ToEdgeConnectionsAdder adder(transition);
    3282       58172 :     Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
    3283             :     const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
    3284      172285 :     for (NBEdge* const target : *outgoing) {
    3285             :         assert(l2eConns.find(target) != l2eConns.end());
    3286      241334 :         for (const int j : l2eConns.find(target)->second) {
    3287      127221 :             const int fromIndex = availableLanes[j];
    3288      127221 :             if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
    3289             :                 // exclude connection if fromLane and toEdge have no common permissions
    3290          69 :                 continue;
    3291             :             }
    3292      127152 :             if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
    3293             :                 // exclude connection if the only commonly permitted class are pedestrians
    3294             :                 // these connections are later built in NBNode::buildWalkingAreas
    3295         410 :                 continue;
    3296             :             }
    3297             :             // avoid building more connections than the edge has viable lanes (earlier
    3298             :             // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
    3299             :             //    @todo To decide which target lanes are still available we need to do a
    3300             :             // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
    3301      126742 :             const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3302             :             int targetLanes = target->getNumLanes();
    3303      126742 :             if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3304        9344 :                 --targetLanes;
    3305             :             }
    3306      126742 :             if (numConsToTarget >= targetLanes) {
    3307        3088 :                 continue;
    3308             :             }
    3309      123654 :             if (myLanes[fromIndex].connectionsDone) {
    3310             :                 // we already have complete information about connections from
    3311             :                 // this lane. do not add anything else
    3312             : #ifdef DEBUG_CONNECTION_GUESSING
    3313             :                 if (DEBUGCOND) {
    3314             :                     std::cout << "     connectionsDone from " << getID() << "_" << fromIndex << ": ";
    3315             :                     for (const Connection& c : getConnectionsFromLane(fromIndex)) {
    3316             :                         std::cout << c.getDescription(this) << ", ";
    3317             :                     }
    3318             :                     std::cout << "\n";
    3319             :                 }
    3320             : #endif
    3321          46 :                 continue;
    3322             :             }
    3323      247216 :             myConnections.push_back(Connection(fromIndex, target, -1));
    3324             : #ifdef DEBUG_CONNECTION_GUESSING
    3325             :             if (DEBUGCOND) {
    3326             :                 std::cout << "     request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3327             :             }
    3328             : #endif
    3329             :         }
    3330             :     }
    3331             : 
    3332       58172 :     addStraightConnections(outgoing, availableLanes, priorities);
    3333             : }
    3334             : 
    3335             : 
    3336             : void
    3337       58172 : NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
    3338             :     // ensure sufficient straight connections for the (highest-priority) straight target
    3339       58172 :     const int numOutgoing = (int) outgoing->size();
    3340             :     NBEdge* target = nullptr;
    3341             :     NBEdge* rightOfTarget = nullptr;
    3342             :     NBEdge* leftOfTarget = nullptr;
    3343             :     int maxPrio = 0;
    3344      172285 :     for (int i = 0; i < numOutgoing; i++) {
    3345      114113 :         if (maxPrio < priorities[i]) {
    3346       94860 :             const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
    3347       94860 :             if (dir == LinkDirection::STRAIGHT) {
    3348       48305 :                 maxPrio = priorities[i];
    3349       48305 :                 target = (*outgoing)[i];
    3350       48305 :                 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
    3351       48305 :                 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
    3352             :             }
    3353             :         }
    3354             :     }
    3355       58172 :     if (target == nullptr) {
    3356             :         return;
    3357             :     }
    3358       48117 :     int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3359             :     int targetLanes = (int)target->getNumLanes();
    3360       48117 :     if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3361        4258 :         --targetLanes;
    3362             :     }
    3363       48117 :     const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
    3364             : #ifdef DEBUG_CONNECTION_GUESSING
    3365             :     if (DEBUGCOND) {
    3366             :         std::cout << "  checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
    3367             :     }
    3368             : #endif
    3369             :     std::vector<int>::const_iterator it_avail = availableLanes.begin();
    3370       49325 :     while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
    3371        1208 :         const int fromIndex = *it_avail;
    3372             :         if (
    3373             :             // not yet connected
    3374        1208 :             (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
    3375             :             // matching permissions
    3376         684 :             && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
    3377             :             // more than pedestrians
    3378         682 :             && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
    3379             :             // lane not yet fully defined
    3380        1888 :             && !myLanes[fromIndex].connectionsDone
    3381             :         ) {
    3382             : #ifdef DEBUG_CONNECTION_GUESSING
    3383             :             if (DEBUGCOND) {
    3384             :                 std::cout << "    candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3385             :             }
    3386             : #endif
    3387             :             // prevent same-edge conflicts
    3388             :             if (
    3389             :                 // no outgoing connections to the right from further left
    3390         322 :                 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3391             :                 // no outgoing connections to the left from further right
    3392        1326 :                 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
    3393             : #ifdef DEBUG_CONNECTION_GUESSING
    3394             :                 if (DEBUGCOND) {
    3395             :                     std::cout << "     request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3396             :                 }
    3397             : #endif
    3398        1200 :                 myConnections.push_back(Connection(fromIndex, target, -1));
    3399         600 :                 numConsToTarget++;
    3400             :             } else {
    3401             : #ifdef DEBUG_CONNECTION_GUESSING
    3402             :                 if (DEBUGCOND) std::cout
    3403             :                             << "     fail check1="
    3404             :                             << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3405             :                             << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
    3406             :                             << " rightOfTarget=" << rightOfTarget->getID()
    3407             :                             << " leftOfTarget=" << leftOfTarget->getID()
    3408             :                             << "\n";
    3409             : #endif
    3410             : 
    3411             :             }
    3412             :         }
    3413             :         ++it_avail;
    3414             :     }
    3415             : }
    3416             : 
    3417             : 
    3418             : const std::vector<int>
    3419       58238 : NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3420             :     std::vector<int> priorities;
    3421       58238 :     MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
    3422             :     const int dist = mainDirections.getStraightest();
    3423       58238 :     if (dist == -1) {
    3424             :         return priorities;
    3425             :     }
    3426             :     // copy the priorities first
    3427       58172 :     priorities.reserve(outgoing->size());
    3428      172285 :     for (const NBEdge* const out : *outgoing) {
    3429      114113 :         int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
    3430             :         assert((prio + 1) * 2 > 0);
    3431      114113 :         prio = (prio + 1) * 2;
    3432      114113 :         priorities.push_back(prio);
    3433             :     }
    3434             :     // when the right turning direction has not a higher priority, divide
    3435             :     //  the importance by 2 due to the possibility to leave the junction
    3436             :     //  faster from this lane
    3437             : #ifdef DEBUG_CONNECTION_GUESSING
    3438             :     if (DEBUGCOND) std::cout << "  prepareEdgePriorities " << getID()
    3439             :                                  << " outgoing=" << toString(*outgoing)
    3440             :                                  << " priorities1=" << toString(priorities)
    3441             :                                  << " dist=" << dist
    3442             :                                  << "\n";
    3443             : #endif
    3444       58172 :     if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
    3445             :         assert(priorities.size() > 0);
    3446       18495 :         priorities[0] /= 2;
    3447             : #ifdef DEBUG_CONNECTION_GUESSING
    3448             :         if (DEBUGCOND) {
    3449             :             std::cout << "   priorities2=" << toString(priorities) << "\n";
    3450             :         }
    3451             : #endif
    3452             :     }
    3453             :     // HEURISTIC:
    3454             :     // when no higher priority exists, let the forward direction be
    3455             :     //  the main direction
    3456       58172 :     if (mainDirections.empty()) {
    3457             :         assert(dist < (int)priorities.size());
    3458       13341 :         priorities[dist] *= 2;
    3459             : #ifdef DEBUG_CONNECTION_GUESSING
    3460             :         if (DEBUGCOND) {
    3461             :             std::cout << "   priorities3=" << toString(priorities) << "\n";
    3462             :         }
    3463             : #endif
    3464             :     }
    3465       58172 :     if (NBNode::isTrafficLight(myTo->getType())) {
    3466        5347 :         priorities[dist] += 1;
    3467             :     } else {
    3468             :         // try to ensure separation of left turns
    3469       52825 :         if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
    3470        1457 :             priorities[0] /= 4;
    3471        1457 :             priorities[(int)priorities.size() - 1] /= 2;
    3472             : #ifdef DEBUG_CONNECTION_GUESSING
    3473             :             if (DEBUGCOND) {
    3474             :                 std::cout << "   priorities6=" << toString(priorities) << "\n";
    3475             :             }
    3476             : #endif
    3477       51368 :         } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
    3478       26502 :                    && outgoing->size() > 2
    3479        5350 :                    && availableLanes.size() == 2
    3480       51549 :                    && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
    3481         172 :             priorities[0] /= 4;
    3482         172 :             priorities.back() /= 2;
    3483             : #ifdef DEBUG_CONNECTION_GUESSING
    3484             :             if (DEBUGCOND) {
    3485             :                 std::cout << "   priorities7=" << toString(priorities) << "\n";
    3486             :             }
    3487             : #endif
    3488             :         }
    3489             :     }
    3490       58172 :     if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
    3491       30784 :         if (myLanes.size() > 2) {
    3492        3536 :             priorities[dist] *= 2;
    3493             : #ifdef DEBUG_CONNECTION_GUESSING
    3494             :             if (DEBUGCOND) {
    3495             :                 std::cout << "   priorities4=" << toString(priorities) << "\n";
    3496             :             }
    3497             : #endif
    3498             :         } else {
    3499       27248 :             priorities[dist] *= 3;
    3500             : #ifdef DEBUG_CONNECTION_GUESSING
    3501             :             if (DEBUGCOND) {
    3502             :                 std::cout << "   priorities5=" << toString(priorities) << "\n";
    3503             :             }
    3504             : #endif
    3505             :         }
    3506             :     }
    3507             :     return priorities;
    3508       58238 : }
    3509             : 
    3510             : 
    3511             : void
    3512       87221 : NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
    3513             :     // do nothing if no turnaround is known
    3514       87221 :     if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
    3515             :         return;
    3516             :     }
    3517             :     // do nothing if the destination node is controlled by a tls and no turnarounds
    3518             :     //  shall be appended for such junctions
    3519       55705 :     if (noTLSControlled && myTo->isTLControlled()) {
    3520             :         return;
    3521             :     }
    3522       55633 :     if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
    3523             :         return;
    3524             :     }
    3525             :     bool isDeadEnd = true;
    3526       57887 :     for (const Connection& c : myConnections) {
    3527       46819 :         if ((c.toEdge->getPermissions(c.toLane)
    3528       46819 :                 & getPermissions(c.fromLane)
    3529       46819 :                 & SVC_PASSENGER) != 0
    3530       46819 :                 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
    3531             :             isDeadEnd = false;
    3532             :             break;
    3533             :         }
    3534             :     }
    3535       55619 :     if (onlyDeadends && !isDeadEnd) {
    3536             :         return;
    3537             :     }
    3538       55243 :     const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3539       55243 :     if (onlyTurnlane) {
    3540          90 :         for (const Connection& c : getConnectionsFromLane(fromLane)) {
    3541          68 :             LinkDirection dir = myTo->getDirection(this, c.toEdge);
    3542          68 :             if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
    3543             :                 return;
    3544             :             }
    3545          82 :         }
    3546             :     }
    3547       55183 :     const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3548       55183 :     if (checkPermissions) {
    3549       55183 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
    3550             :             // exclude connection if fromLane and toEdge have no common permissions
    3551             :             return;
    3552             :         }
    3553       54906 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
    3554             :             // exclude connection if the only commonly permitted class are pedestrians
    3555             :             // these connections are later built in NBNode::buildWalkingAreas
    3556             :             return;
    3557             :         }
    3558             :     }
    3559             :     // avoid railway turn-arounds
    3560       52110 :     if (isRailway(getPermissions() & myTurnDestination->getPermissions())
    3561       52110 :             && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
    3562             :         // except at dead-ends on bidi-edges where they model a reversal in train direction
    3563             :         // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
    3564        3574 :         if (isBidiRail() && isRailDeadEnd()) {
    3565             :             // add a slow connection because direction-reversal implies stopping
    3566        3106 :             setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
    3567        3106 :             return;
    3568             :         } else {
    3569         468 :             return;
    3570             :         }
    3571             :     };
    3572       48536 :     if (noGeometryLike && !isDeadEnd) {
    3573             :         // ignore paths and service entrances if this edge is for passenger traffic
    3574       40542 :         if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
    3575       30571 :                                      && !onlyTurnlane
    3576       61128 :                                      && myTo->geometryLike(
    3577       71106 :                                          NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
    3578      101670 :                                          NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
    3579             :             // make sure the turnDestination has other incoming edges
    3580        7742 :             EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
    3581        7742 :             if (turnIncoming.size() > 1) {
    3582             :                 // this edge is always part of incoming
    3583             :                 return;
    3584             :             }
    3585             :         }
    3586             :     }
    3587       81766 :     setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
    3588             : }
    3589             : 
    3590             : 
    3591             : bool
    3592    16960661 : NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
    3593             :     // maybe it was already set as the turning direction
    3594    16960661 :     if (edge == myTurnDestination) {
    3595             :         return true;
    3596    13338249 :     } else if (myTurnDestination != nullptr) {
    3597             :         // otherwise - it's not if a turning direction exists
    3598             :         return false;
    3599             :     }
    3600     4543605 :     return edge == myPossibleTurnDestination;
    3601             : }
    3602             : 
    3603             : 
    3604             : NBNode*
    3605           0 : NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
    3606             :     // return the from-node when the position is at the begin of the edge
    3607           0 :     if (pos < tolerance) {
    3608           0 :         return myFrom;
    3609             :     }
    3610             :     // return the to-node when the position is at the end of the edge
    3611           0 :     if (pos > myLength - tolerance) {
    3612           0 :         return myTo;
    3613             :     }
    3614             :     return nullptr;
    3615             : }
    3616             : 
    3617             : 
    3618             : void
    3619          22 : NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
    3620             :     int lanes = e->getNumLanes();
    3621          49 :     for (int i = 0; i < lanes; i++) {
    3622          68 :         for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
    3623             :             assert(el.tlID == "");
    3624          82 :             addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
    3625          27 :         }
    3626             :     }
    3627          22 : }
    3628             : 
    3629             : 
    3630             : bool
    3631          93 : NBEdge::lanesWereAssigned() const {
    3632          93 :     return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
    3633             : }
    3634             : 
    3635             : 
    3636             : double
    3637           0 : NBEdge::getMaxLaneOffset() {
    3638           0 :     return SUMO_const_laneWidth * (double)myLanes.size();
    3639             : }
    3640             : 
    3641             : 
    3642             : bool
    3643      245232 : NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
    3644     1238670 :     for (const Connection& c : myConnections) {
    3645      993575 :         if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
    3646             :             return false;
    3647             :         }
    3648             :     }
    3649             :     return true;
    3650             : }
    3651             : 
    3652             : 
    3653             : bool
    3654       41225 : NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
    3655       41225 :     const int fromLane = c.getFromLane();
    3656       41225 :     NBEdge* toEdge = c.getTo();
    3657       41225 :     const int toLane = c.getToLane();
    3658             :     const int tlIndex = c.getTLIndex();
    3659             :     const int tlIndex2 = c.getTLIndex2();
    3660             :     // check whether the connection was not set as not to be controled previously
    3661       41225 :     if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
    3662             :         return false;
    3663             :     }
    3664             : 
    3665             :     assert(fromLane < 0 || fromLane < (int) myLanes.size());
    3666             :     // try to use information about the connections if given
    3667       41225 :     if (fromLane >= 0 && toLane >= 0) {
    3668             :         // find the specified connection
    3669             :         std::vector<Connection>::iterator i =
    3670       41225 :             find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
    3671             :         // ok, we have to test this as on the removal of self-loop edges some connections
    3672             :         //  will be reassigned
    3673       41225 :         if (i != myConnections.end()) {
    3674             :             // get the connection
    3675             :             Connection& connection = *i;
    3676             :             // set the information about the tl
    3677       41222 :             connection.tlID = tlID;
    3678       41222 :             connection.tlLinkIndex = tlIndex;
    3679       41222 :             connection.tlLinkIndex2 = tlIndex2;
    3680             :             return true;
    3681             :         }
    3682             :     }
    3683             :     // if the original connection was not found, set the information for all
    3684             :     //  connections
    3685             :     int no = 0;
    3686             :     bool hadError = false;
    3687          12 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    3688           9 :         if ((*i).toEdge != toEdge) {
    3689           9 :             continue;
    3690             :         }
    3691           0 :         if (fromLane >= 0 && fromLane != (*i).fromLane) {
    3692           0 :             continue;
    3693             :         }
    3694           0 :         if (toLane >= 0 && toLane != (*i).toLane) {
    3695           0 :             continue;
    3696             :         }
    3697           0 :         if ((*i).tlID == "") {
    3698             :             (*i).tlID = tlID;
    3699           0 :             (*i).tlLinkIndex = tlIndex;
    3700           0 :             (*i).tlLinkIndex2 = tlIndex2;
    3701           0 :             no++;
    3702             :         } else {
    3703           0 :             if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
    3704           0 :                 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
    3705             :                 hadError = true;
    3706             :             }
    3707             :         }
    3708             :     }
    3709           3 :     if (hadError && no == 0) {
    3710           0 :         WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
    3711             :     }
    3712             :     return true;
    3713             : }
    3714             : 
    3715             : 
    3716             : void
    3717      128178 : NBEdge::clearControllingTLInformation() {
    3718      377876 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
    3719      249698 :         it->tlID = "";
    3720             :     }
    3721      128178 : }
    3722             : 
    3723             : 
    3724             : PositionVector
    3725      699115 : NBEdge::getCWBoundaryLine(const NBNode& n) const {
    3726      699115 :     PositionVector ret;
    3727             :     int lane;
    3728      699115 :     if (myFrom == (&n)) {
    3729             :         // outgoing
    3730      377223 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3731      377223 :         ret = myLanes[lane].shape;
    3732             :     } else {
    3733             :         // incoming
    3734      321892 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3735      643784 :         ret = myLanes[lane].shape.reverse();
    3736             :     }
    3737      699115 :     ret.move2side(getLaneWidth(lane) / 2.);
    3738      699115 :     return ret;
    3739           0 : }
    3740             : 
    3741             : 
    3742             : PositionVector
    3743      696337 : NBEdge::getCCWBoundaryLine(const NBNode& n) const {
    3744      696337 :     PositionVector ret;
    3745             :     int lane;
    3746      696337 :     if (myFrom == (&n)) {
    3747             :         // outgoing
    3748      321879 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3749      321879 :         ret = myLanes[lane].shape;
    3750             :     } else {
    3751             :         // incoming
    3752      374458 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3753      748916 :         ret = myLanes[lane].shape.reverse();
    3754             :     }
    3755      696337 :     ret.move2side(-getLaneWidth(lane) / 2.);
    3756      696337 :     return ret;
    3757           0 : }
    3758             : 
    3759             : 
    3760             : bool
    3761       14514 : NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
    3762             :     // ok, the number of lanes must match
    3763       14514 :     if (myLanes.size() != possContinuation->myLanes.size()) {
    3764             :         reason = "laneNumber";
    3765         488 :         return false;
    3766             :     }
    3767             :     // do not create self loops
    3768       14026 :     if (myFrom == possContinuation->myTo) {
    3769             :         reason = "loop";
    3770        1729 :         return false;
    3771             :     }
    3772             :     // conserve bidi-rails
    3773       12297 :     if (isBidiRail() != possContinuation->isBidiRail()) {
    3774             :         reason = "bidi-rail";
    3775           8 :         return false;
    3776             :     }
    3777             :     // also, check whether the connections - if any exit do allow to join
    3778             :     //  both edges
    3779             :     // This edge must have a one-to-one connection to the following lanes
    3780       12289 :     switch (myStep) {
    3781             :         case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
    3782             :             break;
    3783             :         case EdgeBuildingStep::INIT:
    3784             :             break;
    3785          19 :         case EdgeBuildingStep::EDGE2EDGES: {
    3786             :             // the following edge must be connected
    3787          19 :             const EdgeVector& conn = getConnectedEdges();
    3788          19 :             if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
    3789             :                 reason = "disconnected";
    3790             :                 return false;
    3791             :             }
    3792             :         }
    3793             :         break;
    3794          20 :         case EdgeBuildingStep::LANES2EDGES:
    3795             :         case EdgeBuildingStep::LANES2LANES_RECHECK:
    3796             :         case EdgeBuildingStep::LANES2LANES_DONE:
    3797             :         case EdgeBuildingStep::LANES2LANES_USER: {
    3798             :             // the possible continuation must be connected
    3799          20 :             if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
    3800             :                 reason = "disconnected";
    3801           0 :                 return false;
    3802             :             }
    3803             :             // all lanes must go to the possible continuation
    3804          20 :             std::vector<int> conns = getConnectionLanes(possContinuation);
    3805          20 :             const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
    3806          20 :             if (conns.size() < myLanes.size() - offset) {
    3807             :                 reason = "some lanes disconnected";
    3808             :                 return false;
    3809             :             }
    3810             :         }
    3811             :         break;
    3812             :         default:
    3813             :             break;
    3814             :     }
    3815       12289 :     const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
    3816       12290 :     if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
    3817             :         return true;
    3818             :     }
    3819       12288 :     const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
    3820       12288 :     if (maxJunctionSize >= 0) {
    3821           4 :         const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
    3822           4 :         if (junctionSize > maxJunctionSize + POSITION_EPS) {
    3823           4 :             reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
    3824           2 :             return false;
    3825             :         }
    3826             :     }
    3827             :     // the priority, too (?)
    3828       12286 :     if (getPriority() != possContinuation->getPriority()) {
    3829             :         reason = "priority";
    3830         118 :         return false;
    3831             :     }
    3832             :     // the speed allowed
    3833       12168 :     if (mySpeed != possContinuation->mySpeed) {
    3834             :         reason = "speed";
    3835        2070 :         return false;
    3836             :     }
    3837             :     // spreadtype should match or it will look ugly
    3838       10098 :     if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
    3839             :         reason = "spreadType";
    3840         113 :         return false;
    3841             :     }
    3842             :     // matching lanes must have identical properties
    3843       23054 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    3844       13193 :         if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
    3845           0 :             reason = "lane " + toString(i) + " speed";
    3846         124 :             return false;
    3847       13193 :         } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
    3848         176 :             reason = "lane " + toString(i) + " permissions";
    3849          88 :             return false;
    3850       13105 :         } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
    3851          14 :             reason = "lane " + toString(i) + " change restrictions";
    3852           7 :             return false;
    3853       13186 :         } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
    3854       13274 :                    fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
    3855          58 :             reason = "lane " + toString(i) + " width";
    3856          29 :             return false;
    3857             :         }
    3858             :     }
    3859             :     // if given identically osm names
    3860       28198 :     if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
    3861       10259 :             && ((myStreetName != "" && possContinuation->getStreetName() != "")
    3862             :                 // only permit merging a short unnamed road with a longer named road
    3863          34 :                 || (myStreetName != "" && myLength <= possContinuation->getLength())
    3864          28 :                 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
    3865         185 :         return false;
    3866             :     }
    3867             : 
    3868             :     return true;
    3869             : }
    3870             : 
    3871             : 
    3872             : void
    3873        9354 : NBEdge::append(NBEdge* e) {
    3874             :     // append geometry
    3875        9354 :     myGeom.append(e->myGeom);
    3876       21726 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    3877       12372 :         myLanes[i].customShape.append(e->myLanes[i].customShape);
    3878       14327 :         if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
    3879       16286 :                 || OptionsCont::getOptions().getBool("output.original-names")) {
    3880       31263 :             const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
    3881       31263 :             const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
    3882       10421 :             if (origID != origID2) {
    3883       12866 :                 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
    3884             :             }
    3885             :         }
    3886       12372 :         myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
    3887       12372 :         myLanes[i].turnSigns = e->myLanes[i].turnSigns;
    3888             :     }
    3889        9354 :     if (e->getLength() > myLength) {
    3890             :         // possibly some lane attributes differ (when using option geometry.remove.min-length)
    3891             :         // make sure to use the attributes from the longer edge
    3892       10144 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    3893        5619 :             myLanes[i].width = e->myLanes[i].width;
    3894             :         }
    3895             :         // defined name prevails over undefined name of shorter road
    3896        4525 :         if (myStreetName == "") {
    3897        2248 :             myStreetName = e->myStreetName;
    3898             :         }
    3899             :     }
    3900             :     // recompute length
    3901        9354 :     myLength += e->myLength;
    3902        9354 :     if (myLoadedLength > 0 || e->myLoadedLength > 0) {
    3903           1 :         myLoadedLength = getFinalLength() + e->getFinalLength();
    3904             :     }
    3905             :     // copy the connections and the building step if given
    3906        9354 :     myStep = e->myStep;
    3907        9354 :     myConnections = e->myConnections;
    3908        9354 :     myTurnDestination = e->myTurnDestination;
    3909        9354 :     myPossibleTurnDestination = e->myPossibleTurnDestination;
    3910        9354 :     myConnectionsToDelete = e->myConnectionsToDelete;
    3911             :     // set the node
    3912        9354 :     myTo = e->myTo;
    3913        9354 :     myTurnSignTarget = e->myTurnSignTarget;
    3914             :     myToBorder = e->myToBorder;
    3915       18708 :     mergeParameters(e->getParametersMap());
    3916             :     if (e->mySignalPosition != Position::INVALID) {
    3917        1931 :         mySignalPosition = e->mySignalPosition;
    3918             :     }
    3919        9354 :     computeAngle(); // myEndAngle may be different now
    3920        9354 : }
    3921             : 
    3922             : 
    3923             : bool
    3924     1095019 : NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
    3925     4538755 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    3926     3808564 :         if ((*i).toEdge == e && (*i).tlID != "") {
    3927             :             return true;
    3928             :         }
    3929             :     }
    3930             :     return false;
    3931             : }
    3932             : 
    3933             : 
    3934             : NBEdge*
    3935     1014383 : NBEdge::getTurnDestination(bool possibleDestination) const {
    3936     1014383 :     if (myTurnDestination == nullptr && possibleDestination) {
    3937       53707 :         return myPossibleTurnDestination;
    3938             :     }
    3939             :     return myTurnDestination;
    3940             : }
    3941             : 
    3942             : 
    3943             : std::string
    3944      348042 : NBEdge::getLaneID(int lane) const {
    3945      696084 :     return myID + "_" + toString(lane);
    3946             : }
    3947             : 
    3948             : 
    3949             : bool
    3950          65 : NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
    3951          65 :     std::vector<double> distances = myGeom.distances(e->getGeometry());
    3952             :     assert(distances.size() > 0);
    3953          65 :     return VectorHelper<double>::maxValue(distances) < threshold;
    3954             : }
    3955             : 
    3956             : 
    3957             : void
    3958          57 : NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
    3959             :     assert(index <= (int)myLanes.size());
    3960         114 :     myLanes.insert(myLanes.begin() + index, Lane(this, ""));
    3961             :     // copy attributes
    3962          57 :     if (myLanes.size() > 1) {
    3963          57 :         int templateIndex = index > 0 ? index - 1 : index + 1;
    3964          57 :         myLanes[index].speed = myLanes[templateIndex].speed;
    3965          57 :         myLanes[index].friction = myLanes[templateIndex].friction;
    3966          57 :         myLanes[index].permissions = myLanes[templateIndex].permissions;
    3967          57 :         myLanes[index].preferred = myLanes[templateIndex].preferred;
    3968          57 :         myLanes[index].endOffset = myLanes[templateIndex].endOffset;
    3969          57 :         myLanes[index].width = myLanes[templateIndex].width;
    3970          57 :         myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
    3971             :     }
    3972          57 :     const EdgeVector& incs = myFrom->getIncomingEdges();
    3973          57 :     if (recomputeShape) {
    3974          43 :         computeLaneShapes();
    3975             :     }
    3976          57 :     if (recomputeConnections) {
    3977         111 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    3978          68 :             (*i)->invalidateConnections(true);
    3979             :         }
    3980          43 :         invalidateConnections(true);
    3981          14 :     } else if (shiftIndices) {
    3982             :         // shift outgoing connections above the added lane to the left
    3983           0 :         for (Connection& c : myConnections) {
    3984           0 :             if (c.fromLane >= index) {
    3985           0 :                 c.fromLane += 1;
    3986             :             }
    3987             :         }
    3988             :         // shift incoming connections above the added lane to the left
    3989           0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    3990           0 :             for (Connection& c : inc->myConnections) {
    3991           0 :                 if (c.toEdge == this && c.toLane >= index) {
    3992           0 :                     c.toLane += 1;
    3993             :                 }
    3994             :             }
    3995             :         }
    3996           0 :         myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
    3997           0 :         myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
    3998             :     }
    3999          57 : }
    4000             : 
    4001             : void
    4002          44 : NBEdge::incLaneNo(int by) {
    4003          44 :     int newLaneNo = (int)myLanes.size() + by;
    4004         101 :     while ((int)myLanes.size() < newLaneNo) {
    4005             :         // recompute shapes on last addition
    4006          57 :         const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4007          57 :         addLane((int)myLanes.size(), recompute, recompute, false);
    4008             :     }
    4009          44 : }
    4010             : 
    4011             : 
    4012             : void
    4013          64 : NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
    4014             :     assert(index < (int)myLanes.size());
    4015          64 :     myLanes.erase(myLanes.begin() + index);
    4016          64 :     if (recompute) {
    4017          16 :         computeLaneShapes();
    4018          16 :         const EdgeVector& incs = myFrom->getIncomingEdges();
    4019          17 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    4020           1 :             (*i)->invalidateConnections(true);
    4021             :         }
    4022          16 :         invalidateConnections(true);
    4023          48 :     } else if (shiftIndices) {
    4024          40 :         removeFromConnections(nullptr, index, -1, false, true);
    4025         116 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4026          76 :             inc->removeFromConnections(this, -1, index, false, true);
    4027             :         }
    4028             :     }
    4029          64 : }
    4030             : 
    4031             : 
    4032             : void
    4033          61 : NBEdge::decLaneNo(int by) {
    4034          61 :     int newLaneNo = (int) myLanes.size() - by;
    4035             :     assert(newLaneNo > 0);
    4036          85 :     while ((int)myLanes.size() > newLaneNo) {
    4037             :         // recompute shapes on last removal
    4038          24 :         const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4039          24 :         deleteLane((int)myLanes.size() - 1, recompute, false);
    4040             :     }
    4041          61 : }
    4042             : 
    4043             : 
    4044             : void
    4045        7123 : NBEdge::markAsInLane2LaneState() {
    4046             :     assert(myTo->getOutgoingEdges().size() == 0);
    4047        7123 :     myStep = EdgeBuildingStep::LANES2LANES_DONE;
    4048        7123 : }
    4049             : 
    4050             : 
    4051             : void
    4052        8039 : NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4053        8039 :     if (lane < 0) { // all lanes are meant...
    4054        7968 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4055        5585 :             allowVehicleClass(i, vclass);
    4056             :         }
    4057             :     } else {
    4058             :         assert(lane < (int)myLanes.size());
    4059        5656 :         myLanes[lane].permissions |= vclass;
    4060             :     }
    4061        8039 : }
    4062             : 
    4063             : 
    4064             : void
    4065       35448 : NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4066       35448 :     if (lane < 0) { // all lanes are meant...
    4067       35448 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4068       21809 :             disallowVehicleClass((int) i, vclass);
    4069             :         }
    4070             :     } else {
    4071             :         assert(lane < (int)myLanes.size());
    4072       21809 :         myLanes[lane].permissions &= ~vclass;
    4073             :     }
    4074       35448 : }
    4075             : 
    4076             : 
    4077             : void
    4078          60 : NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
    4079          60 :     if (lane < 0) { // all lanes are meant...
    4080           0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4081           0 :             preferVehicleClass(i, vclasses);
    4082             :         }
    4083             :     } else {
    4084             :         assert(lane < (int)myLanes.size());
    4085          60 :         myLanes[lane].permissions |= vclasses;
    4086          60 :         myLanes[lane].preferred |= vclasses;
    4087             :     }
    4088          60 : }
    4089             : 
    4090             : 
    4091             : void
    4092       58161 : NBEdge::setLaneWidth(int lane, double width) {
    4093       58161 :     if (lane < 0) {
    4094             :         // all lanes are meant...
    4095         401 :         myLaneWidth = width;
    4096         823 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4097             :             // ... do it for each lane
    4098         422 :             setLaneWidth(i, width);
    4099             :         }
    4100             :         return;
    4101             :     }
    4102             :     assert(lane < (int)myLanes.size());
    4103       57760 :     myLanes[lane].width = width;
    4104             : }
    4105             : 
    4106             : void
    4107        3839 : NBEdge::setLaneType(int lane, const std::string& type) {
    4108        3839 :     if (lane < 0) {
    4109           0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4110             :             // ... do it for each lane
    4111           0 :             setLaneType(i, type);
    4112             :         }
    4113             :         return;
    4114             :     }
    4115             :     assert(lane < (int)myLanes.size());
    4116        3839 :     myLanes[lane].type = type;
    4117             : }
    4118             : 
    4119             : 
    4120             : double
    4121     4974189 : NBEdge::getLaneWidth(int lane) const {
    4122     4974189 :     return myLanes[lane].width != UNSPECIFIED_WIDTH
    4123     4974189 :            ? myLanes[lane].width
    4124     3958283 :            : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
    4125             : }
    4126             : 
    4127             : double
    4128      135479 : NBEdge::getInternalLaneWidth(
    4129             :     const NBNode& node,
    4130             :     const NBEdge::Connection& connection,
    4131             :     const NBEdge::Lane& successor,
    4132             :     bool isVia) const {
    4133             : 
    4134      135479 :     if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
    4135           3 :         return getLaneWidth(connection.fromLane);
    4136             :     }
    4137             : 
    4138      135476 :     return (isBikepath(getPermissions(connection.fromLane)) && (
    4139      135476 :                 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
    4140       15058 :            myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
    4141             : }
    4142             : 
    4143             : double
    4144     1201781 : NBEdge::getTotalWidth() const {
    4145             :     double result = 0;
    4146     2756734 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4147     1554953 :         result += getLaneWidth(i);
    4148             :     }
    4149     1201781 :     return result;
    4150             : }
    4151             : 
    4152             : double
    4153       38641 : NBEdge::getEndOffset(int lane) const {
    4154       38641 :     return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
    4155             : }
    4156             : 
    4157             : 
    4158             : const StopOffset&
    4159      647729 : NBEdge::getEdgeStopOffset() const {
    4160      647729 :     return myEdgeStopOffset;
    4161             : }
    4162             : 
    4163             : 
    4164             : const StopOffset&
    4165          24 : NBEdge::getLaneStopOffset(int lane) const {
    4166          24 :     if (lane == -1) {
    4167          12 :         return myEdgeStopOffset;
    4168             :     } else {
    4169          12 :         return myLanes[lane].laneStopOffset;
    4170             :     }
    4171             : }
    4172             : 
    4173             : 
    4174             : void
    4175       51886 : NBEdge::setEndOffset(int lane, double offset) {
    4176       51886 :     if (lane < 0) {
    4177             :         // all lanes are meant...
    4178           1 :         myEndOffset = offset;
    4179           4 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4180             :             // ... do it for each lane
    4181           3 :             setEndOffset(i, offset);
    4182             :         }
    4183             :         return;
    4184             :     }
    4185             :     assert(lane < (int)myLanes.size());
    4186       51885 :     myLanes[lane].endOffset = offset;
    4187             : }
    4188             : 
    4189             : 
    4190             : bool
    4191       90226 : NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
    4192       90226 :     if (lane < 0) {
    4193       38356 :         if (!overwrite && myEdgeStopOffset.isDefined()) {
    4194             :             return false;
    4195             :         }
    4196             :         // all lanes are meant...
    4197       38352 :         if (offset.getOffset() < 0) {
    4198             :             // Edge length unknown at parsing time, thus check here.
    4199           3 :             WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
    4200           1 :             return false;
    4201             :         } else {
    4202       38351 :             myEdgeStopOffset = offset;
    4203             :         }
    4204       51870 :     } else if (lane < (int)myLanes.size()) {
    4205       51870 :         if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
    4206       51864 :             if (offset.getOffset() < 0) {
    4207             :                 // Edge length unknown at parsing time, thus check here.
    4208           0 :                 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
    4209             :             } else {
    4210       51864 :                 myLanes[lane].laneStopOffset = offset;
    4211             :             }
    4212             :         }
    4213             :     } else {
    4214           0 :         WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
    4215             :     }
    4216             :     return true;
    4217             : }
    4218             : 
    4219             : 
    4220             : void
    4221       56566 : NBEdge::setSpeed(int lane, double speed) {
    4222       56566 :     if (lane < 0) {
    4223             :         // all lanes are meant...
    4224         374 :         mySpeed = speed;
    4225        1160 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4226             :             // ... do it for each lane
    4227         786 :             setSpeed(i, speed);
    4228             :         }
    4229             :         return;
    4230             :     }
    4231             :     assert(lane < (int)myLanes.size());
    4232       56192 :     myLanes[lane].speed = speed;
    4233             : }
    4234             : 
    4235             : 
    4236             : void
    4237       56265 : NBEdge::setFriction(int lane, double friction) {
    4238       56265 :     if (lane < 0) {
    4239             :         // all lanes are meant...
    4240         630 :         myFriction = friction;
    4241        1935 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4242             :             // ... do it for each lane
    4243        1305 :             setFriction(i, friction);
    4244             :         }
    4245             :         return;
    4246             :     }
    4247             :     assert(lane < (int)myLanes.size());
    4248       55635 :     myLanes[lane].friction = friction;
    4249             : }
    4250             : 
    4251             : 
    4252             : void
    4253       50606 : NBEdge::setAcceleration(int lane, bool accelRamp) {
    4254             :     assert(lane >= 0);
    4255             :     assert(lane < (int)myLanes.size());
    4256       50606 :     myLanes[lane].accelRamp = accelRamp;
    4257       50606 : }
    4258             : 
    4259             : 
    4260             : void
    4261          73 : NBEdge::setLaneShape(int lane, const PositionVector& shape) {
    4262             :     assert(lane >= 0);
    4263             :     assert(lane < (int)myLanes.size());
    4264          73 :     myLanes[lane].customShape = shape;
    4265          73 : }
    4266             : 
    4267             : 
    4268             : void
    4269      303537 : NBEdge::setPermissions(SVCPermissions permissions, int lane) {
    4270      303537 :     if (lane < 0) {
    4271      241514 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4272             :             // ... do it for each lane
    4273      132522 :             setPermissions(permissions, i);
    4274             :         }
    4275             :     } else {
    4276             :         assert(lane < (int)myLanes.size());
    4277      194545 :         myLanes[lane].permissions = permissions;
    4278             :     }
    4279      303537 : }
    4280             : 
    4281             : 
    4282             : void
    4283           0 : NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
    4284           0 :     if (lane < 0) {
    4285           0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4286             :             // ... do it for each lane
    4287           0 :             setPreferredVehicleClass(permissions, i);
    4288             :         }
    4289             :     } else {
    4290             :         assert(lane < (int)myLanes.size());
    4291           0 :         myLanes[lane].preferred = permissions;
    4292             :     }
    4293           0 : }
    4294             : 
    4295             : 
    4296             : void
    4297       50838 : NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
    4298             :     assert(lane >= 0);
    4299             :     assert(lane < (int)myLanes.size());
    4300       50838 :     myLanes[lane].changeLeft = changeLeft;
    4301       50838 :     myLanes[lane].changeRight = changeRight;
    4302       50838 : }
    4303             : 
    4304             : 
    4305             : SVCPermissions
    4306    98128504 : NBEdge::getPermissions(int lane) const {
    4307    98128504 :     if (lane < 0) {
    4308             :         SVCPermissions result = 0;
    4309    88087067 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4310    52537237 :             result |= getPermissions(i);
    4311             :         }
    4312    35549830 :         return result;
    4313             :     } else {
    4314             :         assert(lane < (int)myLanes.size());
    4315    62578674 :         return myLanes[lane].permissions;
    4316             :     }
    4317             : }
    4318             : 
    4319             : 
    4320             : void
    4321       61608 : NBEdge::setLoadedLength(double val) {
    4322       61608 :     myLoadedLength = val;
    4323       61608 : }
    4324             : 
    4325             : void
    4326           4 : NBEdge::setAverageLengthWithOpposite(double val) {
    4327           4 :     myLength = val;
    4328           4 : }
    4329             : 
    4330             : 
    4331             : void
    4332         200 : NBEdge::dismissVehicleClassInformation() {
    4333         647 :     for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    4334         447 :         (*i).permissions = SVCAll;
    4335         447 :         (*i).preferred = 0;
    4336             :     }
    4337         200 : }
    4338             : 
    4339             : 
    4340             : bool
    4341      438029 : NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
    4342      438029 :     if (c1.fromLane != c2.fromLane) {
    4343      105602 :         return c1.fromLane < c2.fromLane;
    4344             :     }
    4345      332427 :     if (c1.toEdge != c2.toEdge) {
    4346             :         return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
    4347             :     }
    4348        6686 :     return c1.toLane < c2.toLane;
    4349             : }
    4350             : 
    4351             : 
    4352             : double
    4353       17506 : NBEdge::getSignalOffset() const {
    4354             :     if (mySignalPosition == Position::INVALID) {
    4355             :         return UNSPECIFIED_SIGNAL_OFFSET;
    4356             :     } else {
    4357         972 :         Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
    4358         972 :                            myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
    4359             :         //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
    4360             :         return mySignalPosition.distanceTo2D(laneEnd);
    4361             :     }
    4362             : }
    4363             : 
    4364             : 
    4365             : int
    4366        4988 : NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
    4367             :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4368        4988 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4369        4988 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4370        8893 :     for (int i = start; i != end; i += direction) {
    4371             :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4372             :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4373          81 :         if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
    4374        8867 :                 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
    4375        4920 :             return i;
    4376             :         }
    4377             :     }
    4378             :     return -1;
    4379             : }
    4380             : 
    4381             : int
    4382       66403 : NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
    4383             :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4384       66403 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4385       66403 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4386       91786 :     for (int i = start; i != end; i += direction) {
    4387             :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4388             :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4389       68585 :         SVCPermissions p = myLanes[i].permissions;
    4390       68585 :         if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
    4391       25383 :                 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
    4392       43202 :             return i;
    4393             :         }
    4394             :     }
    4395             :     return -1;
    4396             : }
    4397             : 
    4398             : int
    4399      115914 : NBEdge::getSpecialLane(SVCPermissions permissions) const {
    4400      261733 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4401      147065 :         if (myLanes[i].permissions == permissions) {
    4402        1246 :             return i;
    4403             :         }
    4404             :     }
    4405             :     return -1;
    4406             : }
    4407             : 
    4408             : int
    4409     1505878 : NBEdge::getFirstAllowedLaneIndex(int direction) const {
    4410             :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4411     1505878 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4412     1505878 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4413     1506392 :     for (int i = start; i != end; i += direction) {
    4414     1506107 :         if (myLanes[i].permissions != 0) {
    4415     1505593 :             return i;
    4416             :         }
    4417             :     }
    4418         285 :     return end - direction;
    4419             : }
    4420             : 
    4421             : 
    4422             : std::set<SVCPermissions>
    4423        5681 : NBEdge::getPermissionVariants(int iStart, int iEnd) const {
    4424             :     std::set<SVCPermissions> result;
    4425        5681 :     if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
    4426           0 :         throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
    4427             :     }
    4428       12566 :     for (int i = iStart; i < iEnd; ++i) {
    4429        6885 :         result.insert(getPermissions(i));
    4430             :     }
    4431        5681 :     return result;
    4432             : }
    4433             : 
    4434             : int
    4435     7526570 : NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
    4436             :     int result = 0;
    4437    19266403 :     for (const Lane& lane : myLanes) {
    4438    11739833 :         if ((allPermissions && (lane.permissions & permissions) == permissions)
    4439     1471042 :                 || (!allPermissions && (lane.permissions & permissions) != 0)) {
    4440    10268857 :             result++;
    4441             :         }
    4442             :     }
    4443     7526570 :     return result;
    4444             : }
    4445             : 
    4446             : bool
    4447           0 : NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
    4448             :     assert(lane >= 0 && lane < getNumLanes());
    4449           0 :     return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
    4450             : }
    4451             : 
    4452             : bool
    4453           0 : NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
    4454             :     assert(lane >= 0 && lane < getNumLanes());
    4455           0 :     return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
    4456             : }
    4457             : 
    4458             : double
    4459        9744 : NBEdge::getCrossingAngle(NBNode* node) {
    4460        9744 :     double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
    4461        9744 :     if (angle < 0) {
    4462        2517 :         angle += 360.0;
    4463             :     }
    4464        9744 :     if (angle >= 360) {
    4465           0 :         angle -= 360.0;
    4466             :     }
    4467        9744 :     if (gDebugFlag1) {
    4468           0 :         std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
    4469             :     }
    4470        9744 :     return angle;
    4471             : }
    4472             : 
    4473             : 
    4474             : NBEdge::Lane
    4475           0 : NBEdge::getFirstNonPedestrianLane(int direction) const {
    4476           0 :     int index = getFirstNonPedestrianLaneIndex(direction);
    4477           0 :     if (index < 0) {
    4478           0 :         throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
    4479             :     }
    4480           0 :     return myLanes[index];
    4481             : }
    4482             : 
    4483             : std::string
    4484       17599 : NBEdge::getSidewalkID() {
    4485             :     // see IntermodalEdge::getSidewalk()
    4486       21664 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4487       17650 :         if (myLanes[i].permissions == SVC_PEDESTRIAN) {
    4488       13585 :             return getLaneID(i);
    4489             :         }
    4490             :     }
    4491        4014 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4492        4014 :         if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
    4493        4014 :             return getLaneID(i);
    4494             :         }
    4495             :     }
    4496           0 :     return getLaneID(0);
    4497             : }
    4498             : 
    4499             : void
    4500        4567 : NBEdge::addSidewalk(double width) {
    4501        4567 :     addRestrictedLane(width, SVC_PEDESTRIAN);
    4502        4567 : }
    4503             : 
    4504             : 
    4505             : void
    4506           0 : NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4507           0 :     restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
    4508           0 : }
    4509             : 
    4510             : 
    4511             : void
    4512         875 : NBEdge::addBikeLane(double width) {
    4513         875 :     addRestrictedLane(width, SVC_BICYCLE);
    4514         875 : }
    4515             : 
    4516             : 
    4517             : void
    4518           0 : NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4519           0 :     restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
    4520           0 : }
    4521             : 
    4522             : bool
    4523        6081 : NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
    4524       15767 :     for (const Lane& lane : myLanes) {
    4525        9808 :         if (lane.permissions == vclass) {
    4526             :             return true;
    4527             :         }
    4528             :     }
    4529             :     return false;
    4530             : }
    4531             : 
    4532             : 
    4533             : void
    4534        5674 : NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
    4535        5674 :     if (hasRestrictedLane(vclass)) {
    4536          12 :         WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
    4537           4 :         return;
    4538             :     }
    4539        5670 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    4540        1705 :         myGeom.move2side(width / 2);
    4541             :     }
    4542             :     // disallow the designated vclass on all "old" lanes
    4543        5670 :     disallowVehicleClass(-1, vclass);
    4544             :     // don't create a restricted vehicle lane to the right of a sidewalk
    4545        5670 :     const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
    4546             :     if (newIndex == 0) {
    4547             :         // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
    4548        5619 :         disallowVehicleClass(-1, SVC_PEDESTRIAN);
    4549             :     }
    4550             :     // add new lane
    4551       11340 :     myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
    4552        5670 :     myLanes[newIndex].permissions = vclass;
    4553        5670 :     myLanes[newIndex].width = fabs(width);
    4554             :     // shift outgoing connections to the left
    4555        5696 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4556             :         Connection& c = *it;
    4557          26 :         if (c.fromLane >= newIndex) {
    4558          26 :             c.fromLane += 1;
    4559             :         }
    4560             :     }
    4561             :     // shift incoming connections to the left
    4562        5670 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4563       12664 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4564        6994 :         (*it)->shiftToLanesToEdge(this, 1);
    4565             :     }
    4566        5670 :     myFrom->shiftTLConnectionLaneIndex(this, 1);
    4567        5670 :     myTo->shiftTLConnectionLaneIndex(this, 1);
    4568        5670 :     computeLaneShapes();
    4569             : }
    4570             : 
    4571             : 
    4572             : void
    4573           0 : NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4574             :     // check that previously lane was transformed
    4575           0 :     if (myLanes[0].permissions != vclass) {
    4576           0 :         WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
    4577           0 :         return;
    4578             :     }
    4579             :     // restore old values
    4580             :     myGeom = oldGeometry;
    4581           0 :     myLanes = oldLanes;
    4582           0 :     myConnections = oldConnections;
    4583             :     // shift incoming connections to the right
    4584           0 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4585           0 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4586           0 :         (*it)->shiftToLanesToEdge(this, 0);
    4587             :     }
    4588             :     // Shift TL conections
    4589           0 :     myFrom->shiftTLConnectionLaneIndex(this, 0);
    4590           0 :     myTo->shiftTLConnectionLaneIndex(this, 0);
    4591           0 :     computeLaneShapes();
    4592             : }
    4593             : 
    4594             : 
    4595             : void
    4596        6994 : NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
    4597             :     /// XXX could we repurpose the function replaceInConnections ?
    4598        7102 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4599         108 :         if ((*it).toEdge == to && (*it).toLane >= 0) {
    4600          26 :             (*it).toLane += laneOff;
    4601             :         }
    4602             :     }
    4603        6994 : }
    4604             : 
    4605             : 
    4606             : void
    4607      103124 : NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
    4608      103124 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER && !isRailway(getPermissions()) && getBidiEdge() == nullptr) {
    4609       20564 :         const int i = (node == myTo ? -1 : 0);
    4610       20564 :         const int i2 = (node == myTo ? 0 : -1);
    4611       20564 :         const double dist = myGeom[i].distanceTo2D(node->getPosition());
    4612       20564 :         const double neededOffset = getTotalWidth() / 2;
    4613       20564 :         const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
    4614       20564 :                                   other->getGeometry().distance2D(myGeom[i]));
    4615       20564 :         const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
    4616       20564 :         if (dist < neededOffset && dist2 < neededOffset2) {
    4617             :             PositionVector tmp = myGeom;
    4618             :             // @note this doesn't work well for vissim networks
    4619             :             //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
    4620             :             try {
    4621       17607 :                 tmp.move2side(neededOffset - dist);
    4622       17607 :                 myGeom[i] = tmp[i];
    4623           0 :             } catch (InvalidArgument&) {
    4624           0 :                 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
    4625           0 :             }
    4626       17607 :         }
    4627             :     }
    4628      103124 : }
    4629             : 
    4630             : 
    4631             : Position
    4632          90 : NBEdge::geometryPositionAtOffset(double offset) const {
    4633          90 :     if (myLoadedLength > 0) {
    4634           2 :         return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
    4635             :     } else {
    4636          88 :         return myGeom.positionAtOffset(offset);
    4637             :     }
    4638             : }
    4639             : 
    4640             : 
    4641             : double
    4642      144775 : NBEdge::getFinalLength() const {
    4643             :     double result = getLoadedLength();
    4644      289550 :     if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
    4645             :         // use length to junction center even if a modified geometry was given
    4646       63327 :         PositionVector geom = cutAtIntersection(myGeom);
    4647       63327 :         geom.push_back_noDoublePos(getToNode()->getCenter());
    4648       63327 :         geom.push_front_noDoublePos(getFromNode()->getCenter());
    4649       63327 :         result = geom.length();
    4650       63327 :     }
    4651             :     double avgEndOffset = 0;
    4652      327930 :     for (const Lane& lane : myLanes) {
    4653      183155 :         avgEndOffset += lane.endOffset;
    4654             :     }
    4655      144775 :     if (isBidiRail()) {
    4656        9684 :         avgEndOffset += myPossibleTurnDestination->getEndOffset();
    4657             :     }
    4658      144775 :     avgEndOffset /= (double)myLanes.size();
    4659      144775 :     return MAX2(result - avgEndOffset, POSITION_EPS);
    4660             : }
    4661             : 
    4662             : 
    4663             : void
    4664       24375 : NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
    4665       24375 :     if (laneIdx == -1) {
    4666       24318 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4667       27306 :             setOrigID(origID, append, i);
    4668             :         }
    4669             :     } else {
    4670       13710 :         if (origID != "") {
    4671       13710 :             if (append) {
    4672         114 :                 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
    4673          57 :                 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
    4674          57 :                     oldIDs.push_back(origID);
    4675             :                 }
    4676          57 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
    4677          57 :             } else {
    4678       13653 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
    4679             :             }
    4680             :         } else {
    4681             :             // do not record empty origID parameter
    4682           0 :             myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
    4683             :         }
    4684             :     }
    4685       24375 : }
    4686             : 
    4687             : 
    4688             : const EdgeVector&
    4689         546 : NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
    4690             :     // @todo cache successors instead of recomputing them every time
    4691         546 :     mySuccessors.clear();
    4692             :     //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
    4693        2924 :     for (const Connection& con : myConnections) {
    4694        2340 :         if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
    4695         151 :                 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
    4696         151 :                                             & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
    4697        4718 :                 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
    4698        1590 :             mySuccessors.push_back(con.toEdge);
    4699             :             //std::cout << "   succ=" << con.toEdge->getID() << "\n";
    4700             :         }
    4701             :     }
    4702         546 :     return mySuccessors;
    4703             : }
    4704             : 
    4705             : 
    4706             : const ConstRouterEdgePairVector&
    4707       58288 : NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
    4708             :     // @todo cache successors instead of recomputing them every time
    4709       58288 :     myViaSuccessors.clear();
    4710      121012 :     for (const Connection& con : myConnections) {
    4711             :         std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
    4712             :         // special case for Persons in Netedit
    4713       62724 :         if (vClass == SVC_PEDESTRIAN) {
    4714           0 :             myViaSuccessors.push_back(pair);    // Pedestrians have complete freedom of movement in all sucessors
    4715       62724 :         } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
    4716       62724 :                    (con.toEdge != nullptr) &&
    4717       62724 :                    ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
    4718             :             // ignore duplicates
    4719       61841 :             if (con.getLength() > 0) {
    4720             :                 pair.second = &con;
    4721             :             }
    4722       61841 :             myViaSuccessors.push_back(pair);
    4723             :         }
    4724             :     }
    4725       58288 :     return myViaSuccessors;
    4726             : }
    4727             : 
    4728             : 
    4729             : void
    4730           0 : NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
    4731           0 :     if (outgoing) {
    4732           0 :         for (const Connection& c : myConnections) {
    4733           0 :             std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4734             :         }
    4735             :     }
    4736           0 :     if (incoming) {
    4737           0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4738           0 :             for (Connection& c : inc->myConnections) {
    4739           0 :                 if (c.toEdge == this) {
    4740           0 :                     std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4741             :                 }
    4742             :             }
    4743             :         }
    4744             :     }
    4745           0 : }
    4746             : 
    4747             : 
    4748             : int
    4749          95 : NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
    4750         190 :     return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
    4751             : }
    4752             : 
    4753             : bool
    4754          36 : NBEdge::joinLanes(SVCPermissions perms) {
    4755             :     bool haveJoined = false;
    4756             :     int i = 0;
    4757         181 :     while (i < getNumLanes() - 1) {
    4758         145 :         if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
    4759          37 :             const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
    4760          37 :             const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
    4761          37 :             deleteLane(i, false, true);
    4762          37 :             setLaneWidth(i, newWidth);
    4763          37 :             setLaneType(i, newType);
    4764             :             haveJoined = true;
    4765             :         } else {
    4766         108 :             i++;
    4767             :         }
    4768             :     }
    4769          36 :     return haveJoined;
    4770             : }
    4771             : 
    4772             : 
    4773             : EdgeVector
    4774       81501 : NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
    4775             :     EdgeVector result;
    4776      348526 :     for (NBEdge* edge : edges) {
    4777      267025 :         if ((edge->getPermissions() & permissions) != 0) {
    4778      243439 :             result.push_back(edge);
    4779             :         }
    4780             :     }
    4781       81501 :     return result;
    4782             : }
    4783             : 
    4784             : NBEdge*
    4785        4313 : NBEdge::getStraightContinuation(SVCPermissions permissions) const {
    4786        4313 :     EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
    4787        4313 :     if (cands.size() == 0) {
    4788             :         return nullptr;
    4789             :     }
    4790        4309 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
    4791        4309 :     NBEdge* best = cands.front();
    4792        4309 :     if (isTurningDirectionAt(best)) {
    4793             :         return nullptr;
    4794             :     } else {
    4795        4269 :         return best;
    4796             :     }
    4797             : }
    4798             : 
    4799             : NBEdge*
    4800         124 : NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
    4801         124 :     EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
    4802         124 :     if (cands.size() == 0) {
    4803             :         return nullptr;
    4804             :     }
    4805         112 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
    4806         112 :     NBEdge* best = cands.front();
    4807         112 :     if (best->isTurningDirectionAt(this)) {
    4808             :         return nullptr;
    4809             :     } else {
    4810         105 :         return best;
    4811             :     }
    4812             : }
    4813             : 
    4814             : 
    4815             : NBEdge*
    4816          41 : NBEdge::guessOpposite(bool reguess) {
    4817             :     NBEdge* opposite = nullptr;
    4818          41 :     if (getNumLanes() > 0) {
    4819             :         NBEdge::Lane& lastLane = myLanes.back();
    4820          41 :         const double lastWidth = getLaneWidth(getNumLanes() - 1);
    4821          41 :         if (lastLane.oppositeID == "" || reguess) {
    4822          64 :             for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
    4823          38 :                 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
    4824          26 :                     const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
    4825             :                     // in sharp corners, the difference may be higher
    4826             :                     // factor (sqrt(2) for 90 degree corners
    4827          26 :                     const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
    4828          26 :                     const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
    4829             :                     //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
    4830          26 :                     if (distance < threshold) {
    4831             :                         opposite = cand;
    4832             :                     }
    4833             :                 }
    4834             :             }
    4835          26 :             if (opposite != nullptr) {
    4836          48 :                 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
    4837             :             }
    4838             :         }
    4839             :     }
    4840          41 :     return opposite;
    4841             : }
    4842             : 
    4843             : double
    4844           0 : NBEdge::getDistancAt(double pos) const {
    4845             :     // negative values of myDistances indicate descending kilometrage
    4846           0 :     return fabs(myDistance + pos);
    4847             : }
    4848             : 
    4849             : /****************************************************************************/

Generated by: LCOV version 1.14