LCOV - code coverage report
Current view: top level - src/netbuild - NBEdge.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.3 % 2305 2127
Test Date: 2025-12-06 15:35:27 Functions: 92.5 % 200 185

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

Generated by: LCOV version 2.0-1