LCOV - code coverage report
Current view: top level - src/netbuild - NBEdge.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.4 % 2203 2036
Test Date: 2024-11-21 15:56:26 Functions: 92.3 % 196 181

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

Generated by: LCOV version 2.0-1